Example #1
0
  @NotNull
  // process local and global returns (local substituted with goto end-label global kept unchanged)
  public static List<PointForExternalFinallyBlocks> processReturns(
      @NotNull MethodNode node,
      @NotNull LabelOwner labelOwner,
      boolean remapReturn,
      Label endLabel) {
    if (!remapReturn) {
      return Collections.emptyList();
    }
    List<PointForExternalFinallyBlocks> result = new ArrayList<PointForExternalFinallyBlocks>();
    InsnList instructions = node.instructions;
    AbstractInsnNode insnNode = instructions.getFirst();
    while (insnNode != null) {
      if (InlineCodegenUtil.isReturnOpcode(insnNode.getOpcode())) {
        AbstractInsnNode previous = insnNode.getPrevious();
        MethodInsnNode flagNode;
        boolean isLocalReturn = true;
        String labelName = null;
        if (previous != null
            && previous instanceof MethodInsnNode
            && InlineCodegenUtil.NON_LOCAL_RETURN.equals(((MethodInsnNode) previous).owner)) {
          flagNode = (MethodInsnNode) previous;
          labelName = flagNode.name;
        }

        if (labelName != null) {
          isLocalReturn = labelOwner.isMyLabel(labelName);
          // remove global return flag
          if (isLocalReturn) {
            instructions.remove(previous);
          }
        }

        if (isLocalReturn && endLabel != null) {
          LabelNode labelNode = (LabelNode) endLabel.info;
          JumpInsnNode jumpInsnNode = new JumpInsnNode(Opcodes.GOTO, labelNode);
          instructions.insert(insnNode, jumpInsnNode);
          instructions.remove(insnNode);
          insnNode = jumpInsnNode;
        }

        // genetate finally block before nonLocalReturn flag/return/goto
        result.add(
            new PointForExternalFinallyBlocks(
                isLocalReturn ? insnNode : insnNode.getPrevious(),
                getReturnType(insnNode.getOpcode())));
      }
      insnNode = insnNode.getNext();
    }
    return result;
  }
Example #2
0
  @NotNull
  public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
    List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
    fieldAccessChain.add(aload0);
    AbstractInsnNode next = aload0.getNext();
    while (next != null && next instanceof FieldInsnNode || next instanceof LabelNode) {
      if (next instanceof LabelNode) {
        next = next.getNext();
        continue; // it will be delete on transformation
      }
      fieldAccessChain.add(next);
      if ("this$0".equals(((FieldInsnNode) next).name)) {
        next = next.getNext();
      } else {
        break;
      }
    }

    return fieldAccessChain;
  }
Example #3
0
  public static void putStackValuesIntoLocals(
      List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) {
    Type[] actualParams = Type.getArgumentTypes(descriptor);
    assert actualParams.length == directOrder.size()
        : "Number of expected and actual params should be equals!";

    int size = 0;
    for (Type next : directOrder) {
      size += next.getSize();
    }

    shift += size;
    int index = directOrder.size();

    for (Type next : Lists.reverse(directOrder)) {
      shift -= next.getSize();
      Type typeOnStack = actualParams[--index];
      if (!typeOnStack.equals(next)) {
        StackValue.onStack(typeOnStack).put(next, iv);
      }
      iv.store(shift, next);
    }
  }
