@Nullable
  public static PsiJavaFileStub buildFileStub(@NotNull VirtualFile file, @NotNull byte[] bytes)
      throws ClsFormatException {
    try {
      if (ClassFileViewProvider.isInnerClass(file, bytes)) {
        return null;
      }

      ClassReader reader = new ClassReader(bytes);
      String className = file.getNameWithoutExtension();
      String packageName = getPackageName(reader.getClassName());
      PsiJavaFileStubImpl stub = new PsiJavaFileStubImpl(packageName, true);

      try {
        StubBuildingVisitor<VirtualFile> visitor =
            new StubBuildingVisitor<VirtualFile>(file, STRATEGY, stub, 0, className);
        reader.accept(visitor, EMPTY_ATTRIBUTES, ClassReader.SKIP_FRAMES);
        PsiClassStub<?> result = visitor.getResult();
        if (result == null) return null;
      } catch (OutOfOrderInnerClassException e) {
        return null;
      }

      return stub;
    } catch (Exception e) {
      throw new ClsFormatException(file.getPath() + ": " + e.getMessage(), e);
    }
  }
  protected void doTest(String path) throws Exception {
    File ktFile = new File(path);
    assertTrue("Cannot find a file " + ktFile.getAbsolutePath(), ktFile.exists());

    String fileText = FileUtil.loadFile(ktFile, true);

    KtFile psiFile =
        KotlinTestUtils.createFile(ktFile.getName(), fileText, jetCoreEnvironment.getProject());

    OutputFileCollection outputFiles =
        GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, jetCoreEnvironment);

    List<TestedObject> testedObjects = parseExpectedTestedObject(fileText);
    for (TestedObject testedObject : testedObjects) {
      String className = null;
      for (OutputFile outputFile : outputFiles.asList()) {
        String filePath = outputFile.getRelativePath();
        if (testedObject.isFullContainingClassName
            && filePath.equals(testedObject.containingClass + ".class")) {
          className = filePath;
        } else if (!testedObject.isFullContainingClassName
            && filePath.startsWith(testedObject.containingClass)) {
          className = filePath;
        }
      }

      assertNotNull(
          "Couldn't find a class file with name " + testedObject.containingClass, className);

      OutputFile outputFile = outputFiles.get(className);
      assertNotNull(outputFile);

      ClassReader cr = new ClassReader(outputFile.asByteArray());
      TestClassVisitor classVisitor = getClassVisitor(testedObject.kind, testedObject.name, false);
      cr.accept(classVisitor, ClassReader.SKIP_CODE);

      if (!classVisitor.isExists()) {
        classVisitor = getClassVisitor(testedObject.kind, testedObject.name, true);
        cr.accept(classVisitor, ClassReader.SKIP_CODE);
      }

      boolean isObjectExists =
          !Boolean.valueOf(findStringWithPrefixes(testedObject.textData, "// ABSENT: "));
      assertEquals(
          "Wrong object existence state: " + testedObject, isObjectExists, classVisitor.isExists());

      if (isObjectExists) {
        assertEquals(
            "Wrong access flag for " + testedObject + " \n" + outputFile.asText(),
            getExpectedFlags(testedObject.textData),
            classVisitor.getAccess());
      }
    }
  }
