public MethodVisitor visitMethod( final int access, final String name, String desc, String signature, String[] exceptions) { final MethodVisitor methodvisitor = cv.visitMethod(access, name, desc, signature, exceptions); // patch static initializer if ((access & ACC_STATIC) != 0 && name.equals("<clinit>")) { myHasStaticInitializer = true; return new MethodVisitor(Opcodes.ASM5, methodvisitor) { public void visitCode() { super.visitCode(); patchStaticInitializer(mv); } }; } final Type[] argTypes = Type.getArgumentTypes(desc); final Type returnType = Type.getReturnType(desc); // don't dig through the whole method if there's nothing to do in it if (isStringType(returnType)) { return new InstrumentationAdapter(this, methodvisitor, argTypes, returnType, access, name); } else { for (Type type : argTypes) { if (isStringType(type)) { return new InstrumentationAdapter( this, methodvisitor, argTypes, returnType, access, name); } } } return methodvisitor; }
public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { Type[] args = Type.getArgumentTypes(desc); Type returnType = Type.getReturnType(desc); int startParameter = getStartParameterIndex(name); MethodVisitor v = cv.visitMethod(access, name, desc, signature, exceptions); return new MyMethodAdapter(this, v, args, returnType, access, startParameter, name); }
private static String parseMethodViaDescription( final String desc, final PsiMethodStubImpl stub, final List<String> args) { final String returnType = getTypeText(Type.getReturnType(desc)); final Type[] argTypes = Type.getArgumentTypes(desc); for (Type argType : argTypes) { args.add(getTypeText(argType)); } new PsiTypeParameterListStubImpl(stub); return returnType; }
private void unwrapResult(final MethodVisitor mv, final String desc) { Type returnType = Type.getReturnType(desc); if (returnType == Type.VOID_TYPE) { mv.visitInsn(POP); mv.visitInsn(RETURN); } else { if (isPrimitive(returnType)) { BytecodeHelper.unbox(mv, ClassHelper.make(returnType.getClassName())); } else { mv.visitTypeInsn(CHECKCAST, returnType.getInternalName()); } mv.visitInsn(getReturnInsn(returnType)); } }
@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; }
/** * Creates the "proxy method", e.g. the method that has the same name and signature as the * original method but a completely other implementation. * * @param access * @param name * @param desc * @param signature * @param exceptions * @param methodInfo * @return the method visitor */ private MethodVisitor createProxyMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions, final MethodInfo methodInfo) { MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); // load "this" ie callee if target method is not static if (!Modifier.isStatic(access)) { mv.visitVarInsn(ALOAD, 0); } // load args AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access)); // load "this" ie caller or null if method is static if (Modifier.isStatic(access)) { mv.visitInsn(ACONST_NULL); } else { mv.visitVarInsn(ALOAD, 0); } int joinPointHash = AsmHelper.calculateMethodHash(name, desc); String joinPointClassName = TransformationUtil.getJoinPointClassName( m_declaringTypeName, name, desc, m_declaringTypeName, JoinPointType.METHOD_EXECUTION_INT, joinPointHash); // TODO: should we provide some sort of option to do JITgen when weaving instead of when loading // ? // use case: offline full packaging and alike mv.visitMethodInsn( INVOKESTATIC, joinPointClassName, INVOKE_METHOD_NAME, TransformationUtil.getInvokeSignatureForCodeJoinPoints( access, desc, m_declaringTypeName, m_declaringTypeName)); AsmHelper.addReturnStatement(mv, Type.getReturnType(desc)); mv.visitMaxs(0, 0); // emit the joinpoint m_ctx.addEmittedJoinPoint( new EmittedJoinPoint( JoinPointType.METHOD_EXECUTION_INT, m_declaringTypeName, name, desc, access, m_declaringTypeName, name, desc, access, joinPointHash, joinPointClassName, EmittedJoinPoint.NO_LINE_NUMBER)); return mv; }
@Override public MethodVisitor visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions) { Object key = Arrays.asList(name, desc); if (visitedMethods.contains(key)) return EMPTY_VISITOR; if (Modifier.isPrivate(access) || Modifier.isNative(access) || ((access & ACC_SYNTHETIC) != 0)) { // do not generate bytecode for private methods return EMPTY_VISITOR; } int accessFlags = access; visitedMethods.add(key); if ((objectDelegateMethods.contains(name) || delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) && !Modifier.isStatic(access) && !Modifier.isFinal(access)) { if (!GROOVYOBJECT_METHOD_NAMESS.contains(name)) { if (Modifier.isAbstract(access)) { // prevents the proxy from being abstract accessFlags -= ACC_ABSTRACT; } if (delegatedClosures.containsKey(name) || (!"<init>".equals(name) && hasWildcard)) { delegatedClosures.put(name, Boolean.TRUE); return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags); } if (generateDelegateField && objectDelegateMethods.contains(name)) { return makeDelegateCall(name, desc, signature, exceptions, accessFlags); } delegatedClosures.put(name, Boolean.TRUE); return makeDelegateToClosureCall(name, desc, signature, exceptions, accessFlags); } } else if ("<init>".equals(name) && (Modifier.isPublic(access) || Modifier.isProtected(access))) { return createConstructor(access, name, desc, signature, exceptions); } else if (Modifier.isAbstract(access) && !GROOVYOBJECT_METHOD_NAMESS.contains(name)) { accessFlags -= ACC_ABSTRACT; MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions); mv.visitCode(); Type[] args = Type.getArgumentTypes(desc); if (emptyBody) { Type returnType = Type.getReturnType(desc); if (returnType == Type.VOID_TYPE) { mv.visitInsn(RETURN); } else { int loadIns = getLoadInsn(returnType); switch (loadIns) { case ILOAD: mv.visitInsn(ICONST_0); break; case LLOAD: mv.visitInsn(LCONST_0); break; case FLOAD: mv.visitInsn(FCONST_0); break; case DLOAD: mv.visitInsn(DCONST_0); break; default: mv.visitInsn(ACONST_NULL); } mv.visitInsn(getReturnInsn(returnType)); mv.visitMaxs(2, registerLen(args) + 1); } } else { // for compatibility with the legacy proxy generator, we should throw an // UnsupportedOperationException // instead of an AbtractMethodException mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException"); mv.visitInsn(DUP); mv.visitMethodInsn( INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>", "()V"); mv.visitInsn(ATHROW); mv.visitMaxs(2, registerLen(args) + 1); } mv.visitEnd(); } return EMPTY_VISITOR; }
@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; }
@Override public MethodVisitor visitMethod( final int access, final String name, String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return new FailSafeMethodVisitor( Opcodes.API_VERSION, super.visitMethod(access, name, desc, signature, exceptions)); } final Type[] args = Type.getArgumentTypes(desc); final Type returnType = Type.getReturnType(desc); final MethodVisitor v = cv.visitMethod(access, name, desc, signature, exceptions); final Map<Integer, String> paramNames = myMethodParamNames.get(myClassName + '.' + name + desc); return new FailSafeMethodVisitor(Opcodes.API_VERSION, v) { private final Map<Integer, NotNullState> myNotNullParams = new LinkedHashMap<Integer, NotNullState>(); private int mySyntheticCount = 0; private NotNullState myMethodNotNull; private Label myStartGeneratedCodeLabel; private AnnotationVisitor collectNotNullArgs( AnnotationVisitor base, final NotNullState state) { return new AnnotationVisitor(Opcodes.API_VERSION, base) { @Override public void visit(String methodName, Object o) { if (ANNOTATION_DEFAULT_METHOD.equals(methodName) && !((String) o).isEmpty()) { state.message = (String) o; } else if ("exception".equals(methodName) && o instanceof Type && !((Type) o).getClassName().equals(Exception.class.getName())) { state.exceptionType = ((Type) o).getInternalName(); } super.visit(methodName, o); } }; } public AnnotationVisitor visitParameterAnnotation( final int parameter, final String anno, final boolean visible) { AnnotationVisitor av = mv.visitParameterAnnotation(parameter, anno, visible); if (isReferenceType(args[parameter]) && anno.equals(NOT_NULL_TYPE)) { NotNullState state = new NotNullState(IAE_CLASS_NAME); myNotNullParams.put(new Integer(parameter), state); av = collectNotNullArgs(av, state); } else if (anno.equals(SYNTHETIC_TYPE)) { // see http://forge.ow2.org/tracker/?aid=307392&group_id=23&atid=100023&func=detail mySyntheticCount++; } return av; } @Override public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) { AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime); if (isReferenceType(returnType) && anno.equals(NOT_NULL_TYPE)) { myMethodNotNull = new NotNullState(ISE_CLASS_NAME); av = collectNotNullArgs(av, myMethodNotNull); } return av; } @Override public void visitCode() { if (myNotNullParams.size() > 0) { myStartGeneratedCodeLabel = new Label(); mv.visitLabel(myStartGeneratedCodeLabel); } for (Map.Entry<Integer, NotNullState> entry : myNotNullParams.entrySet()) { Integer param = entry.getKey(); int var = ((access & ACC_STATIC) == 0) ? 1 : 0; for (int i = 0; i < param; ++i) { var += args[i].getSize(); } mv.visitVarInsn(ALOAD, var); Label end = new Label(); mv.visitJumpInsn(IFNONNULL, end); NotNullState state = entry.getValue(); String paramName = paramNames == null ? null : paramNames.get(param); String descrPattern = state.message != null ? state.message : paramName != null ? NULL_ARG_MESSAGE_NAMED : NULL_ARG_MESSAGE_INDEXED; String[] args = state.message != null ? EMPTY_STRING_ARRAY : new String[] { paramName != null ? paramName : String.valueOf(param - mySyntheticCount), myClassName, name }; reportError(state.exceptionType, end, descrPattern, args); } } @Override public void visitLocalVariable( String name, String desc, String signature, Label start, Label end, int index) { final boolean isStatic = (access & ACC_STATIC) != 0; final boolean isParameterOrThisRef = isStatic ? index < args.length : index <= args.length; final Label label = (isParameterOrThisRef && myStartGeneratedCodeLabel != null) ? myStartGeneratedCodeLabel : start; mv.visitLocalVariable(name, desc, signature, label, end, index); } @Override public void visitInsn(int opcode) { if (opcode == ARETURN) { if (myMethodNotNull != null) { mv.visitInsn(DUP); final Label skipLabel = new Label(); mv.visitJumpInsn(IFNONNULL, skipLabel); String descrPattern = myMethodNotNull.message != null ? myMethodNotNull.message : NULL_RESULT_MESSAGE; String[] args = myMethodNotNull.message != null ? EMPTY_STRING_ARRAY : new String[] {myClassName, name}; reportError(myMethodNotNull.exceptionType, skipLabel, descrPattern, args); } } mv.visitInsn(opcode); } private void reportError( final String exceptionClass, final Label end, final String descrPattern, final String[] args) { myAuxGenerator.reportError(mv, myClassName, exceptionClass, descrPattern, args); mv.visitLabel(end); myIsModification = true; processPostponedErrors(); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { try { super.visitMaxs(maxStack, maxLocals); } catch (Throwable e) { //noinspection SpellCheckingInspection registerError(name, "visitMaxs", e); } } }; }