package xapi.bytecode.impl; import xapi.bytecode.ClassPool; import xapi.bytecode.CtClass; import xapi.bytecode.CtClassType; import xapi.bytecode.CtField; import xapi.bytecode.CtMethod; import xapi.bytecode.NotFoundException; import xapi.bytecode.annotation.Annotation; import xapi.bytecode.annotation.AnnotationDefaultAttribute; import xapi.bytecode.annotation.MemberValue; import xapi.bytecode.attributes.ExceptionsAttribute; import xapi.bytecode.attributes.InnerClassesAttribute; import xapi.bytecode.attributes.SignatureAttribute; import xapi.collect.api.InitMap; import xapi.collect.impl.InitMapDefault; import xapi.except.NotImplemented; import xapi.except.NotYetImplemented; import xapi.inject.impl.SingletonProvider; import xapi.log.X_Log; import xapi.source.X_Modifier; import xapi.source.X_Source; import xapi.source.api.IsAnnotation; import xapi.source.api.IsAnnotationValue; import xapi.source.api.IsClass; import xapi.source.api.IsField; import xapi.source.api.IsGeneric; import xapi.source.api.IsMember; import xapi.source.api.IsMethod; import xapi.source.api.IsType; import xapi.source.api.Primitives; import xapi.source.impl.DeclaredMemberFilter; import xapi.source.impl.ImmutableType; import xapi.source.impl.IsClassDelegate; import xapi.source.service.SourceAdapterService; import xapi.util.X_Debug; import xapi.util.X_String; import xapi.util.api.ConvertsValue; import xapi.util.api.Pair; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; public class BytecodeAdapterService implements SourceAdapterService<String, CtMethod, CtField, Annotation> { private final ClassPool pool; // In order to delay calling getScanUrls() until after all constructors are // called, // We use a lazy provider to delay the call until the class pool is actually // needed. protected final SingletonProvider<ClassPool> classPool = new SingletonProvider<ClassPool>() { @Override protected ClassPool initialValue() { for (URL url : getScanUrls()) { try { pool.appendClassPath(url.toExternalForm().replace("file:", "")); } catch (NotFoundException e) { e.printStackTrace(); } } return pool; }; }; InitMap<String, IsClass> classes = new InitMapDefault<String, IsClass>( InitMapDefault.PASS_THRU, new ConvertsValue<String, IsClass>(){ @Override public IsClass convert(String classString) { Pair<String, Integer> cls = X_Source.extractArrayDepth(classString); String clsName = cls.get0().split("/")[0]; // lambdas can have / in classname... final ClassLoader cl = getClassLoader(); URL location = X_Source.classToUrl(clsName, cl); if (location == null && clsName.contains("$$Lambda")) { clsName = clsName.split("[$][$]Lambda")[0]; location = X_Source.classToUrl(clsName, cl); } while (location == null) { // Might be an inner class. Lets have a peek. int lastPeriod = clsName.lastIndexOf('.'); if (lastPeriod == -1) { break; } char[] newName = clsName.toCharArray(); newName[lastPeriod] = '$'; clsName = new String(newName); location = X_Source.classToUrl(clsName, cl); } IsClass asClass; try { X_Log.debug("Converting",cls.get0(),"to",location); asClass = new ClassAdapter(new CtClassType(location.openStream(), classPool.get())); } catch (NullPointerException e) { if (cls.get0().equals(cls.get0().toLowerCase())) { asClass = Primitives.valueOf("_"+cls.get0()); } else { throw X_Debug.rethrow(e); } } catch (IOException e) { X_Log.error("Unable to find "+cls.get0()); throw X_Debug.rethrow(e); } if (cls.get1()>0) { return new IsClassDelegate(asClass, cls.get1()); } return asClass; } }); public BytecodeAdapterService() { this(new ClassPool(true)); } public BytecodeAdapterService(ClassPool pool) { this.pool = pool; } protected URL[] getScanUrls() { return X_Source.getUrls(getClassLoader()); } @Override public IsClass toClass(String binaryName) { return classes.get(binaryName); } protected ClassLoader getClassLoader() { return Thread.currentThread().getContextClassLoader(); // BytecodeAdapterService.class.getClassLoader(); } @Override public IsMethod toMethod(CtMethod type) { return new MethodAdapter(type); } @Override public IsField toField(CtField type) { return new FieldAdapter(type); } @Override public IsAnnotation toAnnotation(Annotation type) { return new AnnotationAdapter(type); } class AnnotationAdapter implements xapi.source.api.IsAnnotation { private Annotation anno; private SingletonProvider<IsClass> annoClass = new SingletonProvider<IsClass>() { @Override protected IsClass initialValue() { return toClass(anno.getTypeName()); }; }; private SingletonProvider<IsAnnotation> retentionAnno = new SingletonProvider<IsAnnotation>() { @Override protected IsAnnotation initialValue() { return annoClass.get().getAnnotation(Retention.class.getName()); }; }; public AnnotationAdapter(Annotation type) { this.anno = type; } @Override public boolean isPrimitive() { return false; } @Override public boolean isCompile() { IsAnnotation retention = retentionAnno.get(); if (retention == null) { return true; } IsMethod value = retention.getMethod("value"); IsAnnotationValue val = retention.getValue(value); return RetentionPolicy.CLASS == val.getRawValue(); } @Override public boolean isRuntime() { IsAnnotation retention = retentionAnno.get(); if (retention == null) { return false; } IsMethod value = retention.getMethod("value"); IsAnnotationValue val = retention.getValue(value); return RetentionPolicy.RUNTIME == val.getRawValue(); } @Override public boolean isSource() { IsAnnotation retention = retentionAnno.get(); if (retention == null) { return false; } IsMethod value = retention.getMethod("value"); IsAnnotationValue val = retention.getValue(value); return RetentionPolicy.SOURCE == val.getRawValue(); } @Override public IsType getEnclosingType() { return annoClass.get().getEnclosingType(); } @Override public String getPackage() { return annoClass.get().getPackage(); } @Override public String getSimpleName() { return annoClass.get().getSimpleName(); } @Override public String getEnclosedName() { return annoClass.get().getEnclosedName(); } @Override public String getQualifiedName() { return annoClass.get().getQualifiedName(); } @Override public Iterable<IsMethod> getMethods() { return annoClass.get().getMethods(); } @Override public Iterable<IsMethod> getDeclaredMethods() { return annoClass.get().getDeclaredMethods(); } @Override public IsMethod getMethod(String name, IsType... params) { return annoClass.get().getMethod(name, params); } @Override public IsMethod getMethod(String name, boolean checkErased, Class<?>... params) { return annoClass.get().getMethod(name, checkErased, params); } @Override public IsAnnotationValue getDefaultValue(IsMethod method) { return method.getDefaultValue(); } @Override public IsAnnotationValue getValue(IsMethod value) { MemberValue val = anno.getMemberValue(value.getName()); if (val == null) { return getDefaultValue(value); } return BytecodeUtil.extractValue(val, BytecodeAdapterService.this, value.getReturnType()); } @Override public Object toAnnotation(ClassLoader loader) { throw new NotYetImplemented("AnnotationProxy not yet implemented"); } @Override public String toString() { return getQualifiedName(); } @Override public int hashCode() { return getQualifiedName().hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof IsAnnotation) { return ((IsAnnotation)obj).getQualifiedName().equals(getQualifiedName()); } return false; } } abstract class MemberAdapter implements IsMember { int modifier; ImmutableType type; protected final SingletonProvider<Iterable<IsAnnotation>> annotations = new SingletonProvider<Iterable<IsAnnotation>>() { @Override protected Iterable<IsAnnotation> initialValue() { Annotation[] annos = getRawAnnotations(); if (annos == null) { annos = new Annotation[0]; } return new MemberIterable<Annotation, IsAnnotation>(annotationBuilder, annos); }; }; MemberAdapter(int modifier, String pkg, String enclosedName) { this.modifier = modifier; this.type = new ImmutableType(pkg, enclosedName); } protected abstract Annotation[] getRawAnnotations(); @Override public boolean isPrimitive() { return type.isPrimitive(); } @Override public IsType getEnclosingType() { return type.getEnclosingType(); } @Override public String getPackage() { return type.getPackage(); } @Override public String getSimpleName() { return type.getSimpleName(); } @Override public String getEnclosedName() { return type.getEnclosedName(); } @Override public String getQualifiedName() { return type.getQualifiedName(); } @Override public Iterable<xapi.source.api.IsAnnotation> getAnnotations() { return annotations.get(); } @Override public IsAnnotation getAnnotation(String name) { if (name.indexOf('.') == -1) { for (IsAnnotation anno : getAnnotations()) { if (anno.getSimpleName().equals(name)) { return anno; } } } else { for (IsAnnotation anno : getAnnotations()) { if (anno.getQualifiedName().equals(name)) { return anno; } } } return null; } @Override public boolean isPublic() { return X_Modifier.isPublic(modifier); } @Override public boolean isPrivate() { return X_Modifier.isPrivate(modifier); } @Override public boolean isProtected() { return X_Modifier.isProtected(modifier); } @Override public boolean isPackageProtected() { return X_Modifier.isPackage(modifier); } @Override public boolean hasModifier(int modifier) { return X_Modifier.contains(this.modifier, modifier); } @Override public int getModifier() { return modifier; } @Override public int hashCode() { return getQualifiedName().hashCode(); } @Override public boolean equals(Object obj) { if (obj instanceof IsType) { return ((IsType)obj).getQualifiedName().equals(getQualifiedName()); } return false; } @Override public String toString() { return getQualifiedName(); } } class MemberIterable<From, To> implements Iterable<To> { private To[] members; @SuppressWarnings("unchecked") public MemberIterable(ConvertsValue<From, To> converter, From[] from) { this.members = (To[]) new Object[from.length]; for (int i = 0, m = from.length; i < m; ++i) { members[i] = converter.convert(from[i]); } } @Override public Iterator<To> iterator() { return new Itr(); } private class Itr implements Iterator<To> { private int pos = 0; @Override public boolean hasNext() { return pos < members.length; } @Override public To next() { return members[pos++]; } @Override public void remove() { throw new UnsupportedOperationException(); } } } private final ConvertsValue<Annotation, IsAnnotation> annotationBuilder = new ConvertsValue<Annotation, IsAnnotation>() { @Override public IsAnnotation convert(Annotation from) { return new AnnotationAdapter(from); } }; private final ConvertsValue<CtMethod, IsMethod> methodBuilder = new ConvertsValue<CtMethod, IsMethod>() { @Override public IsMethod convert(CtMethod from) { return new MethodAdapter(from); } }; private final ConvertsValue<CtField, IsField> fieldBuilder = new ConvertsValue<CtField, IsField>() { @Override public IsField convert(CtField from) { return new FieldAdapter(from); } }; private final ConvertsValue<CtClass, IsClass> interfaceBuilder = new ConvertsValue<CtClass, IsClass>() { @Override public IsClass convert(CtClass from) { return toClass(from.getName()); } }; class ClassAdapter extends MemberAdapter implements IsClass { private CtClass cls; private SingletonProvider<Iterable<IsMethod>> methods = new SingletonProvider<Iterable<IsMethod>>() { @Override protected Iterable<IsMethod> initialValue() { return new MemberIterable<CtMethod, IsMethod>(methodBuilder, cls.getMethods()); }; }; private SingletonProvider<Iterable<IsField>> fields = new SingletonProvider<Iterable<IsField>>() { @Override protected Iterable<IsField> initialValue() { return new MemberIterable<CtField, IsField>(fieldBuilder, cls.getFields()); }; }; private SingletonProvider<Iterable<IsClass>> interfaces = new SingletonProvider<Iterable<IsClass>>() { @Override protected Iterable<IsClass> initialValue() { try { return new MemberIterable<CtClass, IsClass>(interfaceBuilder, cls.getInterfaces()); } catch (NotFoundException e) { throw new RuntimeException("Unable to load interfaces for " + X_String.join(", ", cls.getClassFile2().getInterfaces()), e); } }; }; private SingletonProvider<Iterable<IsClass>> innerClasses = new SingletonProvider<Iterable<IsClass>>() { @Override protected Iterable<IsClass> initialValue() { try { return new MemberIterable<CtClass, IsClass>(interfaceBuilder, cls.getNestedClasses()); } catch (NotFoundException e) { throw new RuntimeException("Unable to load interfaces for " + cls.getAttribute(InnerClassesAttribute.tag), e); } }; }; public ClassAdapter(CtClass type) { super(type.getModifiers(), type.getPackageName(), type.getEnclosedName()); this.cls = type; } @Override protected Annotation[] getRawAnnotations() { return cls.getClassFile2().getAnnotations(); } @Override public Iterable<IsMethod> getDeclaredMethods() { return new DeclaredMemberFilter<IsMethod>(getMethods(), this); } @Override public Iterable<IsMethod> getMethods() { return methods.get(); } @Override public IsMethod getMethod(String name, IsType... params) { assert name != null; for (IsMethod method : getMethods()) { if (method.getName().equals(name)) { if (X_Source.typesEqual(method.getParameters(), params)) { return method; } } } return null; } @Override public IsAnnotation getAnnotation(String name) { return super.getAnnotation(name); } @Override public IsMethod getMethod(String name, boolean checkErased, Class<?>... params) { assert name != null; for (IsMethod method : getMethods()) { if (method.getName().equals(name)) { if (X_Source.typesEqual(method.getParameters(), params)) { return method; } } } if (checkErased) { // TODO: lookup hierarchy of each param's IsType to see if we can get a match } return null; } @Override public Iterable<IsField> getFields() { return fields.get(); } @Override public IsField getField(String name) { for (IsField field : getFields()) { if (field.getName().equals(name)) { return field; } } return null; } @Override @SuppressWarnings("unchecked") public Iterable<IsGeneric> getGenerics() { SignatureAttribute attr = (SignatureAttribute) cls.getClassFile2() .getAttribute(SignatureAttribute.tag); if (attr == null) { return Collections.EMPTY_LIST; } System.err.println(attr); return Collections.EMPTY_LIST; } @Override public IsGeneric getGeneric(String name) { for (IsGeneric g : getGenerics()) { if (g.genericName().equals(name)) { return g; } } return null; } @Override public boolean hasGenerics() { return getGenerics().iterator().hasNext(); } @Override public Iterable<IsClass> getInterfaces() { return interfaces.get(); } @Override public boolean hasInterface() { try { return cls.getInterfaces().length > 0; } catch (NotFoundException e) { throw X_Debug.rethrow(e); } } @Override public boolean isAbstract() { return X_Modifier.isAbstract(getModifier()); } @Override public boolean isAnnotation() { return X_Modifier.isAnnotation(getModifier()); } @Override public boolean isArray() { return cls.isArray(); } @Override public boolean isEnum() { return X_Modifier.isEnum(getModifier()); } @Override public boolean isFinal() { return X_Modifier.isFinal(getModifier()); } @Override public boolean isStatic() { return X_Modifier.isStatic(getModifier()); } @Override public boolean isInterface() { return cls.isInterface(); } @Override public IsMethod getEnclosingMethod() { try { CtMethod enclosed = cls.getEnclosingMethod(); if (enclosed == null) { return null; } IsClass method = BytecodeAdapterService.this.toClass(enclosed .getDeclaringClass().getName()); return method.getMethod(enclosed.getName(), BytecodeAdapterService.this.toTypes(enclosed.getParameterTypes())); } catch (NotFoundException e) { throw X_Debug.rethrow(e); } } @Override public Iterable<IsClass> getInnerClasses() { CtClass[] clses; try { clses = cls.getNestedClasses(); } catch (NotFoundException e) { throw X_Debug.rethrow(e); } ArrayList<IsClass> asClasses = new ArrayList<IsClass>(clses.length); for (CtClass cls : clses) { asClasses.add(new ClassAdapter(cls)); } return asClasses; } @Override public Class<?> toClass(ClassLoader loader) throws ClassNotFoundException { throw new NotImplemented("Class loading is not yet implemented"); } @Override public String toSignature() { return cls.getName(); } @Override public final String toString() { return cls.getName(); } } class MethodAdapter extends MemberAdapter implements IsMethod { private CtMethod method; public MethodAdapter(CtMethod type) { super(type.getModifiers(), type.getDeclaringClass().getPackageName(), type.getDeclaringClass().getEnclosedName()); this.method = type; } @Override protected Annotation[] getRawAnnotations() { return method.getMethodInfo2().getAnnotations(); } @Override public IsClass getEnclosingType() { return toClass(type.getPackage() + "." + type.getEnclosedName().replace('.', '$')); } @Override public boolean isAbstract() { return X_Modifier.isAbstract(getModifier()); } @Override public IsAnnotation getAnnotation(String name) { // TODO Auto-generated method stub return super.getAnnotation(name); } @Override public boolean isStatic() { return X_Modifier.isStatic(getModifier()); } @Override public String getName() { return method.getName(); } @Override public IsType getReturnType() { try { CtClass returnType = method.getReturnType(); return X_Source.toType(returnType.getPackageName(), returnType.getEnclosedName()); } catch (NotFoundException e) { throw new AssertionError(e); } } @Override public IsType[] getParameters() { try { return toTypes(method.getParameterTypes()); } catch (NotFoundException e) { throw new AssertionError(e); } } @Override public IsGeneric[] getGenerics() { return null; } @Override public IsType[] getExceptions() { try { return toTypes(method.getExceptionTypes()); } catch (NotFoundException e) { ExceptionsAttribute exceptions = method.getMethodInfo2() .getExceptionsAttribute(); return X_Source.toTypes(exceptions.getExceptions()); } } @Override public IsAnnotationValue getDefaultValue() { AnnotationDefaultAttribute def = (AnnotationDefaultAttribute)method.getMethodInfo2().getAttribute(AnnotationDefaultAttribute.tag); IsType type; try { type = new ImmutableType(method.getReturnType().getPackageName(), method.getReturnType().getEnclosedName()); } catch (NotFoundException e) { X_Log.warn("Not able to lookup return type for ",method, e); type = null; } return def == null ? null : BytecodeUtil.extractValue(def.getDefaultValue(), BytecodeAdapterService.this, type); } @Override public String toSignature() { return method.getSignature(); } @Override public String toString() { try { return method.getReturnType().getName() + " " + method.getLongName(); } catch (NotFoundException e) { throw new AssertionError(e); } } } class FieldAdapter extends MemberAdapter implements IsField { private CtField field; public FieldAdapter(CtField type) { super(type.getModifiers(), type.getDeclaringClass().getPackageName(), type.getDeclaringClass().getEnclosedName()); this.field = type; } @Override protected Annotation[] getRawAnnotations() { return field.getFieldInfo2().getAnnotations(); } @Override public IsClass getEnclosingType() { return (IsClass) super.getEnclosingType(); } @Override public String getName() { return field.getName(); } @Override public boolean isStatic() { return X_Modifier.isStatic(getModifier()); } @Override public boolean isVolatile() { return X_Modifier.isVolatile(getModifier()); } @Override public boolean isTransient() { return X_Modifier.isTransient(getModifier()); } @Override public String toSignature() { return field.getSignature(); } @Override public String toString() { return toSignature(); } } public IsType[] toTypes(CtClass[] parameterTypes) { IsType[] types = new IsType[parameterTypes.length]; for (int i = 0, m = parameterTypes.length; i < m; ++i) { CtClass param = parameterTypes[i]; types[i] = X_Source.toType(param.getPackageName(), param.getEnclosedName()); } return types; } }