예제 #1
0
  public InlineResult doInline(
      @NotNull MethodVisitor adapter,
      @NotNull LocalVarRemapper remapper,
      boolean remapReturn,
      @NotNull LabelOwner labelOwner) {
    // analyze body
    MethodNode transformedNode = markPlacesForInlineAndRemoveInlinable(node);

    // substitute returns with "goto end" instruction to keep non local returns in lambdas
    Label end = new Label();
    transformedNode = doInline(transformedNode);
    removeClosureAssertions(transformedNode);
    InsnList instructions = transformedNode.instructions;
    instructions.resetLabels();

    MethodNode resultNode =
        new MethodNode(
            InlineCodegenUtil.API,
            transformedNode.access,
            transformedNode.name,
            transformedNode.desc,
            transformedNode.signature,
            ArrayUtil.toStringArray(transformedNode.exceptions));
    RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper);
    try {
      transformedNode.accept(visitor);
    } catch (Exception e) {
      throw wrapException(e, transformedNode, "couldn't inline method call");
    }

    resultNode.visitLabel(end);

    if (inliningContext.isRoot()) {
      InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks);
    }

    processReturns(resultNode, labelOwner, remapReturn, end);
    // flush transformed node to output
    resultNode.accept(new InliningInstructionAdapter(adapter));

    return result;
  }
예제 #2
0
  @NotNull
  public MethodNode prepareNode(@NotNull MethodNode node) {
    final int capturedParamsSize = parameters.getCaptured().size();
    final int realParametersSize = parameters.getReal().size();
    Type[] types = Type.getArgumentTypes(node.desc);
    Type returnType = Type.getReturnType(node.desc);

    ArrayList<Type> capturedTypes = parameters.getCapturedTypes();
    Type[] allTypes =
        ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()]));

    node.instructions.resetLabels();
    MethodNode transformedNode =
        new MethodNode(
            InlineCodegenUtil.API,
            node.access,
            node.name,
            Type.getMethodDescriptor(returnType, allTypes),
            node.signature,
            null) {

          private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda();

          private int getNewIndex(int var) {
            return var + (var < realParametersSize ? 0 : capturedParamsSize);
          }

          @Override
          public void visitVarInsn(int opcode, int var) {
            super.visitVarInsn(opcode, getNewIndex(var));
          }

          @Override
          public void visitIincInsn(int var, int increment) {
            super.visitIincInsn(getNewIndex(var), increment);
          }

          @Override
          public void visitMaxs(int maxStack, int maxLocals) {
            super.visitMaxs(maxStack, maxLocals + capturedParamsSize);
          }

          @Override
          public void visitLineNumber(int line, @NotNull Label start) {
            if (isInliningLambda) {
              super.visitLineNumber(line, start);
            }
          }

          @Override
          public void visitLocalVariable(
              @NotNull String name,
              @NotNull String desc,
              String signature,
              @NotNull Label start,
              @NotNull Label end,
              int index) {
            if (isInliningLambda) {
              super.visitLocalVariable(name, desc, signature, start, end, getNewIndex(index));
            }
          }
        };

    node.accept(transformedNode);

    transformCaptured(transformedNode);

    return transformedNode;
  }
