1
2
3
4 package joeq.Class;
5
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.Enumeration;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.Iterator;
12 import java.util.LinkedHashSet;
13 import java.util.List;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.zip.ZipEntry;
17 import java.util.zip.ZipFile;
18 import java.io.DataInput;
19 import java.io.DataInputStream;
20 import java.io.File;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.PrintStream;
26 import joeq.ClassLib.ClassLibInterface;
27 import joeq.Main.jq;
28 import joeq.UTF.Utf8;
29 import jwutil.collections.AppendIterator;
30 import jwutil.collections.Filter;
31 import jwutil.collections.FilterIterator;
32 import jwutil.collections.UnmodifiableIterator;
33 import jwutil.util.Assert;
34
35 /***
36 * PrimordialClassLoader
37 *
38 * @author John Whaley <jwhaley@alum.mit.edu>
39 * @version $Id: PrimordialClassLoader.java 2192 2005-02-18 06:07:06Z livshits $
40 */
41 public class PrimordialClassLoader extends ClassLoader implements jq_ClassFileConstants {
42
43 public static
44 public static final PrintStream out = System.out;
45
46 abstract static class ClasspathElement {
47 /*** Open a stream to read the given resource, or return
48 * <code>null</code> if resource cannot be found. */
49 abstract InputStream getResourceAsStream(String resourcename);
50 abstract boolean containsResource(String name);
51 /*** Iterate over all classes in the given package. */
52 Iterator listPackage(String packagename) { return listPackage(packagename, false); }
53 abstract Iterator listPackage(String packagename, boolean recursive);
54 abstract Iterator listPackages();
55 }
56 /*** A .zip or .jar file in the CLASSPATH. */
57 static class ZipFileElement extends ClasspathElement {
58 ZipFile zf;
59 Map entries;
60 ZipFileElement(ZipFile zf) {
61 this.zf = zf;
62 }
63 void initializeEntryMap() {
64 int size = zf.size();
65 entries = new HashMap(size + (size >> 1));
66 if (size > 0) {
67 for (Enumeration e = zf.entries(); e.hasMoreElements(); ) {
68 ZipEntry ze = (ZipEntry) e.nextElement();
69 entries.put(ze.getName(), ze);
70 }
71 }
72 if (TRACE) out.println(this+" contains: "+entries.keySet());
73 }
74 public String toString() { return zf.getName(); }
75 InputStream getResourceAsStream(String name) {
76 if (TRACE) out.println("Getting resource for "+name+" in zip file "+zf.getName());
77 if (entries == null) initializeEntryMap();
78 if (name.charAt(0) == '/') name = name.substring(1);
79 ZipEntry ze = (ZipEntry) entries.get(name);
80 try {
81 return (ze==null)?null:zf.getInputStream(ze);
82 } catch (IOException e) { return null; }
83 }
84 boolean containsResource(String name) {
85 if (TRACE) out.println("Searching for "+name+" in zip file "+zf.getName());
86 if (entries == null) initializeEntryMap();
87 return entries.containsKey(name);
88 }
89 Iterator listPackage(final String pathname, final boolean recursive) {
90 if (TRACE) out.println("Listing package "+pathname+" of zip file "+zf.getName());
91
92 if (entries == null) initializeEntryMap();
93 final String filesep = "/";
94 return new FilterIterator(entries.values().iterator(),
95 new Filter() {
96 public boolean isElement(Object o) {
97 ZipEntry zze = (ZipEntry) o;
98 String name = zze.getName();
99 if (TRACE) out.println("Checking if zipentry "+name+" is in package "+pathname);
100 return (!zze.isDirectory()) && name.startsWith(pathname) &&
101 name.endsWith(".class") &&
102 (recursive || name.lastIndexOf(filesep)==(pathname.length()-1));
103 }
104 public Object map(Object o) {
105 return ((ZipEntry)o).getName();
106 }
107 });
108 }
109 Iterator listPackages() {
110 if (TRACE) out.println("Listing packages of zip file "+zf.getName());
111 if (entries == null) initializeEntryMap();
112 LinkedHashSet result = new LinkedHashSet();
113 for (Iterator i=entries.values().iterator(); i.hasNext(); ) {
114 ZipEntry zze = (ZipEntry) i.next();
115 if (zze.isDirectory()) continue;
116 String name = zze.getName();
117 if (name.endsWith(".class")) {
118 int index = name.lastIndexOf('/');
119 result.add(name.substring(0, index+1));
120 }
121 }
122 if (TRACE) out.println("Result: "+result);
123 return result.iterator();
124 }
125 /*** Close the zipfile when this object is garbage-collected. */
126 protected void finalize() throws Throwable {
127
128 try { if (zf!=null) zf.close(); } finally { super.finalize(); }
129 }
130 }
131
132
133 public static final String pathsep = System.getProperty("path.separator");
134 public static final String filesep = System.getProperty("file.separator");
135 /*** A regular path string in the CLASSPATH. */
136 static class PathElement extends ClasspathElement {
137 String path;
138 Set entries;
139
140 PathElement(String path) {
141 this.path = path;
142 }
143
144 void initializeEntryMap() {
145 this.entries = new HashSet();
146 buildEntries(null);
147 if (TRACE) out.println(this+" contains: "+entries);
148 }
149
150 public String toString() { return path; }
151
152 InputStream getResourceAsStream(String name) {
153 if (TRACE) out.println("Getting resource for "+name+" in path "+path);
154 if (entries == null) initializeEntryMap();
155 if (name.charAt(0) == '/') name = name.substring(1);
156 if (!entries.contains(name))
157 return null;
158 if (filesep.charAt(0) != '/') name = name.replace('/', filesep.charAt(0));
159 try {
160 File f = new File(path, name);
161 return new FileInputStream(f);
162 } catch (FileNotFoundException e) {
163 return null;
164 }
165 }
166
167 boolean containsResource(String name) {
168 if (TRACE) out.println("Searching for "+name+" in path "+path);
169 if (entries == null) initializeEntryMap();
170 return entries.contains(name);
171 }
172
173 Iterator listPackage(final String pathn, final boolean recursive) {
174 if (TRACE) out.println("Listing package "+pathn+" in path "+path);
175 if (entries == null) initializeEntryMap();
176 final String filesep = "/";
177 return new FilterIterator(entries.iterator(),
178 new Filter() {
179 public boolean isElement(Object o) {
180 String name = (String) o;
181 if (TRACE) out.println("Checking if file "+name+" is in package "+pathn);
182 return name.startsWith(pathn) &&
183 name.endsWith(".class") &&
184 (recursive || name.lastIndexOf(filesep)==(pathn.length()-1));
185 }
186 });
187 }
188
189 Iterator listPackages() {
190 if (TRACE) out.println("Listing packages of path "+path);
191 HashSet hs = new HashSet();
192 listPackages(null, hs);
193 return hs.iterator();
194 }
195
196 private void listPackages(final String dir, final HashSet pkgs) {
197 final File f = dir == null ? new File(path) : new File(path, dir);
198 if (!f.exists() || !f.isDirectory()) return;
199
200 String [] subdirs = f.list(new java.io.FilenameFilter() {
201 public boolean accept(File _dir, String name) {
202 if (dir != null && name.endsWith(".class"))
203 pkgs.add(dir);
204 return new File(_dir, name).isDirectory();
205 }
206 });
207 for (int i = 0; i < subdirs.length; i++) {
208 String dn = (String)subdirs[i];
209 if (dir != null)
210 dn = dir + filesep + dn;
211 listPackages(dn, pkgs);
212 }
213 }
214
215 private void buildEntries(final String pathn) {
216 File f;
217 if (pathn == null) {
218 f = new File(path);
219 } else if (filesep.charAt(0) == '/') {
220 f = new File(path, pathn);
221 } else {
222 f = new File(path, pathn.replace('/', filesep.charAt(0)));
223 }
224 if (!f.exists() || !f.isDirectory()) return;
225 String[] cls = f.list(new java.io.FilenameFilter() {
226 public boolean accept(File _dir, String name) {
227 return !new File(_dir, name).isDirectory();
228 }
229 });
230
231 if (cls != null) {
232 for (int i = 0; i < cls.length; ++i) {
233 String s = (pathn==null)?(cls[i]):(pathn+cls[i]);
234 entries.add(s);
235 }
236 }
237
238 String [] subdirs = f.list(new java.io.FilenameFilter() {
239 public boolean accept(File _dir, String name) {
240 return new File(_dir, name).isDirectory();
241 }
242 });
243 if (subdirs != null) {
244 for (int i = 0; i < subdirs.length; i++) {
245 String dn = (String)subdirs[i];
246 if (pathn != null) dn = pathn + dn;
247 buildEntries(dn + '/');
248 }
249 }
250 }
251 }
252
253 /*** Vector of ClasspathElements corresponding to CLASSPATH entries. */
254 public void addToClasspath(String s) {
255
256 Set duplicates = new HashSet();
257 duplicates.addAll(classpathList);
258 for (Iterator it = classpaths(s); it.hasNext(); ) {
259 String path = (String) it.next();
260 if (duplicates.contains(path)) continue;
261 else duplicates.add(path);
262 if (path.toLowerCase().endsWith(".zip") ||
263 path.toLowerCase().endsWith(".jar"))
264 try {
265 if (TRACE) out.println("Adding zip file "+path+" to classpath");
266 classpathList.add(new ZipFileElement(new ZipFile(path)));
267 } catch (IOException ex) {
268 else {
269 if (TRACE) out.println("Adding path "+path+" to classpath");
270 classpathList.add(new PathElement(path));
271 }
272 }
273 ((ArrayList) classpathList).trimToSize();
274 }
275
276 /*** Iterate over the components of the system CLASSPATH.
277 * Each element is a <code>String</code> naming one segment of the
278 * CLASSPATH. */
279 public static final Iterator classpaths(String classpath) {
280
281
282 if (!classpath.startsWith(pathsep)) classpath = pathsep + classpath;
283 if (!classpath.endsWith(pathsep)) classpath = classpath + pathsep;
284 final String cp = classpath;
285
286 return new UnmodifiableIterator() {
287 int i=0;
288 public boolean hasNext() {
289 return (cp.length() > (i+pathsep.length()));
290 }
291 public Object next() {
292 i+=pathsep.length();
293 String path = cp.substring(i, cp.indexOf(pathsep, i));
294 i+=path.length();
295 return path;
296 }
297 };
298 }
299
300 public Iterator listPackage(final String pathname) {
301 return listPackage(pathname, false);
302 }
303
304 public Iterator listPackage(final String pathname, boolean recursive) {
305 Iterator result = null;
306 for (Iterator it = classpathList.iterator(); it.hasNext(); ) {
307 ClasspathElement cpe = (ClasspathElement)it.next();
308 Iterator lp = cpe.listPackage(pathname, recursive);
309 if (!lp.hasNext()) continue;
310 result = result==null?lp:new AppendIterator(lp, result);
311 }
312 if (result == null) return Collections.EMPTY_SET.iterator();
313 return result;
314 }
315
316 public Iterator listPackages() {
317 Iterator result = null;
318 for (Iterator it = classpathList.iterator(); it.hasNext(); ) {
319 ClasspathElement cpe = (ClasspathElement)it.next();
320 Iterator lp = cpe.listPackages();
321 if (!lp.hasNext()) continue;
322 result = result==null?lp:new AppendIterator(lp, result);
323 }
324 if (result == null) return Collections.EMPTY_SET.iterator();
325 return result;
326 }
327
328 public String classpathToString() {
329 StringBuffer result = new StringBuffer(pathsep);
330 for (Iterator it = classpathList.iterator(); it.hasNext(); ) {
331 ClasspathElement cpe = (ClasspathElement) it.next();
332 result.append(cpe.toString());
333 result.append(pathsep);
334 }
335 return result.toString();
336 }
337
338 public static String descriptorToResource(String desc) {
339 Assert._assert(desc.charAt(0)==TC_CLASS);
340 Assert._assert(desc.charAt(desc.length()-1)==TC_CLASSEND);
341 Assert._assert(desc.indexOf('.')==-1);
342 return desc.substring(1, desc.length()-1) + ".class";
343 }
344
345 /*** Translate a class name into a corresponding resource name.
346 * @param classname The class name to translate.
347 */
348 public static String classnameToResource(String classname) {
349 Assert._assert(classname.indexOf('/')==-1);
350
351 return classname.replace('.', filesep.charAt(0)) + ".class";
352 }
353
354 public String getResourcePath(String name) {
355 for (Iterator it = classpathList.iterator(); it.hasNext(); ) {
356 ClasspathElement cpe = (ClasspathElement) it.next();
357 if (cpe.containsResource(name))
358 return cpe.toString();
359 }
360
361 return null;
362 }
363
364 public String getPackagePath(String name) {
365 for (Iterator it = classpathList.iterator(); it.hasNext(); ) {
366 ClasspathElement cpe = (ClasspathElement) it.next();
367 for (Iterator it2 = cpe.listPackages(); it2.hasNext(); ) {
368 if (name.equals(it2.next()))
369 return cpe.toString();
370 }
371 }
372
373 return null;
374 }
375
376 /*** Open an <code>InputStream</code> on a resource found somewhere
377 * in the CLASSPATH.
378 * @param name The filename of the resource to locate.
379 */
380 public InputStream getResourceAsStream(String name) {
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484 bs_desc2type;
485 private jq_Type[] allTypes; private int numTypes;
486 private final List
487
488 public jq_Type[] getAllTypes() {
489 return allTypes;
490 }
491
492 public int getNumTypes() {
493 return numTypes;
494 }
495
496 public final Set
497 HashSet s = new HashSet();
498 for (int i = 0; i < numTypes; ++i) {
499 jq_Type t = allTypes[i];
500 if (t instanceof jq_Class) {
501 jq_Class k = (jq_Class) t;
502 if (k.doesConstantPoolContain(m))
503 s.add(k);
504 }
505 }
506 return s;
507 }
508
509 public final jq_Class getOrCreateClass(String desc, DataInput in) {
510 jq_Class t = (jq_Class)getOrCreateBSType(Utf8.get(desc));
511 t.load(in);
512 return t;
513 }
514
515 public final jq_Type getBSType(String desc) { return getBSType(Utf8.get(desc)); }
516 public final jq_Type getBSType(Utf8 desc) {
517 return (jq_Type)bs_desc2type.get(desc);
518 }
519 public final jq_Type getOrCreateBSType(String desc) { return getOrCreateBSType(Utf8.get(desc)); }
520 public final jq_Type getOrCreateBSType(Utf8 desc) {
521 if (jq.RunningNative)
522 return ClassLibInterface.DEFAULT.getOrCreateType(this, desc);
523 jq_Type t = (jq_Type)bs_desc2type.get(desc);
524 if (t == null) {
525 if (desc.isDescriptor(jq_ClassFileConstants.TC_CLASS)) {
526
527 if (TRACE) out.println("Adding class type "+desc);
528 t = jq_Class.newClass(this, desc);
529 } else if (desc.isDescriptor(jq_ClassFileConstants.TC_ARRAY)) {
530 if (TRACE) out.println("Adding array type "+desc);
531 Utf8 elementDesc = desc.getArrayElementDescriptor();
532 jq_Type elementType = getOrCreateBSType(elementDesc);
533
534 t = jq_Array.newArray(desc, this, elementType);
535 } else {
536
537 if (desc == Utf8.BYTE_DESC)
538 t = jq_Primitive.newPrimitive(desc, "byte", 1);
539 else if (desc == Utf8.CHAR_DESC)
540 t = jq_Primitive.newPrimitive(desc, "char", 2);
541 else if (desc == Utf8.DOUBLE_DESC)
542 t = jq_Primitive.newPrimitive(desc, "double", 8);
543 else if (desc == Utf8.FLOAT_DESC)
544 t = jq_Primitive.newPrimitive(desc, "float", 4);
545 else if (desc == Utf8.INT_DESC)
546 t = jq_Primitive.newPrimitive(desc, "int", 4);
547 else if (desc == Utf8.LONG_DESC)
548 t = jq_Primitive.newPrimitive(desc, "long", 8);
549 else if (desc == Utf8.SHORT_DESC)
550 t = jq_Primitive.newPrimitive(desc, "short", 2);
551 else if (desc == Utf8.BOOLEAN_DESC)
552 t = jq_Primitive.newPrimitive(desc, "boolean", 1);
553 else if (desc == Utf8.VOID_DESC)
554 t = jq_Primitive.newPrimitive(desc, "void", 0);
555
556
557
558
559
560
561
562
563
564
565 else Assert.UNREACHABLE("bad descriptor! "+desc);
566 }
567 put_desc2type(desc, t);
568 }
569 return t;
570 }
571
572
573
574
575
576 public final void replaceClass(String cName)
577 {
578 Utf8 oldDesc = Utf8.get("L"+cName.replace('.', '/')+";") ;
579 jq_Type old = PrimordialClassLoader.getOrCreateType(this, oldDesc);
580 Assert._assert(old != null);
581 Assert._assert(oldDesc.isDescriptor(jq_ClassFileConstants.TC_CLASS));
582
583
584 Utf8 newDesc = Utf8.get("LREPLACE"+cName.replace('.', '/')+";") ;
585 jq_Class new_c = jq_Class.newClass(this, newDesc);
586 put_desc2type(newDesc, new_c);
587
588
589 DataInputStream in = null;
590 try {
591 in = getClassFileStream(oldDesc);
592 if (in == null) throw new NoClassDefFoundError(jq_Class.className(oldDesc));
593 new_c.load(in);
594 } catch (IOException x) {
595 x.printStackTrace();
596 throw new ClassFormatError(x.toString());
597 } finally {
598 try { if (in != null) in.close(); } catch (IOException _) { }
599 }
600 }
601
602 public void unloadBSType(jq_Type t) {
603 bs_desc2type.remove(t.getDesc());
604 for (int i = 0; ; ++i) {
605 if (allTypes[i] == t) {
606 numTypes--;
607 System.arraycopy(allTypes, i+1, allTypes, i, numTypes - i);
608 allTypes[numTypes] = null;
609 break;
610 }
611 }
612 }
613
614 public static final jq_Type getOrCreateType(ClassLoader cl, Utf8 desc) {
615 if (jq.RunningNative)
616 return ClassLibInterface.DEFAULT.getOrCreateType(cl, desc);
617 Assert._assert(cl == PrimordialClassLoader.loader);
618 return PrimordialClassLoader.loader.getOrCreateBSType(desc);
619 }
620
621 public static final void unloadType(ClassLoader cl, jq_Type t) {
622 if (jq.RunningNative) {
623 ClassLibInterface.DEFAULT.unloadType(cl, t);
624 return;
625 }
626 Assert._assert(cl == PrimordialClassLoader.loader);
627 PrimordialClassLoader.loader.unloadBSType(t);
628 }
629 }