示例#3
0
  private static void checkLeakingParameters(Class<?> jClass) throws IOException {
    final HashMap<Method, boolean[]> map = new HashMap<Method, boolean[]>();

    // collecting leakedParameters
    final ClassReader classReader =
        new ClassReader(
            new FileInputStream(
                jClass.getResource("/" + jClass.getName().replace('.', '/') + ".class").getFile()));
    classReader.accept(
        new ClassVisitor(Opcodes.ASM5) {
          @Override
          public MethodVisitor visitMethod(
              int access, String name, String desc, String signature, String[] exceptions) {
            final MethodNode node =
                new MethodNode(Opcodes.ASM5, access, name, desc, signature, exceptions);
            final Method method = new Method(classReader.getClassName(), name, desc);
            return new MethodVisitor(Opcodes.ASM5, node) {
              @Override
              public void visitEnd() {
                super.visitEnd();
                try {
                  map.put(
                      method,
                      LeakingParameters.build(classReader.getClassName(), node, false).parameters);
                } catch (AnalyzerException ignore) {
                }
              }
            };
          }
        },
        ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);

    for (java.lang.reflect.Method jMethod : jClass.getDeclaredMethods()) {
      Method method =
          new Method(
              Type.getType(jClass).getInternalName(),
              jMethod.getName(),
              Type.getMethodDescriptor(jMethod));
      Annotation[][] annotations = jMethod.getParameterAnnotations();
      for (int i = 0; i < annotations.length; i++) {
        boolean isLeaking = false;
        Annotation[] parameterAnnotations = annotations[i];
        for (Annotation parameterAnnotation : parameterAnnotations) {
          if (parameterAnnotation.annotationType() == ExpectLeaking.class) {
            isLeaking = true;
          }
        }
        assertEquals(method.toString() + " #" + i, isLeaking, map.get(method)[i]);
      }
    }
  }
  @Nullable
  public static SMAPAndMethodNode getMethodNode(
      byte[] classData, final String methodName, final String methodDescriptor, ClassId classId)
      throws ClassNotFoundException, IOException {
    ClassReader cr = new ClassReader(classData);
    final MethodNode[] node = new MethodNode[1];
    final String[] debugInfo = new String[2];
    final int[] lines = new int[2];
    lines[0] = Integer.MAX_VALUE;
    lines[1] = Integer.MIN_VALUE;
    cr.accept(
        new ClassVisitor(API) {

          @Override
          public void visitSource(String source, String debug) {
            super.visitSource(source, debug);
            debugInfo[0] = source;
            debugInfo[1] = debug;
          }

          @Override
          public MethodVisitor visitMethod(
              int access,
              @NotNull String name,
              @NotNull String desc,
              String signature,
              String[] exceptions) {
            if (methodName.equals(name) && methodDescriptor.equals(desc)) {
              node[0] =
                  new MethodNode(API, access, name, desc, signature, exceptions) {
                    @Override
                    public void visitLineNumber(int line, @NotNull Label start) {
                      super.visitLineNumber(line, start);
                      lines[0] = Math.min(lines[0], line);
                      lines[1] = Math.max(lines[1], line);
                    }
                  };
              return node[0];
            }
            return null;
          }
        },
        ClassReader.SKIP_FRAMES | (GENERATE_SMAP ? 0 : ClassReader.SKIP_DEBUG));

    SMAP smap =
        SMAPParser.parseOrCreateDefault(
            debugInfo[1], debugInfo[0], classId.toString(), lines[0], lines[1]);
    return new SMAPAndMethodNode(node[0], smap);
  }
  @NotNull
  private List<InnerClassAttribute> extractInnerClasses(@NotNull String className) {
    OutputFile outputFile = generateClassesInFile().get(className + ".class");
    assertNotNull(outputFile);
    byte[] bytes = outputFile.asByteArray();
    ClassReader reader = new ClassReader(bytes);
    final List<InnerClassAttribute> result = new ArrayList<InnerClassAttribute>();

    reader.accept(
        new ClassVisitor(ASM5) {
          @Override
          public void visitInnerClass(
              @NotNull String name, String outerName, String innerName, int access) {
            result.add(new InnerClassAttribute(name, outerName, innerName, access));
          }
        },
        ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);

    return result;
  }
  @NotNull
  public InlineResult doTransform(
      @NotNull AnonymousObjectGeneration anonymousObjectGen,
      @NotNull FieldRemapper parentRemapper) {
    final List<InnerClassNode> innerClassNodes = new ArrayList<InnerClassNode>();
    ClassBuilder classBuilder = createClassBuilder();
    final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>();

    reader.accept(
        new ClassVisitor(InlineCodegenUtil.API, classBuilder.getVisitor()) {
          @Override
          public void visit(
              int version,
              int access,
              @NotNull String name,
              String signature,
              String superName,
              String[] interfaces) {
            InlineCodegenUtil.assertVersionNotGreaterThanJava6(version, name);
            super.visit(version, access, name, signature, superName, interfaces);
          }

          @Override
          public void visitInnerClass(
              @NotNull String name, String outerName, String innerName, int access) {
            innerClassNodes.add(new InnerClassNode(name, outerName, innerName, access));
          }

          @Override
          public MethodVisitor visitMethod(
              int access,
              @NotNull String name,
              @NotNull String desc,
              String signature,
              String[] exceptions) {
            MethodNode node = new MethodNode(access, name, desc, signature, exceptions);
            if (name.equals("<init>")) {
              if (constructor != null)
                throw new RuntimeException(
                    "Lambda, SAM or anonymous object should have only one constructor");

              constructor = node;
            } else {
              methodsToTransform.add(node);
            }
            return node;
          }

          @Override
          public FieldVisitor visitField(
              int access,
              @NotNull String name,
              @NotNull String desc,
              String signature,
              Object value) {
            addUniqueField(name);
            if (InlineCodegenUtil.isCapturedFieldName(name)) {
              return null;
            } else {
              return super.visitField(access, name, desc, signature, value);
            }
          }

          @Override
          public void visitSource(String source, String debug) {
            sourceInfo = source;
            debugInfo = debug;
          }

          @Override
          public void visitEnd() {}
        },
        ClassReader.SKIP_FRAMES);

    if (!inliningContext.isInliningLambda) {
      if (debugInfo != null && !debugInfo.isEmpty()) {
        sourceMapper = SourceMapper.Companion.createFromSmap(SMAPParser.parse(debugInfo));
      } else {
        // seems we can't do any clever mapping cause we don't know any about original class name
        sourceMapper = IdenticalSourceMapper.INSTANCE;
      }
      if (sourceInfo != null && !InlineCodegenUtil.GENERATE_SMAP) {
        classBuilder.visitSource(sourceInfo, debugInfo);
      }
    } else {
      if (sourceInfo != null) {
        classBuilder.visitSource(sourceInfo, debugInfo);
      }
      sourceMapper = IdenticalSourceMapper.INSTANCE;
    }

    ParametersBuilder allCapturedParamBuilder = ParametersBuilder.newBuilder();
    ParametersBuilder constructorParamBuilder = ParametersBuilder.newBuilder();
    List<CapturedParamInfo> additionalFakeParams =
        extractParametersMappingAndPatchConstructor(
            constructor,
            allCapturedParamBuilder,
            constructorParamBuilder,
            anonymousObjectGen,
            parentRemapper);
    List<MethodVisitor> deferringMethods = new ArrayList<MethodVisitor>();

    for (MethodNode next : methodsToTransform) {
      MethodVisitor deferringVisitor = newMethod(classBuilder, next);
      InlineResult funResult =
          inlineMethodAndUpdateGlobalResult(
              anonymousObjectGen,
              parentRemapper,
              deferringVisitor,
              next,
              allCapturedParamBuilder,
              false);

      Type returnType = Type.getReturnType(next.desc);
      if (!AsmUtil.isPrimitive(returnType)) {
        String oldFunReturnType = returnType.getInternalName();
        String newFunReturnType = funResult.getChangedTypes().get(oldFunReturnType);
        if (newFunReturnType != null) {
          inliningContext.typeRemapper.addAdditionalMappings(oldFunReturnType, newFunReturnType);
        }
      }
      deferringMethods.add(deferringVisitor);
    }

    for (MethodVisitor method : deferringMethods) {
      method.visitEnd();
    }

    generateConstructorAndFields(
        classBuilder,
        allCapturedParamBuilder,
        constructorParamBuilder,
        anonymousObjectGen,
        parentRemapper,
        additionalFakeParams);

    SourceMapper.Companion.flushToClassBuilder(sourceMapper, classBuilder);

    ClassVisitor visitor = classBuilder.getVisitor();
    for (InnerClassNode node : innerClassNodes) {
      visitor.visitInnerClass(node.name, node.outerName, node.innerName, node.access);
    }

    writeOuterInfo(visitor);

    classBuilder.done();

    anonymousObjectGen.setNewLambdaType(newLambdaType);
    return transformationResult;
  }
  private static Map<String, Map<Integer, String>> getAllParameterNames(ClassReader reader) {
    final Map<String, Map<Integer, String>> methodParamNames =
        new LinkedHashMap<String, Map<Integer, String>>();

    reader.accept(
        new ClassVisitor(Opcodes.API_VERSION) {
          private String myClassName = null;

          public void visit(
              final int version,
              final int access,
              final String name,
              final String signature,
              final String superName,
              final String[] interfaces) {
            myClassName = name;
          }

          public MethodVisitor visitMethod(
              final int access,
              final String name,
              final String desc,
              final String signature,
              final String[] exceptions) {
            final String methodName = myClassName + '.' + name + desc;
            final Map<Integer, String> names = new LinkedHashMap<Integer, String>();
            final Type[] args = Type.getArgumentTypes(desc);
            methodParamNames.put(methodName, names);

            final boolean isStatic = (access & ACC_STATIC) != 0;

            final Map<Integer, Integer> paramSlots =
                new LinkedHashMap<
                    Integer, Integer>(); // map: localVariableSlot -> methodParameterIndex
            int slotIndex = isStatic ? 0 : 1;
            for (int paramIndex = 0; paramIndex < args.length; paramIndex++) {
              final Type arg = args[paramIndex];
              paramSlots.put(slotIndex, paramIndex);
              slotIndex += arg.getSize();
            }

            return new MethodVisitor(api) {

              @Override
              public void visitLocalVariable(
                  String name2,
                  String desc,
                  String signature,
                  Label start,
                  Label end,
                  int slotIndex) {
                final Integer paramIndex = paramSlots.get(slotIndex);
                if (paramIndex != null) {
                  names.put(paramIndex, name2);
                }
              }
            };
          }
        },
        0);
    return methodParamNames;
  }
 public static boolean processClassFile(final ClassReader reader, final ClassVisitor writer) {
   final NotNullVerifyingInstrumenter instrumenter =
       new NotNullVerifyingInstrumenter(writer, reader);
   reader.accept(instrumenter, 0);
   return instrumenter.isModification();
 }