public Class<? extends T> generate() { visitor.visitEnd(); byte[] bytecode = visitor.toByteArray(); return DEFINE_CLASS_METHOD.invoke( type.getClassLoader(), typeName, bytecode, 0, bytecode.length); }
/** * Generates an implementation of the given managed type. * * <p>The generated class will implement/extend the managed type and will: * * <ul> * <li>provide implementations for abstract getters and setters that delegate to model nodes * <li>provide a `toString()` implementation * <li>mix-in implementation of {@link ManagedInstance} * <li>provide a constructor that accepts a {@link ModelElementState}, which will be used to * implement the above. * </ul> * * In case a delegate schema is supplied, the generated class will also have: * * <ul> * <li>a constructor that also takes a delegate instance * <li>methods that call through to the delegate instance * </ul> */ public <T, M extends T, D extends T> Class<? extends M> generate( StructSchema<M> viewSchema, @Nullable StructSchema<D> delegateSchema) { if (delegateSchema != null && Modifier.isAbstract(delegateSchema.getType().getConcreteClass().getModifiers())) { throw new IllegalArgumentException("Delegate type must be null or a non-abstract type"); } ClassWriter visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); ModelType<M> viewType = viewSchema.getType(); StringBuilder generatedTypeNameBuilder = new StringBuilder(viewType.getName()); if (delegateSchema != null) { generatedTypeNameBuilder .append("$BackedBy_") .append(delegateSchema.getType().getName().replaceAll("\\.", "_")); } else { generatedTypeNameBuilder.append("$Impl"); } String generatedTypeName = generatedTypeNameBuilder.toString(); Type generatedType = Type.getType("L" + generatedTypeName.replaceAll("\\.", "/") + ";"); Class<M> viewClass = viewType.getConcreteClass(); Class<?> superclass; final ImmutableSet.Builder<String> interfacesToImplement = ImmutableSet.builder(); final ImmutableSet.Builder<Class<?>> typesToDelegate = ImmutableSet.builder(); typesToDelegate.add(viewClass); interfacesToImplement.add(MANAGED_INSTANCE_TYPE); if (viewClass.isInterface()) { superclass = Object.class; interfacesToImplement.add(Type.getInternalName(viewClass)); } else { superclass = viewClass; } // TODO:LPTR This should be removed once BinaryContainer is a ModelMap // We need to also implement all the interfaces of the delegate type because otherwise // BinaryContainer won't recognize managed binaries as BinarySpecInternal if (delegateSchema != null) { ModelSchemaUtils.walkTypeHierarchy( delegateSchema.getType().getConcreteClass(), new ModelSchemaUtils.TypeVisitor<D>() { @Override public void visitType(Class<? super D> type) { if (type.isInterface()) { typesToDelegate.add(type); interfacesToImplement.add(Type.getInternalName(type)); } } }); } generateProxyClass( visitor, viewSchema, delegateSchema, interfacesToImplement.build(), typesToDelegate.build(), generatedType, Type.getType(superclass)); ClassLoader targetClassLoader = viewClass.getClassLoader(); if (delegateSchema != null) { // TODO - remove this once the above is removed try { viewClass.getClassLoader().loadClass(delegateSchema.getType().getConcreteClass().getName()); } catch (ClassNotFoundException e) { // Delegate class is not visible to managed view type -> view type is more general than // delegate type, so use the delegate classloader instead targetClassLoader = delegateSchema.getType().getConcreteClass().getClassLoader(); } } return defineClass(visitor, targetClassLoader, generatedTypeName); }
private InnerLoader findClassLoader(Class clazz) { ClassLoader cl = clazz.getClassLoader(); if (cl == null) cl = this.getClass().getClassLoader(); return new InnerLoader(cl); }
public static List<InstantiatorDefinition> extractDefinitions(final Type target) throws IOException { final List<InstantiatorDefinition> constructors = new ArrayList<InstantiatorDefinition>(); final Class<?> targetClass = TypeHelper.toClass(target); ClassLoader cl = targetClass.getClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } final String fileName = targetClass.getName().replace('.', '/') + ".class"; final InputStream is = cl.getResourceAsStream(fileName); try { if (is == null) { throw new IOException("Cannot find file " + fileName + " in " + cl); } ClassReader classReader = new ClassReader(is); classReader.accept( new ClassVisitor(Opcodes.ASM5) { List<String> genericTypeNames; @Override public void visit( int version, int access, String name, String signature, String superName, String[] interfaces) { if (signature != null) { genericTypeNames = AsmUtils.extractGenericTypeNames(signature); } else { genericTypeNames = Collections.emptyList(); } super.visit(version, access, name, signature, superName, interfaces); } @Override public MethodVisitor visitMethod( int access, final String methodName, String desc, String signature, String[] exceptions) { final boolean isConstructor = "<init>".equals(methodName); if ((Opcodes.ACC_PUBLIC & access) == Opcodes.ACC_PUBLIC && (isConstructor || ((Opcodes.ACC_STATIC & access) == Opcodes.ACC_STATIC && !desc.endsWith("V")))) { final List<String> descTypes = AsmUtils.extractTypeNames(desc); final List<String> genericTypes; final List<String> names = new ArrayList<String>(); if (signature != null) { genericTypes = AsmUtils.extractTypeNames(signature); } else { genericTypes = descTypes; } if (!isConstructor) { if (descTypes.size() > 0) { try { final Type genericType = AsmUtils.toGenericType( descTypes.get(descTypes.size() - 1), genericTypeNames, target); if (!targetClass.isAssignableFrom(TypeHelper.toClass(genericType))) { return null; } } catch (ClassNotFoundException e) { return null; } } else return null; } return new MethodVisitor(Opcodes.ASM5) { Label firstLabel; Label lastLabel; @Override public void visitLabel(Label label) { if (firstLabel == null) { firstLabel = label; } lastLabel = label; } @Override public void visitLocalVariable( String name, String desc, String signature, Label start, Label end, int index) { if (start.equals(firstLabel) && end.equals(lastLabel) && !"this".equals(name)) { names.add(name); } } @Override public void visitEnd() { try { final List<Parameter> parameters = new ArrayList<Parameter>(); int l = descTypes.size() - (isConstructor ? 0 : 1); for (int i = 0; i < l; i++) { String name = "arg" + i; if (i < names.size()) { name = names.get(i); } parameters.add( createParameter(i, name, descTypes.get(i), genericTypes.get(i))); } final Member executable; if (isConstructor) { executable = targetClass.getDeclaredConstructor(toTypeArray(parameters)); } else { executable = targetClass.getDeclaredMethod(methodName, toTypeArray(parameters)); } constructors.add( new ExecutableInstantiatorDefinition( executable, parameters.toArray(new Parameter[0]))); } catch (Exception e) { ErrorHelper.rethrow(e); } } private Class<?>[] toTypeArray(List<Parameter> parameters) { Class<?>[] types = new Class<?>[parameters.size()]; for (int i = 0; i < types.length; i++) { types[i] = parameters.get(i).getType(); } return types; } private Parameter createParameter( int index, String name, String desc, String signature) { try { Type basicType = AsmUtils.toGenericType(desc, genericTypeNames, target); Type genericType = basicType; if (signature != null) { Type type = AsmUtils.toGenericType(signature, genericTypeNames, target); if (type != null) { genericType = type; } } return new Parameter(index, name, TypeHelper.toClass(basicType), genericType); } catch (ClassNotFoundException e) { throw new Error("Unexpected error " + e, e); } } }; } else { return null; } } }, 0); } finally { try { is.close(); } catch (Exception e) { } } return constructors; }