Example #4
0
  @NotNull
  protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node) {
    node = prepareNode(node);

    Analyzer<SourceValue> analyzer =
        new Analyzer<SourceValue>(new SourceInterpreter()) {
          @NotNull
          @Override
          protected Frame<SourceValue> newFrame(int nLocals, int nStack) {
            return new Frame<SourceValue>(nLocals, nStack) {
              @Override
              public void execute(
                  @NotNull AbstractInsnNode insn, Interpreter<SourceValue> interpreter)
                  throws AnalyzerException {
                if (insn.getOpcode() == Opcodes.RETURN) {
                  // there is exception on void non local return in frame
                  return;
                }
                super.execute(insn, interpreter);
              }
            };
          }
        };

    Frame<SourceValue>[] sources;
    try {
      sources = analyzer.analyze("fake", node);
    } catch (AnalyzerException e) {
      throw wrapException(e, node, "couldn't inline method call");
    }

    AbstractInsnNode cur = node.instructions.getFirst();
    int index = 0;

    boolean awaitClassReification = false;
    Set<LabelNode> possibleDeadLabels = new HashSet<LabelNode>();

    while (cur != null) {
      Frame<SourceValue> frame = sources[index];

      if (frame != null) {
        if (ReifiedTypeInliner.isNeedClassReificationMarker(cur)) {
          awaitClassReification = true;
        } else if (cur.getType() == AbstractInsnNode.METHOD_INSN) {
          MethodInsnNode methodInsnNode = (MethodInsnNode) cur;
          String owner = methodInsnNode.owner;
          String desc = methodInsnNode.desc;
          String name = methodInsnNode.name;
          // TODO check closure
          int paramLength = Type.getArgumentTypes(desc).length + 1; // non static
          if (isInvokeOnLambda(owner, name) /*&& methodInsnNode.owner.equals(INLINE_RUNTIME)*/) {
            SourceValue sourceValue = frame.getStack(frame.getStackSize() - paramLength);

            LambdaInfo lambdaInfo = null;
            int varIndex = -1;

            if (sourceValue.insns.size() == 1) {
              AbstractInsnNode insnNode = sourceValue.insns.iterator().next();

              lambdaInfo = getLambdaIfExists(insnNode);
              if (lambdaInfo != null) {
                // remove inlinable access
                node.instructions.remove(insnNode);
              }
            }

            invokeCalls.add(new InvokeCall(varIndex, lambdaInfo));
          } else if (isAnonymousConstructorCall(owner, name)) {
            Map<Integer, LambdaInfo> lambdaMapping = new HashMap<Integer, LambdaInfo>();
            int paramStart = frame.getStackSize() - paramLength;

            for (int i = 0; i < paramLength; i++) {
              SourceValue sourceValue = frame.getStack(paramStart + i);
              if (sourceValue.insns.size() == 1) {
                AbstractInsnNode insnNode = sourceValue.insns.iterator().next();
                LambdaInfo lambdaInfo = getLambdaIfExists(insnNode);
                if (lambdaInfo != null) {
                  lambdaMapping.put(i, lambdaInfo);
                  node.instructions.remove(insnNode);
                }
              }
            }

            anonymousObjectGenerations.add(
                buildConstructorInvocation(owner, desc, lambdaMapping, awaitClassReification));
            awaitClassReification = false;
          }
        } else if (cur.getOpcode() == Opcodes.GETSTATIC) {
          FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
          String owner = fieldInsnNode.owner;
          if (isAnonymousSingletonLoad(owner, fieldInsnNode.name)) {
            anonymousObjectGenerations.add(
                new AnonymousObjectGeneration(
                    owner, isSameModule, awaitClassReification, isAlreadyRegenerated(owner), true));
            awaitClassReification = false;
          }
        }
      }
      AbstractInsnNode prevNode = cur;
      cur = cur.getNext();
      index++;

      // given frame is <tt>null</tt> if and only if the corresponding instruction cannot be reached
      // (dead code).
      if (frame == null) {
        // clean dead code otherwise there is problems in unreachable finally block, don't touch
        // label it cause try/catch/finally problems
        if (prevNode.getType() == AbstractInsnNode.LABEL) {
          // NB: Cause we generate exception table for default handler using gaps (see
          // ExpressionCodegen.visitTryExpression)
          // it may occurs that interval for default handler starts before catch start label, so
          // this label seems as dead,
          // but as result all this labels will be merged into one (see KT-5863)
        } else {
          node.instructions.remove(prevNode);
        }
      }
    }

    // clean dead try/catch blocks
    List<TryCatchBlockNode> blocks = node.tryCatchBlocks;
    for (Iterator<TryCatchBlockNode> iterator = blocks.iterator(); iterator.hasNext(); ) {
      TryCatchBlockNode block = iterator.next();
      if (isEmptyTryInterval(block)) {
        iterator.remove();
      }
    }

    return node;
  }
Example #5
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;
  }