@Override public void visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { Set<String> interfacesSet = new LinkedHashSet<String>(); if (interfaces != null) Collections.addAll(interfacesSet, interfaces); for (Class extraInterface : classList) { if (extraInterface.isInterface()) interfacesSet.add(BytecodeHelper.getClassInternalName(extraInterface)); } final boolean addGroovyObjectSupport = !GroovyObject.class.isAssignableFrom(superClass); if (addGroovyObjectSupport) interfacesSet.add("groovy/lang/GroovyObject"); super.visit( V1_5, ACC_PUBLIC, proxyName, signature, BytecodeHelper.getClassInternalName(superClass), interfacesSet.toArray(new String[interfacesSet.size()])); addDelegateFields(); if (addGroovyObjectSupport) { createGroovyObjectSupport(); } for (Class clazz : classList) { visitClass(clazz); } }
/** * check whether a class should not be considered for transformation * * @param clazz the class to check * @return true if clazz should not be considered for transformation otherwise false */ protected boolean isSkipClass(Class<?> clazz) { if (!inst.isModifiableClass(clazz)) { return true; } // we can safely skip array classes, interfaces and primitive classes if (clazz.isArray()) { return true; } if (clazz.isInterface()) { return true; } if (clazz.isPrimitive()) { return true; } String name = clazz.getName(); if (isBytemanClass(name) || !isTransformable(name)) { return true; } return false; }
private void writeSetter(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) { WeaklyTypeReferencingMethod<?, Void> weakSetter = property.getSetter(); // There is no setter for this property if (weakSetter == null) { return; } String propertyName = property.getName(); Class<?> propertyClass = property.getType().getConcreteClass(); Type propertyType = Type.getType(propertyClass); Label calledOutsideOfConstructor = new Label(); Method setter = weakSetter.getMethod(); // the regular typed setter String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, propertyType); MethodVisitor methodVisitor = declareMethod( visitor, setter.getName(), methodDescriptor, AsmClassGeneratorUtils.signature(setter)); putCanCallSettersFieldValueOnStack(methodVisitor, generatedType); jumpToLabelIfStackEvaluatesToTrue(methodVisitor, calledOutsideOfConstructor); throwExceptionBecauseCalledOnItself(methodVisitor); methodVisitor.visitLabel(calledOutsideOfConstructor); putStateFieldValueOnStack(methodVisitor, generatedType); putConstantOnStack(methodVisitor, propertyName); putFirstMethodArgumentOnStack(methodVisitor, propertyType); if (propertyClass.isPrimitive()) { boxType(methodVisitor, propertyClass); } invokeStateSetMethod(methodVisitor); finishVisitingMethod(methodVisitor); }
public void addGetter(MetaBeanProperty property) throws Exception { MetaMethod getter = property.getGetter(); // GENERATE private boolean <prop>Set; String flagName = String.format("%sSet", property.getName()); visitor.visitField( Opcodes.ACC_PRIVATE, flagName, Type.BOOLEAN_TYPE.getDescriptor(), null, null); addConventionGetter(getter.getName(), flagName, property); String getterName = getter.getName(); Class<?> returnType = getter.getReturnType(); // If it's a boolean property, there can be get or is type variants. // If this class has both, decorate both. if (returnType.equals(Boolean.TYPE)) { boolean getterIsIsMethod = getterName.startsWith("is"); String propertyNameComponent = getterName.substring(getterIsIsMethod ? 2 : 3); String alternativeGetterName = String.format("%s%s", getterIsIsMethod ? "get" : "is", propertyNameComponent); try { type.getMethod(alternativeGetterName); addConventionGetter(alternativeGetterName, flagName, property); } catch (NoSuchMethodException e) { // ignore, no method to override } } }
@Test public void testEnclosingType() throws Exception { for (Class<?> type : standardTypes) { assertThat( describe(type).getEnclosingType(), type.getEnclosingClass() == null ? nullValue(TypeDescription.class) : is((TypeDescription) new TypeDescription.ForLoadedType(type.getEnclosingClass()))); } }
@Test public void testSourceName() throws Exception { for (Class<?> type : standardTypes) { if (type.isArray()) { assertThat(describe(type).getActualName(), is(type.getComponentType().getName() + "[]")); } else { assertThat(describe(type).getActualName(), is(type.getName())); } } }
/** * use the supplied bytes to define a class and try creating an instance via the empty * constructor printing details of any errors which occur * * @param classname * @param protectionDomain * @param bytes * @return the bytes if all goes well otherwise null */ public byte[] verify(String classname, ProtectionDomain protectionDomain, byte[] bytes) { try { Class clazz = super.defineClass(classname, bytes, 0, bytes.length, protectionDomain); clazz.newInstance(); } catch (Throwable th) { System.out.println("Transformer:verifyTransformedBytes " + th); return null; } return bytes; }
private void visitType(java.lang.reflect.Type type, StringBuilder builder) { if (type instanceof Class) { Class<?> cl = (Class<?>) type; if (cl.isPrimitive()) { builder.append(Type.getType(cl).getDescriptor()); } else { if (cl.isArray()) { builder.append(cl.getName().replace('.', '/')); } else { builder.append('L'); builder.append(cl.getName().replace('.', '/')); builder.append(';'); } } } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; visitNested(parameterizedType.getRawType(), builder); builder.append('<'); for (java.lang.reflect.Type param : parameterizedType.getActualTypeArguments()) { visitType(param, builder); } builder.append(">;"); } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type; if (wildcardType.getUpperBounds().length == 1 && wildcardType.getUpperBounds()[0].equals(Object.class)) { if (wildcardType.getLowerBounds().length == 0) { builder.append('*'); return; } } else { for (java.lang.reflect.Type upperType : wildcardType.getUpperBounds()) { builder.append('+'); visitType(upperType, builder); } } for (java.lang.reflect.Type lowerType : wildcardType.getLowerBounds()) { builder.append('-'); visitType(lowerType, builder); } } else if (type instanceof TypeVariable) { TypeVariable<?> typeVar = (TypeVariable) type; builder.append('T'); builder.append(typeVar.getName()); builder.append(';'); } else if (type instanceof GenericArrayType) { GenericArrayType arrayType = (GenericArrayType) type; builder.append('['); visitType(arrayType.getGenericComponentType(), builder); } else { throw new IllegalArgumentException( String.format("Cannot generate signature for %s.", type)); } }
/** * Visit every class/interface this proxy should implement, and generate the appropriate bytecode * for delegation if available. * * @param clazz an class for which to generate bytecode */ private void visitClass(final Class clazz) { Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { Class<?>[] exceptionTypes = method.getExceptionTypes(); String[] exceptions = new String[exceptionTypes.length]; for (int i = 0; i < exceptions.length; i++) { exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]); } // for each method defined in the class, generate the appropriate delegation bytecode visitMethod( method.getModifiers(), method.getName(), BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes()), null, exceptions); } Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor method : constructors) { Class<?>[] exceptionTypes = method.getExceptionTypes(); String[] exceptions = new String[exceptionTypes.length]; for (int i = 0; i < exceptions.length; i++) { exceptions[i] = BytecodeHelper.getClassInternalName(exceptionTypes[i]); } // for each method defined in the class, generate the appropriate delegation bytecode visitMethod( method.getModifiers(), "<init>", BytecodeHelper.getMethodDescriptor(Void.TYPE, method.getParameterTypes()), null, exceptions); } for (Class intf : clazz.getInterfaces()) { visitClass(intf); } Class superclass = clazz.getSuperclass(); if (superclass != null) visitClass(superclass); // Ultimately, methods can be available in the closure map which are not defined by the // superclass // nor the interfaces for (Map.Entry<String, Boolean> entry : delegatedClosures.entrySet()) { Boolean visited = entry.getValue(); if (!visited) { String name = entry.getKey(); if (!"*".equals(name)) { // generate a new method visitMethod(ACC_PUBLIC, name, "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); } } } }
public static Class<?> malform(Class<?> type) throws Exception { ClassReader classReader = new ClassReader(type.getName()); ClassWriter classWriter = new ClassWriter(classReader, 0); classReader.accept(new SignatureMalformer(classWriter), 0); ClassLoader classLoader = new ByteArrayClassLoader( null, Collections.singletonMap(type.getName(), classWriter.toByteArray()), null, ByteArrayClassLoader.PersistenceHandler.MANIFEST, PackageDefinitionStrategy.NoOp.INSTANCE); return classLoader.loadClass(type.getName()); }
private void visitNested(java.lang.reflect.Type type, StringBuilder builder) { if (type instanceof Class) { Class<?> cl = (Class<?>) type; if (cl.isPrimitive()) { builder.append(Type.getType(cl).getDescriptor()); } else { builder.append('L'); builder.append(cl.getName().replace('.', '/')); } } else { visitType(type, builder); } }
// the overload of type Object for Groovy coercions: public void setFoo(Object foo) private void createTypeConvertingSetter( ClassVisitor visitor, Type generatedType, ModelProperty<?> property) { if (!property.isWritable() || !(property.getSchema() instanceof ScalarValueSchema)) { return; } Class<?> propertyClass = property.getType().getConcreteClass(); Type propertyType = Type.getType(propertyClass); Class<?> boxedClass = propertyClass.isPrimitive() ? BOXED_TYPES.get(propertyClass) : propertyClass; Type boxedType = Type.getType(boxedClass); Method setter = property.getSetter().getMethod(); MethodVisitor methodVisitor = declareMethod( visitor, setter.getName(), SET_OBJECT_PROPERTY_DESCRIPTOR, SET_OBJECT_PROPERTY_DESCRIPTOR); putThisOnStack(methodVisitor); putTypeConverterFieldValueOnStack(methodVisitor, generatedType); // Object converted = $typeConverter.convert(foo, Float.class, false); methodVisitor.visitVarInsn(ALOAD, 1); // put var #1 ('foo') on the stack methodVisitor.visitLdcInsn(boxedType); // push the constant Class onto the stack methodVisitor.visitInsn( propertyClass.isPrimitive() ? ICONST_1 : ICONST_0); // push int 1 or 0 (interpreted as true or false) onto the stack methodVisitor.visitMethodInsn( INVOKEINTERFACE, TYPE_CONVERTER_TYPE.getInternalName(), "convert", COERCE_TO_SCALAR_DESCRIPTOR, true); methodVisitor.visitTypeInsn(CHECKCAST, boxedType.getInternalName()); if (propertyClass.isPrimitive()) { unboxType(methodVisitor, propertyClass); } // invoke the typed setter, popping 'this' and 'converted' from the stack methodVisitor.visitMethodInsn( INVOKEVIRTUAL, generatedType.getInternalName(), setter.getName(), Type.getMethodDescriptor(Type.VOID_TYPE, propertyType), false); finishVisitingMethod(methodVisitor); }
private static List<Method> getInheritedMethods(Class baseClass, List<Method> methods) { Collections.addAll(methods, baseClass.getMethods()); Class currentClass = baseClass; while (currentClass != null) { Method[] protectedMethods = currentClass.getDeclaredMethods(); for (Method method : protectedMethods) { if (method.getName().indexOf('$') != -1) continue; if (Modifier.isProtected(method.getModifiers()) && !containsEquivalentMethod(methods, method)) methods.add(method); } currentClass = currentClass.getSuperclass(); } return methods; }
private Method getToStringMethod(Class<?> managedTypeClass) { try { return managedTypeClass.getMethod("toString"); } catch (NoSuchMethodException e) { return null; } }
private boolean isTransformed(Class clazz, String name, boolean isInterface) { if (isBytemanClass(name) || !isTransformable(name)) { return false; } boolean found = false; List<RuleScript> scripts; if (isInterface) { scripts = scriptRepository.scriptsForInterfaceName(name); } else { scripts = scriptRepository.scriptsForClassName(name); } if (scripts != null) { for (RuleScript script : scripts) { if (!script.hasTransform(clazz)) { found = true; if (isVerbose()) { System.out.println("Retransforming loaded bootstrap class " + clazz.getName()); } break; } } } return found; }
public Class<? extends T> generate() { visitor.visitEnd(); byte[] bytecode = visitor.toByteArray(); return DEFINE_CLASS_METHOD.invoke( type.getClassLoader(), typeName, bytecode, 0, bytecode.length); }
protected void injectGetByIndex( ClassWriter classWriter, String targetClassName, List<Field> fields) { MethodVisitor methodVisitor = classWriter.visitMethod( ACC_PUBLIC, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", null, new String[] {getInternalName(ILLEGAL_ACCESS_EXCEPTION.getCanonicalName())}); Boxer boxer = new Boxer(methodVisitor); methodVisitor.visitCode(); methodVisitor.visitVarInsn(ILOAD, 2); int maxStack = 6; Label[] labels = new Label[fields.size()]; Label errorLabel = new Label(); for (int i = 0; i < fields.size(); i++) { labels[i] = new Label(); } methodVisitor.visitTableSwitchInsn(0, labels.length - 1, errorLabel, labels); if (!fields.isEmpty()) { maxStack--; for (int i = 0; i < fields.size(); i++) { Field field = fields.get(i); Class<?> type = field.getType(); String fieldDescriptor = Type.getDescriptor(type); methodVisitor.visitLabel(labels[i]); if (i == 0) methodVisitor.visitFrame(F_APPEND, 1, new Object[] {targetClassName}, 0, null); else methodVisitor.visitFrame(F_SAME, 0, null, 0, null); if (isPublic(field)) { methodVisitor.visitVarInsn(ALOAD, 1); methodVisitor.visitTypeInsn(CHECKCAST, targetClassName); methodVisitor.visitFieldInsn(GETFIELD, targetClassName, field.getName(), fieldDescriptor); boxer.box(Type.getType(type)); } else { injectReflectiveGetter(methodVisitor); } methodVisitor.visitInsn(ARETURN); } methodVisitor.visitLabel(errorLabel); methodVisitor.visitFrame(F_SAME, 0, null, 0, null); } injectException(methodVisitor, IllegalAccessException.class); methodVisitor.visitMaxs(maxStack, 3); methodVisitor.visitEnd(); }
private void castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) { if (returnType.isPrimitive()) { unboxType(methodVisitor, returnType); } else { methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType)); } }
public static Class getHelperAdapter(Rule rule, Class helperClass, boolean compileToBytecode) throws CompileException { Class adapterClass; // ok we have to create the adapter class // n.b. we don't bother synchronizing here -- if another rule is racing to create an adapter // in parallel we don't really care about generating two of them -- we can use whichever // one gets installed last try { String helperName = Type.getInternalName(helperClass); String compiledHelperName; // we put the helper in the if (compileToBytecode) { compiledHelperName = helperName + "_HelperAdapter_Compiled_" + nextId(); } else { compiledHelperName = helperName + "_HelperAdapter_Interpreted_" + nextId(); } byte[] classBytes = compileBytes(rule, helperClass, helperName, compiledHelperName, compileToBytecode); String externalName = compiledHelperName.replace('/', '.'); // dump the compiled class bytes if required Transformer.maybeDumpClass(externalName, classBytes); // ensure the class is loaded // think we need to load the generated helper using the class loader of the trigger class ClassLoader loader = rule.getLoader(); adapterClass = loadHelperAdapter(loader, externalName, classBytes); } catch (CompileException ce) { throw ce; } catch (Throwable th) { if (compileToBytecode) { throw new CompileException( "Compiler.createHelperAdapter : exception creating compiled helper adapter for " + helperClass.getName(), th); } else { throw new CompileException( "Compiler.createHelperAdapter : exception creating interpreted helper adapter for " + helperClass.getName(), th); } } return adapterClass; }
private ClassBuilderImpl(Class<T> type) { this.type = type; visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS); typeName = type.getName() + "_Decorated"; generatedType = Type.getType("L" + typeName.replaceAll("\\.", "/") + ";"); superclassType = Type.getType(type); }
private void writeDelegateMethods( final ClassVisitor visitor, final Type generatedType, StructSchema<?> delegateSchema, Set<Class<?>> typesToDelegate) { Class<?> delegateClass = delegateSchema.getType().getConcreteClass(); Type delegateType = Type.getType(delegateClass); Map<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> methodsToDelegate = Maps.newHashMap(); for (Class<?> typeToDelegate : typesToDelegate) { for (Method methodToDelegate : typeToDelegate.getMethods()) { if (ModelSchemaUtils.isIgnoredMethod(methodToDelegate)) { continue; } Equivalence.Wrapper<Method> methodKey = METHOD_EQUIVALENCE.wrap(methodToDelegate); Map<Class<?>, Method> methodsByReturnType = methodsToDelegate.get(methodKey); if (methodsByReturnType == null) { methodsByReturnType = Maps.newHashMap(); methodsToDelegate.put(methodKey, methodsByReturnType); } methodsByReturnType.put(methodToDelegate.getReturnType(), methodToDelegate); } } Set<Equivalence.Wrapper<Method>> delegateMethodKeys = ImmutableSet.copyOf( Iterables.transform( Arrays.asList(delegateClass.getMethods()), new Function<Method, Equivalence.Wrapper<Method>>() { @Override public Equivalence.Wrapper<Method> apply(Method method) { return METHOD_EQUIVALENCE.wrap(method); } })); for (Map.Entry<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> entry : methodsToDelegate.entrySet()) { Equivalence.Wrapper<Method> methodKey = entry.getKey(); if (!delegateMethodKeys.contains(methodKey)) { continue; } Map<Class<?>, Method> methodsByReturnType = entry.getValue(); for (Method methodToDelegate : methodsByReturnType.values()) { writeDelegatedMethod(visitor, generatedType, delegateType, methodToDelegate); } } }
@Test public void testEnclosingMethod() throws Exception { for (Class<?> type : standardTypes) { Matcher<MethodDescription> matcher; if (type.getEnclosingMethod() != null) { matcher = CoreMatchers.<MethodDescription>is( new MethodDescription.ForLoadedMethod(type.getEnclosingMethod())); } else if (type.getEnclosingConstructor() != null) { matcher = CoreMatchers.<MethodDescription>is( new MethodDescription.ForLoadedConstructor(type.getEnclosingConstructor())); } else { matcher = nullValue(MethodDescription.class); } assertThat(describe(type).getEnclosingMethod(), matcher); } }
@Override public FieldAccessor build(ClassLoader classLoader) { String targetClassType = getInternalName(this.getTarget().getName()); ClassWriter classWriter = this.getClassWriter(); injectFieldTable(classWriter); injectConstructor(classWriter); injectGetTargetClass(classWriter, this.getTarget()); List<Field> fields = new ArrayList<>(); Class<?> current = this.getTarget(); while (current != Object.class) { for (Field field : current.getDeclaredFields()) { fields.add(field); } current = current.getSuperclass(); } injectGetByIndex(classWriter, targetClassType, fields); injectSetByIndex(classWriter, targetClassType, fields); injectGetByName(classWriter); injectSetByName(classWriter); injectGetFieldTable(classWriter); injectGetIndex(classWriter); classWriter.visitEnd(); Class<?> result = CompilerService.create(classLoader) .defineClass( getExternalName( getAccessorNameInternal( this.getTarget(), this .getAccessorType())), // this is somewhat redundant but maybe in the // future the class-name-format changes this.getClassWriter().toByteArray()); return (FieldAccessor) instantiate(result); }
/** * Creates a visitor which will be used as a base class for initiating the visit. It is not * necessary that the class is the superclass, any will do, as long as it can be loaded from a * byte[]. * * @param baseClass * @return */ private ClassReader createClassVisitor(final Class baseClass) { try { String name = baseClass.getName(); String path = name.replace('.', '/') + ".class"; InputStream in = loader.getResourceAsStream(path); return new ClassReader(in); } catch (IOException e) { throw new GroovyRuntimeException( "Unable to generate a proxy for " + baseClass + " from class loader " + loader, e); } }
protected void injectException( MethodVisitor methodVisitor, Class<? extends Throwable> throwable) { methodVisitor.visitTypeInsn(NEW, getInternalName(throwable.getCanonicalName())); methodVisitor.visitInsn(DUP); methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder"); methodVisitor.visitInsn(DUP); methodVisitor.visitLdcInsn("Invalid index: "); methodVisitor.visitMethodInsn( INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V"); methodVisitor.visitVarInsn(ILOAD, 2); methodVisitor.visitMethodInsn( INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;"); methodVisitor.visitMethodInsn( INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); methodVisitor.visitMethodInsn( INVOKESPECIAL, getInternalName(throwable.getCanonicalName()), "<init>", "(Ljava/lang/String;)V"); methodVisitor.visitInsn(ATHROW); }
@Test public void testIsPackageDescription() throws Exception { assertThat( describe( Class.forName( Sample.class.getPackage().getName() + "." + PackageDescription.PACKAGE_CLASS_NAME)) .isPackageType(), is(true)); assertThat(describe(Object.class).isPackageType(), is(false)); }
private static void resetLambdaClassSequenceNumber() { try { Field counterField = Class.forName("java.lang.invoke.InnerClassLambdaMetafactory").getDeclaredField("counter"); counterField.setAccessible(true); AtomicInteger counter = (AtomicInteger) counterField.get(null); counter.set(0); } catch (Throwable t) { // print to stdout to keep in sync with other log output System.out.println( "WARNING: Failed to start class numbering from one. Don't worry, it's cosmetic, " + "but please file a bug report and tell on which JDK version this happened."); t.printStackTrace(System.out); } }
private void assertAnnotations(Class<?> type) { assertThat( describe(type).getDeclaredAnnotations(), hasItems( new AnnotationList.ForLoadedAnnotations(type.getDeclaredAnnotations()) .toArray(new AnnotationDescription[type.getDeclaredAnnotations().length]))); assertThat( describe(type).getDeclaredAnnotations().size(), is(type.getDeclaredAnnotations().length)); assertThat( describe(type).getInheritedAnnotations(), hasItems( new AnnotationList.ForLoadedAnnotations(type.getAnnotations()) .toArray(new AnnotationDescription[type.getAnnotations().length]))); assertThat(describe(type).getInheritedAnnotations().size(), is(type.getAnnotations().length)); }
private void unboxType(MethodVisitor methodVisitor, Class<?> primitiveClass) { // Float f = (Float) tmp // f==null?0:f.floatValue() Class<?> boxedType = BOXED_TYPES.get(primitiveClass); Type primitiveType = Type.getType(primitiveClass); methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(boxedType)); methodVisitor.visitInsn(DUP); Label exit = new Label(); Label elseValue = new Label(); methodVisitor.visitJumpInsn(IFNONNULL, elseValue); methodVisitor.visitInsn(POP); pushDefaultValue(methodVisitor, primitiveClass); methodVisitor.visitJumpInsn(GOTO, exit); methodVisitor.visitLabel(elseValue); methodVisitor.visitMethodInsn( INVOKEVIRTUAL, Type.getInternalName(boxedType), primitiveClass.getSimpleName() + "Value", Type.getMethodDescriptor(primitiveType), false); methodVisitor.visitLabel(exit); }
private String proxyName() { String name = delegateClass != null ? delegateClass.getName() : superClass.getName(); int index = name.lastIndexOf('.'); if (index == -1) return name + pxyCounter.incrementAndGet() + "_groovyProxy"; return name.substring(index + 1, name.length()) + pxyCounter.incrementAndGet() + "_groovyProxy"; }