/** * 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; }
@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); } }
/** * Construct a proxy generator. This generator is used when we need to create a proxy object for a * class or an interface given a map of closures. * * @param closureMap the delegates implementations * @param superClass corresponding to the superclass class visitor * @param interfaces extra interfaces the proxy should implement * @param proxyLoader the class loader which should be used to load the generated proxy * @param delegateClass if not null, generate a delegate field with the corresponding class * @param emptyBody if set to true, the unimplemented abstract methods will receive an empty body * instead of throwing an {@link UnsupportedOperationException}. */ public ProxyGeneratorAdapter( final Map<Object, Object> closureMap, final Class superClass, final Class[] interfaces, final ClassLoader proxyLoader, final boolean emptyBody, final Class delegateClass) { super(new ClassWriter(0)); this.visitedMethods = new LinkedHashSet<Object>(); this.delegatedClosures = closureMap.isEmpty() ? EMPTY_DELEGATECLOSURE_MAP : new HashMap<String, Boolean>(); boolean wildcard = false; for (Map.Entry<Object, Object> entry : closureMap.entrySet()) { String name = entry.getKey().toString(); if ("*".equals(name)) { wildcard = true; } this.delegatedClosures.put(name, Boolean.FALSE); } this.hasWildcard = wildcard; // if we have to delegate to another object, generate the appropriate delegate field // and collect the name of the methods for which delegation is active this.generateDelegateField = delegateClass != null; this.objectDelegateMethods = generateDelegateField ? createDelegateMethodList(delegateClass, interfaces) : EMPTY_STRING_SET; this.delegateClass = delegateClass; // a proxy is supposed to be a concrete class, so it cannot extend an interface. // If the provided superclass is an interface, then we replace the superclass with Object // and add this interface to the list of implemented interfaces boolean isSuperClassAnInterface = superClass.isInterface(); this.superClass = isSuperClassAnInterface ? Object.class : superClass; // create the base list of classes which have possible methods to be overloaded this.classList = new LinkedList<Class>(); this.classList.add(superClass); if (generateDelegateField) { classList.add(delegateClass); } if (interfaces != null) { Collections.addAll(this.classList, interfaces); } this.proxyName = proxyName(); this.loader = proxyLoader != null ? new InnerLoader(proxyLoader) : findClassLoader(superClass); this.emptyBody = emptyBody; // generate bytecode ClassWriter writer = (ClassWriter) cv; ClassReader cr = createClassVisitor(Object.class); cr.accept(this, 0); byte[] b = writer.toByteArray(); // CheckClassAdapter.verify(new ClassReader(b), true, new PrintWriter(System.err)); cachedClass = loader.defineClass(proxyName.replace('/', '.'), b); // cache no-arg constructor Class[] args = generateDelegateField ? new Class[] {Map.class, delegateClass} : new Class[] {Map.class}; Constructor constructor; try { constructor = cachedClass.getConstructor(args); } catch (NoSuchMethodException e) { constructor = null; } cachedNoArgConstructor = constructor; }
/** * 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); }