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()); } } }
@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); } }
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(); }