예제 #3
0
  private MethodNode doInline(MethodNode node) {

    final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls);

    final MethodNode resultNode =
        new MethodNode(node.access, node.name, node.desc, node.signature, null);

    final Iterator<AnonymousObjectGeneration> iterator = anonymousObjectGenerations.iterator();

    RemappingMethodAdapter remappingMethodAdapter =
        new RemappingMethodAdapter(
            resultNode.access, resultNode.desc, resultNode, new TypeRemapper(currentTypeMapping));

    InlineAdapter lambdaInliner =
        new InlineAdapter(remappingMethodAdapter, parameters.totalSize()) {

          private AnonymousObjectGeneration anonymousObjectGen;

          private void handleAnonymousObjectGeneration() {
            anonymousObjectGen = iterator.next();

            if (anonymousObjectGen.shouldRegenerate()) {
              // TODO: need poping of type but what to do with local funs???
              Type newLambdaType =
                  Type.getObjectType(inliningContext.nameGenerator.genLambdaClassName());
              currentTypeMapping.put(
                  anonymousObjectGen.getOwnerInternalName(), newLambdaType.getInternalName());
              AnonymousObjectTransformer transformer =
                  new AnonymousObjectTransformer(
                      anonymousObjectGen.getOwnerInternalName(),
                      inliningContext.subInlineWithClassRegeneration(
                          inliningContext.nameGenerator, currentTypeMapping, anonymousObjectGen),
                      isSameModule,
                      newLambdaType);

              InlineResult transformResult =
                  transformer.doTransform(anonymousObjectGen, nodeRemapper);
              result.addAllClassesToRemove(transformResult);

              if (inliningContext.isInliningLambda && !anonymousObjectGen.isStaticOrigin()) {
                // this class is transformed and original not used so we should remove original one
                // after inlining
                // Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC
                // within lambda
                // because it can be local function from outer scope
                result.addClassToRemove(anonymousObjectGen.getOwnerInternalName());
              }

              if (transformResult.getReifiedTypeParametersUsages().wereUsedReifiedParameters()) {
                ReifiedTypeInliner.putNeedClassReificationMarker(mv);
                result
                    .getReifiedTypeParametersUsages()
                    .mergeAll(transformResult.getReifiedTypeParametersUsages());
              }
            }
          }

          @Override
          public void anew(@NotNull Type type) {
            if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) {
              handleAnonymousObjectGeneration();
            }

            // in case of regenerated anonymousObjectGen type would be remapped to new one via
            // remappingMethodAdapter
            super.anew(type);
          }

          @Override
          public void visitMethodInsn(
              int opcode, String owner, String name, String desc, boolean itf) {
            if (
            /*INLINE_RUNTIME.equals(owner) &&*/ isInvokeOnLambda(owner, name)) { // TODO add method
              assert !currentInvokes.isEmpty();
              InvokeCall invokeCall = currentInvokes.remove();
              LambdaInfo info = invokeCall.lambdaInfo;

              if (info == null) {
                // noninlinable lambda
                super.visitMethodInsn(opcode, owner, name, desc, itf);
                return;
              }

              int valueParamShift = getNextLocalIndex(); // NB: don't inline cause it changes
              putStackValuesIntoLocals(
                  info.getInvokeParamsWithoutCaptured(), valueParamShift, this, desc);

              addInlineMarker(this, true);
              Parameters lambdaParameters = info.addAllParameters(nodeRemapper);

              InlinedLambdaRemapper newCapturedRemapper =
                  new InlinedLambdaRemapper(
                      info.getLambdaClassType().getInternalName(), nodeRemapper, lambdaParameters);

              setLambdaInlining(true);
              MethodInliner inliner =
                  new MethodInliner(
                      info.getNode(),
                      lambdaParameters,
                      inliningContext.subInlineLambda(info),
                      newCapturedRemapper,
                      true /*cause all calls in same module as lambda*/,
                      "Lambda inlining " + info.getLambdaClassType().getInternalName());

              LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
              InlineResult lambdaResult =
                  inliner.doInline(
                      this.mv, remapper, true, info); // TODO add skipped this and receiver
              result.addAllClassesToRemove(lambdaResult);

              // return value boxing/unboxing
              Method bridge =
                  typeMapper
                      .mapSignature(
                          ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor()))
                      .getAsmMethod();
              Method delegate =
                  typeMapper.mapSignature(info.getFunctionDescriptor()).getAsmMethod();
              StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this);
              setLambdaInlining(false);
              addInlineMarker(this, false);
            } else if (isAnonymousConstructorCall(owner, name)) { // TODO add method
              assert anonymousObjectGen != null
                  : "<init> call not corresponds to new call" + owner + " " + name;
              if (anonymousObjectGen.shouldRegenerate()) {
                // put additional captured parameters on stack
                for (CapturedParamDesc capturedParamDesc :
                    anonymousObjectGen.getAllRecapturedParameters()) {
                  visitFieldInsn(
                      Opcodes.GETSTATIC,
                      capturedParamDesc.getContainingLambdaName(),
                      "$$$" + capturedParamDesc.getFieldName(),
                      capturedParamDesc.getType().getDescriptor());
                }
                super.visitMethodInsn(
                    opcode,
                    anonymousObjectGen.getNewLambdaType().getInternalName(),
                    name,
                    anonymousObjectGen.getNewConstructorDescriptor(),
                    itf);
                anonymousObjectGen = null;
              } else {
                super.visitMethodInsn(
                    opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf);
              }
            } else if (ReifiedTypeInliner.isNeedClassReificationMarker(
                new MethodInsnNode(opcode, owner, name, desc, false))) {
              // we will put it if needed in anew processing
            } else {
              super.visitMethodInsn(
                  opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf);
            }
          }

          @Override
          public void visitFieldInsn(
              int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) {
            if (opcode == Opcodes.GETSTATIC && isAnonymousSingletonLoad(owner, name)) {
              handleAnonymousObjectGeneration();
            }
            super.visitFieldInsn(opcode, owner, name, desc);
          }

          @Override
          public void visitMaxs(int stack, int locals) {
            lambdasFinallyBlocks = resultNode.tryCatchBlocks.size();
            super.visitMaxs(stack, locals);
          }
        };

    node.accept(lambdaInliner);

    return resultNode;
  }