PageRenderTime 66ms CodeModel.GetById 12ms app.highlight 49ms RepoModel.GetById 1ms app.codeStats 0ms

/src/kilim/mirrors/CachedClassMirrors.java

http://github.com/kilim/kilim
Java | 287 lines | 219 code | 56 blank | 12 comment | 26 complexity | 3f30a903338b3fa857bd281535ef7b70 MD5 | raw file
  1// copyright 2016 nqzero, 2014 sriram srinivasan - offered under the terms of the MIT License
  2
  3package kilim.mirrors;
  4
  5import java.io.DataInputStream;
  6import java.io.IOException;
  7import java.util.ArrayList;
  8import java.util.List;
  9import java.util.concurrent.ConcurrentHashMap;
 10import kilim.Constants;
 11import kilim.WeavingClassLoader;
 12
 13import org.objectweb.asm.AnnotationVisitor;
 14import org.objectweb.asm.Attribute;
 15import org.objectweb.asm.ClassReader;
 16import org.objectweb.asm.ClassVisitor;
 17import org.objectweb.asm.FieldVisitor;
 18import org.objectweb.asm.MethodVisitor;
 19import org.objectweb.asm.Opcodes;
 20
 21
 22/**
 23 * CachedClassMirrors caches information about a set of classes that are loaded through byte arrays, and which 
 24 * are not already loaded by the classloader
 25 **/
 26
 27public class CachedClassMirrors {
 28    final static String[] EMPTY_SET = new String[0];
 29    
 30    ConcurrentHashMap<String,ClassMirror> cachedClasses = new ConcurrentHashMap<String, ClassMirror>();
 31    final ClassLoader source;
 32
 33    public CachedClassMirrors() {
 34        source = getClass().getClassLoader();
 35    }
 36    public CachedClassMirrors(ClassLoader $source) {
 37        source = $source;
 38    }
 39    
 40    public ClassMirror classForName(String className) throws ClassMirrorNotFoundException {
 41        // defer to loaded class objects first, then to cached class mirrors.
 42        ClassMirror ret = cachedClasses.get(className);
 43        if (ret != null) return ret;
 44
 45        // even if a class is loaded, we can't tell if it's resolved, so querying it might trigger
 46        // loading of other classes, so use asm for everything
 47        
 48        byte [] code = WeavingClassLoader.findCode(source,className);
 49        if (code != null) return mirror(code);
 50        
 51        throw new ClassMirrorNotFoundException(className);
 52    }
 53
 54    public ClassMirror mirror(byte[] bytecode) {
 55        ClassMirror mirror = new ClassMirror(bytecode);
 56        return place(mirror);
 57    }
 58
 59    private ClassMirror place(ClassMirror r1) {
 60        r1.mirrors = this;
 61        ClassMirror r2 = cachedClasses.putIfAbsent(r1.getName(),r1);
 62        return r2==null ? r1:r2;
 63    }
 64
 65    /** get the major version of klass by loading the bytecode from source, unused but useful for debugging */
 66    public static int getVersion(ClassLoader source,Class klass) {
 67        String cname = WeavingClassLoader.makeResourceName(klass.getName());
 68        DataInputStream in = new DataInputStream(source.getResourceAsStream(cname));
 69        try {
 70            int magic = in.readInt();
 71            int minor = in.readUnsignedShort();
 72            int major = in.readUnsignedShort();
 73            in.close();
 74            return major;
 75        }
 76        catch (IOException ex) { throw new RuntimeException(ex); }
 77    }
 78    
 79    public ClassMirror mirror(Class<?> clazz) {
 80        try {
 81            return classForName(clazz.getName());
 82        }
 83        catch (ClassMirrorNotFoundException ex) {
 84            throw new AssertionError("class-based lookup should never fail",ex);
 85        }
 86    }
 87    
 88    private static String map(String word) {
 89        return word==null ? null : word.replace("/",".");
 90    }
 91    private static String [] map(String [] words) {
 92        if (words==null) return words;
 93        String [] mod = new String[words.length];
 94        for (int ii = 0; ii < mod.length; ii++) mod[ii] = words[ii].replace("/",".");
 95        return mod;
 96    }
 97
 98    
 99    public static class ClassMirror {
100        private String name;
101        private boolean isInterface;
102        private MethodMirror[] declaredMethods;
103        private String[] interfaceNames;
104        private String superName;
105        private int version = 0;
106        CachedClassMirrors mirrors;
107
108        private List<MethodMirror> tmpMethodList; //used only while processing bytecode. 
109
110        public ClassMirror(byte []bytecode) {
111            ClassReader cr = new ClassReader(bytecode);
112            Visitor visitor = new Visitor();
113            cr.accept(visitor, /*flags*/0);
114        }
115
116        // used by DualMirror (external package) for testing the mirrors
117        ClassMirror() {}
118
119        public String getName() {
120            return name;
121        }
122
123        public boolean isInterface() {
124            return isInterface;
125        }
126
127        public boolean equals(Object obj) {
128            if (obj instanceof ClassMirror) {
129                ClassMirror mirr = (ClassMirror) obj;
130                String n1 = name, n2 = mirr.getName();
131                return n1.equals(n2) && mirr.isInterface() == this.isInterface;
132            }
133            return false;
134        }
135
136        public int hashCode() {
137            return this.name.hashCode();
138        }
139
140        public MethodMirror[] getDeclaredMethods() {
141            if (declaredMethods != null)
142                return declaredMethods;
143            return declaredMethods = new MethodMirror[0];
144        }
145
146        public String[] getInterfaces() throws ClassMirrorNotFoundException {
147            return interfaceNames;
148        }
149
150        public String getSuperclass() throws ClassMirrorNotFoundException {
151            return superName;
152        }
153
154
155
156        public int version() {
157            return (version & 0x00FF);
158        }
159
160        public boolean isAssignableFrom(ClassMirror c) throws ClassMirrorNotFoundException {
161            if (c==null) return false;
162            if (this.equals(c)) return true;
163
164            String sname = c.getSuperclass();
165            ClassMirror supcl = sname==null ? null : mirrors.classForName(sname);
166            if (isAssignableFrom(supcl)) return true;
167            for (String icl: c.getInterfaces()) {
168                supcl = mirrors.classForName(icl);
169                if (isAssignableFrom(supcl))
170                    return true;
171            }
172            return false;
173        }
174
175        public class Visitor extends ClassVisitor {
176            Visitor() {
177                super(Constants.KILIM_ASM);
178            }
179
180            // ClassVisitor implementation
181            public void visit(int $version, int access, String $name, String signature, String $superName,
182                    String[] $interfaces) {
183                version = $version;
184                name = map($name);
185                superName = map($superName);
186                interfaceNames = $interfaces == null ? CachedClassMirrors.EMPTY_SET : map($interfaces);
187                isInterface = (access & Opcodes.ACC_INTERFACE) > 0;
188                if (isInterface) superName = null;
189            }
190
191
192
193            public MethodVisitor visitMethod(int access, String name, String desc, String signature,
194                    String[] exceptions) {
195                if (name.equals("<init>")) return null;
196                if (name.equals("<clinit>")) return null;
197                if (tmpMethodList == null) {
198                    tmpMethodList = new ArrayList<CachedClassMirrors.MethodMirror>();
199                }
200                MethodMirror mirror = new MethodMirror(access, name, desc, map(exceptions));
201                tmpMethodList.add(mirror);
202                return null; // null MethodVisitor to avoid examining the instructions.
203            }
204
205            public void visitEnd() {
206                if (tmpMethodList != null) {
207                    declaredMethods = new MethodMirror[tmpMethodList.size()];
208                    int i = 0;
209                    for (MethodMirror mm: tmpMethodList) {
210                        declaredMethods[i++] = mm;
211                    }
212                    tmpMethodList = null;
213                }
214            }
215
216            // Dummy methods
217
218            public void visitSource(String source, String debug) {}
219
220            public void visitNestMember(String nestMember) {}
221            public void visitNestHost(String nestHost) {}
222            
223            public void visitOuterClass(String owner, String name, String desc) {}
224            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
225                return DummyAnnotationVisitor.singleton;
226            }
227            public void visitAttribute(Attribute attr) {}
228            public void visitInnerClass(String name, String outerName, String innerName, int access) {}
229            public FieldVisitor visitField(int access, String name, String desc, String signature,
230                    Object value) {
231                return null;
232            }
233        }
234    }
235    static class DummyAnnotationVisitor extends AnnotationVisitor {
236        public DummyAnnotationVisitor() {
237            super(Constants.KILIM_ASM);
238        }
239        static DummyAnnotationVisitor singleton = new DummyAnnotationVisitor();
240        public void visit(String name, Object value) {}
241        public AnnotationVisitor visitAnnotation(String name, String desc) {return this;}
242        public AnnotationVisitor visitArray(String name) {return DummyAnnotationVisitor.singleton;}
243        public void visitEnd() {}
244        public void visitEnum(String name, String desc, String value) {}
245    }
246
247
248    public static class MethodMirror {
249
250        private String[] exceptions;
251        private String desc;
252        private String name;
253        private int    modifiers;
254        private boolean isBridge;
255
256        public MethodMirror(int modifiers, String name, String desc, String[] exceptions) {
257            this.modifiers = modifiers;
258            this.name = name;
259            this.desc = desc;
260            this.exceptions = (exceptions == null) ? CachedClassMirrors.EMPTY_SET : exceptions;
261            isBridge = (modifiers & Opcodes.ACC_BRIDGE) > 0;
262        }
263
264        public String getName() {
265            return name;
266        }
267
268        public String[] getExceptionTypes() {
269            return exceptions;
270        }
271
272        public String getMethodDescriptor() {
273            return desc;
274        }
275
276        public boolean isBridge() {
277            return isBridge;
278        }
279
280        public int getModifiers() {
281            return modifiers;
282        }
283    }
284}
285
286
287