private void writeNonAbstractMethodWrapper( ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass, Method method) { Label start = new Label(); Label end = new Label(); Label handler = new Label(); MethodVisitor methodVisitor = declareMethod(visitor, method); methodVisitor.visitTryCatchBlock(start, end, handler, null); setCanCallSettersField(methodVisitor, generatedType, false); methodVisitor.visitLabel(start); invokeSuperMethod(methodVisitor, managedTypeClass, method); methodVisitor.visitLabel(end); setCanCallSettersField(methodVisitor, generatedType, true); methodVisitor.visitInsn(ARETURN); methodVisitor.visitLabel(handler); setCanCallSettersField(methodVisitor, generatedType, true); methodVisitor.visitInsn(ATHROW); methodVisitor.visitMaxs(0, 0); methodVisitor.visitEnd(); }
/** Generate a call to the delegate object. */ protected MethodVisitor makeDelegateCall( final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags) { MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions); mv.visitVarInsn(ALOAD, 0); // load this mv.visitFieldInsn( GETFIELD, proxyName, DELEGATE_OBJECT_FIELD, BytecodeHelper.getTypeDescription(delegateClass)); // load delegate // using InvokerHelper to allow potential intercepted calls int size; mv.visitLdcInsn(name); // method name Type[] args = Type.getArgumentTypes(desc); BytecodeHelper.pushConstant(mv, args.length); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); size = 3; int idx = 1; for (int i = 0; i < args.length; i++) { Type arg = args[i]; mv.visitInsn(DUP); BytecodeHelper.pushConstant(mv, i); // primitive types must be boxed if (isPrimitive(arg)) { mv.visitIntInsn(getLoadInsn(arg), idx); String wrappedType = getWrappedClassDescriptor(arg); mv.visitMethodInsn( INVOKESTATIC, wrappedType, "valueOf", "(" + arg.getDescriptor() + ")L" + wrappedType + ";"); } else { mv.visitVarInsn(ALOAD, idx); // load argument i } size = Math.max(6, 5 + registerLen(arg)); idx += registerLen(arg); mv.visitInsn(AASTORE); // store value into array } mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;"); unwrapResult(mv, desc); mv.visitMaxs(size, registerLen(args) + 1); return EMPTY_VISITOR; }
@NotNull private InlineResult inlineMethod( @NotNull AnonymousObjectGeneration anonymousObjectGen, @NotNull FieldRemapper parentRemapper, @NotNull MethodVisitor deferringVisitor, @NotNull MethodNode sourceNode, @NotNull ParametersBuilder capturedBuilder, boolean isConstructor) { ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifedTypeInliner.reifyInstructions(sourceNode); Parameters parameters = isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode); RegeneratedLambdaFieldRemapper remapper = new RegeneratedLambdaFieldRemapper( oldObjectType.getInternalName(), newLambdaType.getInternalName(), parameters, anonymousObjectGen.getCapturedLambdasToInline(), parentRemapper, isConstructor); MethodInliner inliner = new MethodInliner( sourceNode, parameters, inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")), remapper, isSameModule, "Transformer for " + anonymousObjectGen.getOwnerInternalName(), sourceMapper, new InlineCallSiteInfo( anonymousObjectGen.getOwnerInternalName(), sourceNode.name, isConstructor ? anonymousObjectGen.getNewConstructorDescriptor() : sourceNode.desc)); InlineResult result = inliner.doInline( deferringVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE); result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify); deferringVisitor.visitMaxs(-1, -1); return result; }
private MethodVisitor createConstructor( final int access, final String name, final String desc, final String signature, final String[] exceptions) { Type[] args = Type.getArgumentTypes(desc); StringBuilder newDesc = new StringBuilder("("); for (Type arg : args) { newDesc.append(arg.getDescriptor()); } newDesc.append("Ljava/util/Map;"); // the closure map if (generateDelegateField) { newDesc.append(BytecodeHelper.getTypeDescription(delegateClass)); } newDesc.append(")V"); MethodVisitor mv = super.visitMethod(access, name, newDesc.toString(), signature, exceptions); mv.visitCode(); initializeDelegateClosure(mv, args.length); if (generateDelegateField) { initializeDelegateObject(mv, args.length + 1); } mv.visitVarInsn(ALOAD, 0); int idx = 1; for (Type arg : args) { if (isPrimitive(arg)) { mv.visitIntInsn(getLoadInsn(arg), idx); } else { mv.visitVarInsn(ALOAD, idx); // load argument i } idx += registerLen(arg); } mv.visitMethodInsn( INVOKESPECIAL, BytecodeHelper.getClassInternalName(superClass), "<init>", desc); mv.visitInsn(RETURN); int max = idx + 1 + (generateDelegateField ? 1 : 0); mv.visitMaxs(max, max); mv.visitEnd(); return EMPTY_VISITOR; }
protected MethodVisitor makeDelegateToClosureCall( final String name, final String desc, final String signature, final String[] exceptions, final int accessFlags) { MethodVisitor mv = super.visitMethod(accessFlags, name, desc, signature, exceptions); // TraceMethodVisitor tmv = new TraceMethodVisitor(mv); // mv = tmv; mv.visitCode(); int stackSize = 0; // method body should be: // this.$delegate$closure$methodName.call(new Object[] { method arguments }) Type[] args = Type.getArgumentTypes(desc); int arrayStore = args.length + 1; BytecodeHelper.pushConstant(mv, args.length); mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); // stack size = 1 stackSize = 1; int idx = 1; for (int i = 0; i < args.length; i++) { Type arg = args[i]; mv.visitInsn(DUP); // stack size = 2 BytecodeHelper.pushConstant(mv, i); // array index, stack size = 3 stackSize = 3; // primitive types must be boxed if (isPrimitive(arg)) { mv.visitIntInsn(getLoadInsn(arg), idx); String wrappedType = getWrappedClassDescriptor(arg); mv.visitMethodInsn( INVOKESTATIC, wrappedType, "valueOf", "(" + arg.getDescriptor() + ")L" + wrappedType + ";"); } else { mv.visitVarInsn(ALOAD, idx); // load argument i } idx += registerLen(arg); stackSize = Math.max(4, 3 + registerLen(arg)); mv.visitInsn(AASTORE); // store value into array } mv.visitVarInsn(ASTORE, arrayStore); // store array int arrayIndex = arrayStore; mv.visitVarInsn(ALOAD, 0); // load this mv.visitFieldInsn( GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;"); // load closure map mv.visitLdcInsn(name); // load method name mv.visitMethodInsn( INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); arrayStore++; mv.visitVarInsn(ASTORE, arrayStore); // if null, test if wildcard exists Label notNull = new Label(); mv.visitIntInsn(ALOAD, arrayStore); mv.visitJumpInsn(IFNONNULL, notNull); mv.visitVarInsn(ALOAD, 0); // load this mv.visitFieldInsn( GETFIELD, proxyName, CLOSURES_MAP_FIELD, "Ljava/util/Map;"); // load closure map mv.visitLdcInsn("*"); // load wildcard mv.visitMethodInsn( INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); mv.visitVarInsn(ASTORE, arrayStore); mv.visitLabel(notNull); mv.visitVarInsn(ALOAD, arrayStore); mv.visitMethodInsn( INVOKESTATIC, BytecodeHelper.getClassInternalName(this.getClass()), "ensureClosure", "(Ljava/lang/Object;)Lgroovy/lang/Closure;"); mv.visitVarInsn(ALOAD, arrayIndex); // load argument array stackSize++; mv.visitMethodInsn( INVOKEVIRTUAL, "groovy/lang/Closure", "call", "([Ljava/lang/Object;)Ljava/lang/Object;"); // call closure unwrapResult(mv, desc); mv.visitMaxs(stackSize, arrayStore + 1); mv.visitEnd(); // System.out.println("tmv.getText() = " + tmv.getText()); return EMPTY_VISITOR; }
@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; }
/** * When an object doesn't implement the GroovyObject interface, we generate bytecode for the * {@link GroovyObject} interface methods. Otherwise, the superclass is expected to implement * them. */ private void createGroovyObjectSupport() { visitField(ACC_PRIVATE + ACC_TRANSIENT, "metaClass", "Lgroovy/lang/MetaClass;", null, null); // getMetaClass MethodVisitor mv; { mv = super.visitMethod(ACC_PUBLIC, "getMetaClass", "()Lgroovy/lang/MetaClass;", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;"); Label l1 = new Label(); mv.visitJumpInsn(IFNONNULL, l1); Label l2 = new Label(); mv.visitLabel(l2); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/InvokerHelper", "getMetaClass", "(Ljava/lang/Class;)Lgroovy/lang/MetaClass;"); mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;"); mv.visitLabel(l1); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;"); mv.visitInsn(ARETURN); mv.visitMaxs(2, 1); mv.visitEnd(); } // getProperty { mv = super.visitMethod( ACC_PUBLIC, "getProperty", "(Ljava/lang/String;)Ljava/lang/Object;", null, null); mv.visitCode(); mv.visitIntInsn(ALOAD, 0); mv.visitMethodInsn( INVOKEINTERFACE, "groovy/lang/GroovyObject", "getMetaClass", "()Lgroovy/lang/MetaClass;"); mv.visitIntInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn( INVOKEINTERFACE, "groovy/lang/MetaClass", "getProperty", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"); mv.visitInsn(ARETURN); mv.visitMaxs(3, 2); mv.visitEnd(); } // setProperty { mv = super.visitMethod( ACC_PUBLIC, "setProperty", "(Ljava/lang/String;Ljava/lang/Object;)V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); mv.visitMethodInsn( INVOKEINTERFACE, "groovy/lang/MetaClass", "setProperty", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V"); Label l1 = new Label(); mv.visitLabel(l1); mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); mv.visitMaxs(4, 3); mv.visitEnd(); } // invokeMethod { mv = super.visitMethod( ACC_PUBLIC, "invokeMethod", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;", null, null); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKEVIRTUAL, proxyName, "getMetaClass", "()Lgroovy/lang/MetaClass;"); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); mv.visitMethodInsn( INVOKEINTERFACE, "groovy/lang/MetaClass", "invokeMethod", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;"); mv.visitInsn(ARETURN); Label l1 = new Label(); mv.visitLabel(l1); mv.visitMaxs(4, 3); mv.visitEnd(); } // setMetaClass { mv = super.visitMethod(ACC_PUBLIC, "setMetaClass", "(Lgroovy/lang/MetaClass;)V", null, null); mv.visitCode(); Label l0 = new Label(); mv.visitLabel(l0); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, 1); mv.visitFieldInsn(PUTFIELD, proxyName, "metaClass", "Lgroovy/lang/MetaClass;"); Label l1 = new Label(); mv.visitLabel(l1); mv.visitInsn(RETURN); Label l2 = new Label(); mv.visitLabel(l2); mv.visitMaxs(2, 2); mv.visitEnd(); } }