1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm;
22
23
import java.io.ByteArrayInputStream;
24
import java.io.ByteArrayOutputStream;
25
import java.io.IOException;
26
import java.io.InputStream;
27
import java.io.Writer;
28
import java.net.MalformedURLException;
29
import java.net.URL;
30
import java.nio.ByteBuffer;
31
import java.util.ArrayList;
32
import java.util.Collection;
33
import java.util.HashSet;
34
import java.util.List;
35
import java.util.Map;
36
import java.util.TreeMap;
37
38
import org.jnode.assembler.ObjectResolver;
39
import org.jnode.util.BootableArrayList;
40
import org.jnode.util.ByteBufferInputStream;
41
import org.jnode.annotation.PrivilegedActionPragma;
42
import org.jnode.vm.classmgr.ClassDecoder;
43
import org.jnode.vm.classmgr.IMTBuilder;
44
import org.jnode.vm.classmgr.SelectorMap;
45
import org.jnode.vm.classmgr.VmIsolatedStatics;
46
import org.jnode.vm.classmgr.VmMethod;
47
import org.jnode.vm.classmgr.VmSharedStatics;
48
import org.jnode.vm.classmgr.VmType;
49
import org.jnode.vm.compiler.CompiledIMT;
50
import org.jnode.vm.compiler.IMTCompiler;
51
import org.jnode.vm.compiler.NativeCodeCompiler;
52
import org.jnode.vm.isolate.VmIsolate;
53
import org.jnode.vm.scheduler.VmProcessor;
54
55
/**
56
 * Default classloader.
57
 *
58
 * @author Ewout Prangsma (epr@users.sourceforge.net)
59
 */
60
public final class VmSystemClassLoader extends VmAbstractClassLoader {
61
62
    private transient TreeMap<String, ClassInfo> classInfos;
63
64
    private VmType[] bootClasses;
65
66
    private transient URL[] classesURL;
67
68
    private static transient boolean verbose = false;
69
70
    private transient boolean failOnNewLoad = false;
71
72
    private transient ClassLoader classLoader;
73
74
    private transient ObjectResolver resolver;
75
76
    private Map<String, byte[]> systemRtJar;
77
78
    // private static JarFile systemJarFile;
79
80
    private final ClassLoader parent;
81
82
    /**
83
     * Our mapping from method signatures to selectors
84
     */
85
    private final SelectorMap selectorMap;
86
87
    private final VmArchitecture arch;
88
89
    private boolean requiresCompile = false;
90
91
    private final VmSharedStatics sharedStatics;
92
93
    private transient VmIsolatedStatics isolatedStatics;
94
95
    private transient HashSet<String> failedClassNames;
96
97
    private List<ResourceLoader> resourceLoaders;
98
99
    /**
100
     * Constructor for VmClassLoader.
101
     *
102
     * @param classesURL
103
     * @param arch
104
     */
105
    public VmSystemClassLoader(URL classesURL, VmArchitecture arch) {
106
        this(new URL[]{classesURL}, arch, null);
107
    }
108
109
    /**
110
     * Constructor for VmClassLoader.
111
     *
112
     * @param classesURL
113
     * @param arch
114
     */
115
    public VmSystemClassLoader(URL[] classesURL, VmArchitecture arch) {
116
        this(classesURL, arch, null);
117
    }
118
119
    /**
120
     * Constructor for VmClassLoader.
121
     *
122
     * @param classesURL
123
     * @param arch
124
     * @param resolver
125
     */
126
    public VmSystemClassLoader(URL[] classesURL, VmArchitecture arch,
127
                               ObjectResolver resolver) {
128
        this.classesURL = classesURL;
129
        this.classInfos = new TreeMap<String, ClassInfo>();
130
        this.parent = null;
131
        this.selectorMap = new SelectorMap();
132
        this.arch = arch;
133
        this.resolver = resolver;
134
        this.resourceLoaders = new BootableArrayList<ResourceLoader>();
135
        this.sharedStatics = new VmSharedStatics(arch, resolver);
136
        this.isolatedStatics = new VmIsolatedStatics(arch, resolver);
137
    }
138
139
    /**
140
     * Constructor for VmClassLoader.
141
     *
142
     * @param classLoader
143
     */
144
    public VmSystemClassLoader(ClassLoader classLoader) {
145
        this.classLoader = classLoader;
146
        this.classInfos = new TreeMap<String, ClassInfo>();
147
        this.parent = classLoader.getParent();
148
        final VmSystemClassLoader sysCl = VmSystem.getSystemClassLoader();
149
        this.selectorMap = sysCl.selectorMap;
150
        this.arch = sysCl.arch;
151
        this.sharedStatics = sysCl.sharedStatics;
152
    }
153
154
    /**
155
     * Gets the collection with all currently loaded classes. All collection
156
     * elements are instanceof VmClass.
157
     *
158
     * @return Collection
159
     */
160
    public Collection<VmType> getLoadedClasses() {
161
        if (classInfos != null) {
162
            final ArrayList<VmType> list = new ArrayList<VmType>();
163
            for (ClassInfo ci : classInfos.values()) {
164
                if (ci.isLoaded()) {
165
                    try {
166
                        list.add(ci.getVmClass());
167
                    } catch (ClassNotFoundException ex) {
168
                        /* ignore */
169
                    }
170
                }
171
            }
172
            return list;
173
        } else {
174
            final ArrayList<VmType> list = new ArrayList<VmType>();
175
            final VmType[] arr = bootClasses;
176
            final int count = arr.length;
177
            for (int i = 0; i < count; i++) {
178
                list.add(arr[i]);
179
            }
180
            return list;
181
        }
182
    }
183
184
    /**
185
     * Gets the number of loaded classes.
186
     *
187
     * @return the number of loaded classes
188
     */
189
    public int getLoadedClassCount() {
190
        if (classInfos != null) {
191
            return classInfos.size();
192
        } else {
193
            return bootClasses.length;
194
        }
195
    }
196
197
    /**
198
     * Gets the loaded class with a given name, or null if no such class has
199
     * been loaded.
200
     *
201
     * @param name
202
     * @return VmClass
203
     */
204
    public VmType<?> findLoadedClass(String name) {
205
        if (classInfos != null) {
206
            if (name.indexOf('/') >= 0) {
207
                //throw new IllegalArgumentException("name contains '/'");
208
                //return null here
209
                return null;
210
            }
211
            final ClassInfo ci = getClassInfo(name, false);
212
            if (ci != null) {
213
                try {
214
                    return ci.getVmClass();
215
                } catch (ClassNotFoundException ex) {
216
                    // throw new RuntimeException(ex);
217
                    return null;
218
                }
219
            } else {
220
                return null;
221
            }
222
        } else {
223
            final VmType[] list = bootClasses;
224
            final int count = list.length;
225
            for (int i = 0; i < count; i++) {
226
                VmType vmClass = list[i];
227
                if (vmClass.nameEquals(name)) {
228
                    return vmClass;
229
                }
230
            }
231
            return null;
232
        }
233
    }
234
235
    /**
236
     * Result all loaded classes as an array of VmClass entries.
237
     *
238
     * @return VmClass[]
239
     * @throws ClassNotFoundException
240
     */
241
    public VmType[] prepareAfterBootstrap() throws ClassNotFoundException {
242
        if (this.classInfos != null) {
243
            final VmType[] result = new VmType[classInfos.size()];
244
            int j = 0;
245
            for (ClassInfo ci : classInfos.values()) {
246
                result[j++] = ci.getVmClass();
247
            }
248
            bootClasses = result;
249
            return result;
250
        } else {
251
            return bootClasses;
252
        }
253
    }
254
255
    /**
256
     * Add a class that has been loaded.
257
     *
258
     * @param name
259
     * @param cls
260
     */
261
    public synchronized void addLoadedClass(String name, VmType cls) {
262
        if (failOnNewLoad) {
263
            throw new RuntimeException("Cannot load a new class when failOnNewLoad is set (" + name + ")");
264
        }
265
        if (classInfos != null) {
266
            classInfos.put(name, new ClassInfo(cls));
267
        }
268
    }
269
270
    /**
271
     * Gets the ClassInfo for the given name. If not found and create is True, a
272
     * new ClassInfo is created, added to the list and returned. If not found
273
     * and create is False, null is returned.
274
     *
275
     * @param name
276
     * @param create
277
     * @return the ClassInfo for the given name
278
     */
279
    private synchronized ClassInfo getClassInfo(String name, boolean create) {
280
        if (name == null) {
281
            Unsafe.debug(" getClassInfo(null)!! ");
282
        }
283
        ClassInfo ci = classInfos.get(name);
284
        if (ci != null) {
285
            return ci;
286
        } else if (create) {
287
            ci = new ClassInfo(name);
288
            classInfos.put(name, ci);
289
        }
290
        return ci;
291
    }
292
293
    /**
294
     * Load a class with a given name
295
     *
296
     * @param name
297
     * @param resolve
298
     * @return The loaded class
299
     * @throws ClassNotFoundException
300
     * @see org.jnode.vm.classmgr.VmClassLoader#loadClass(String, boolean)
301
     */
302
    @PrivilegedActionPragma
303
    public VmType<?> loadClass(String name, boolean resolve)
304
        throws ClassNotFoundException {
305
306
        // Also implement the java.lang.ClassLoader principals here
307
        // otherwise they cannot work in java.lang.ClassLoader.
308
        if ((parent != null) && !parent.skipParentLoader(name)) {
309
            try {
310
                final Class<?> cls = parent.loadClass(name);
311
                return VmType.fromClass((Class<?>) cls);
312
            } catch (ClassNotFoundException ex) {
313
                // Don't care, try it ourselves.
314
            }
315
        }
316
317
        VmType cls = findLoadedClass(name);
318
        if (cls != null) {
319
            return cls;
320
        }
321
        if (classInfos == null) {
322
            // Unsafe.debug("classInfos==null");
323
            throw new ClassNotFoundException(name);
324
        }
325
326
        // BootLog.debug("load class" + name);
327
328
        if (name.indexOf('/') >= 0) {
329
            //throw new IllegalArgumentException("name contains '/'");
330
            //throw CNFE here
331
            throw new ClassNotFoundException(name);
332
        }
333
334
        if ((failedClassNames != null) && (failedClassNames.contains(name))) {
335
            throw new ClassNotFoundException(name);
336
        }
337
338
        final ClassInfo ci = getClassInfo(name, true);
339
340
        if (!ci.isLoaded()) {
341
            try {
342
                if (name.charAt(0) == '[') {
343
                    ci.setVmClass(loadArrayClass(name, resolve));
344
                } else {
345
                    ci.setVmClass(loadNormalClass(name));
346
                }
347
                if (failOnNewLoad) {
348
                    throw new RuntimeException("Cannot load a new class when failOnNewLoad is set (" + name + ")");
349
                }
350
            } catch (ClassNotFoundException ex) {
351
                ci.setLoadError(ex.toString());
352
                classInfos.remove(ci.getName());
353
                addFailedClassName(name);
354
                throw new ClassNotFoundException(name, ex);
355
            } catch (IOException ex) {
356
                ci.setLoadError(ex.toString());
357
                classInfos.remove(ci.getName());
358
                addFailedClassName(name);
359
                throw new ClassNotFoundException(name, ex);
360
            }
361
            if (resolve) {
362
                ci.getVmClass().link();
363
            }
364
        }
365
        return ci.getVmClass();
366
    }
367
368
    private void addFailedClassName(String name) {
369
        if (failedClassNames == null) {
370
            failedClassNames = new HashSet<String>();
371
        }
372
        failedClassNames.add(name);
373
    }
374
375
    /**
376
     * Gets the ClassLoader belonging to this loader.
377
     *
378
     * @return ClassLoader
379
     */
380
    public final ClassLoader asClassLoader() {
381
        if (VmIsolate.isRoot()) {
382
            if (classLoader == null) {
383
                classLoader = new ClassLoaderWrapper(this);
384
            }
385
            return classLoader;
386
        } else {
387
            ClassLoader loader = VmIsolate.currentIsolate().getSystemClassLoader();
388
            if (loader == null) {
389
                loader = new ClassLoaderWrapper(this);
390
                VmIsolate.currentIsolate().setSystemClassLoader(loader);
391
            }
392
            return loader;
393
        }
394
    }
395
396
    /**
397
     * Load a normal (non-array) class with a given name
398
     *
399
     * @param name
400
     * @return VmClass
401
     * @throws IOException
402
     * @throws ClassNotFoundException
403
     */
404
    private VmType loadNormalClass(String name) throws IOException,
405
        ClassNotFoundException {
406
407
        final String archN = arch.getName();
408
        boolean allowNatives = VmUtils.allowNatives(name, archN);
409
410
        final ByteBuffer image = getClassData(name);
411
412
        if (failOnNewLoad) {
413
            throw new RuntimeException("Cannot load a new class when failOnNewLoad is set (" + name + ")");
414
        }
415
416
        return ClassDecoder.defineClass(name, image, !allowNatives, this, null);
417
    }
418
419
    /**
420
     * Gets the number of loaded classes.
421
     *
422
     * @return int
423
     */
424
    public int size() {
425
        if (classInfos != null) {
426
            return classInfos.size();
427
        } else {
428
            return bootClasses.length;
429
        }
430
    }
431
432
    /**
433
     * Gets an inputstream for the class file that contains the given classname.
434
     *
435
     * @param clsName
436
     * @return InputStream
437
     * @throws MalformedURLException
438
     * @throws IOException
439
     * @throws ClassNotFoundException
440
     */
441
    private ByteBuffer getClassData(String clsName) throws IOException, ClassNotFoundException {
442
443
        final String fName = clsName.replace('.', '/') + ".class";
444
445
        if (systemRtJar != null) {
446
            // Try the system RT jar first
447
            final byte[] data = systemRtJar.get(fName);
448
            if (data != null) {
449
                return ByteBuffer.wrap(data);
450
            }
451
452
            for (ResourceLoader l : resourceLoaders) {
453
                final ByteBuffer buf = l.getResourceAsBuffer(fName);
454
                if (buf != null) {
455
                    return buf;
456
                }
457
            }
458
459
            throw new ClassNotFoundException("System class " + clsName
460
                + " not found.");
461
        } else {
462
            final InputStream is = getResourceAsStream(fName);
463
            if (is == null) {
464
                throw new ClassNotFoundException("Class resource of " + clsName
465
                    + " not found.");
466
            } else {
467
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
468
                int len;
469
                byte[] buf = new byte[4096];
470
                while ((len = is.read(buf)) > 0) {
471
                    bos.write(buf, 0, len);
472
                }
473
                is.close();
474
475
                return ByteBuffer.wrap(bos.toByteArray());
476
            }
477
        }
478
    }
479
480
    /**
481
     * @see org.jnode.vm.classmgr.VmClassLoader#resourceExists(java.lang.String)
482
     */
483
    public final boolean resourceExists(String resName) {
484
        try {
485
            for (ResourceLoader l : resourceLoaders) {
486
                if (l.containsResource(resName)) {
487
                    if (verbose) {
488
                        System.out.println("resourceExists(" + resName + ")->true");
489
                    }
490
                    return true;
491
                }
492
            }
493
            final InputStream is = getResourceAsStream(resName);
494
            if (is != null) {
495
                if (verbose) {
496
                    System.out.println("resourceExists(" + resName + "), using getResourceAsStream->true");
497
                }
498
                is.close();
499
                return true;
500
            } else {
501
                if (verbose) {
502
                    System.out.println("resourceExists(" + resName + "), using getResourceAsStream->false");
503
                }
504
                return false;
505
            }
506
        } catch (IOException ex) {
507
            if (verbose) {
508
                ex.printStackTrace();
509
            }
510
            return false;
511
        }
512
    }
513
514
    public URL findResource(String name) {
515
        if (verbose) {
516
            System.out.println("VmSystemClassLoader.findResource(" + name + ")");
517
        }
518
        if (name.startsWith("/")) {
519
            name = name.substring(1);
520
        }
521
        if (classesURL != null) {
522
            if (verbose) {
523
                System.out.println("Loading resource " + name + " from " + classesURL);
524
            }
525
            try {
526
                for (URL u : classesURL) {
527
                    URL url = new URL(u, name);
528
                    try {
529
                        url.openStream().close();
530
                        return url;
531
                    } catch (IOException e) {
532
                        //continue
533
                    }
534
                }
535
                return null;
536
            } catch (MalformedURLException e) {
537
                e.printStackTrace();
538
                return null;
539
            }
540
        } else if (systemRtJar != null) {
541
            if (verbose) {
542
                System.out.println("Loading resource " + name + " from systemRtJar");
543
            }
544
            final byte[] data = systemRtJar.get(name);
545
            if (verbose) {
546
                System.out.println(">>>>>> findResource(" + name + "), (data==null)=" + (data == null));
547
            }
548
549
            if (data != null) {
550
                if (verbose) {
551
                    System.out.println(">>>>>> resource: " + new String(data));
552
                }
553
                return ClassLoader.getSystemResource(name);
554
            } else {
555
                for (ResourceLoader l : resourceLoaders) {
556
                    final URL url = l.getResource(name);
557
                    if (url != null) {
558
                        return url;
559
                    }
560
                }
561
                return null;
562
            }
563
        } else {
564
            if (verbose) {
565
                System.out.println("!!!! findResource(" + name + ") : ERROR");
566
            }
567
            return null;
568
        }
569
    }
570
571
572
    /**
573
     * Gets an inputstream for a resource with the given name.
574
     *
575
     * @param name The name of the resource
576
     * @return An opened inputstream to the resource with the given name, or
577
     *         null if not found.
578
     * @throws MalformedURLException
579
     * @throws IOException
580
     */
581
    public InputStream getResourceAsStream(String name) throws IOException {
582
        if (name.startsWith("/")) {
583
            name = name.substring(1);
584
        }
585
        if (classesURL != null) {
586
            if (verbose) {
587
                System.out.println("Loading resource " + name + " from " + classesURL);
588
            }
589
            for (URL u : classesURL) {
590
                URL url = new URL(u, name);
591
                try {
592
                    return url.openStream();
593
                } catch (IOException e) {
594
                    //continue
595
                }
596
            }
597
            return null;
598
        } else if (systemRtJar != null) {
599
            if (verbose) {
600
                System.out.println("Loading resource " + name + " from systemRtJar");
601
            }
602
            final byte[] data = systemRtJar.get(name);
603
            if (data != null) {
604
                return new ByteArrayInputStream(data);
605
            } else {
606
                for (ResourceLoader l : resourceLoaders) {
607
                    final ByteBuffer buf = l.getResourceAsBuffer(name);
608
                    if (buf != null) {
609
                        return new ByteBufferInputStream(buf);
610
                    }
611
                }
612
                return null;
613
            }
614
        } else {
615
            throw new IOException("Don't no how to load " + name);
616
        }
617
    }
618
619
    /**
620
     * Returns the verbose.
621
     *
622
     * @return boolean
623
     */
624
    public boolean isVerbose() {
625
        return verbose;
626
    }
627
628
    /**
629
     * Sets the verbose.
630
     *
631
     * @param verbose The verbose to set
632
     */
633
    public void setVerbose(boolean verbose) {
634
        this.verbose = verbose;
635
    }
636
637
    /**
638
     * Returns the failOnNewLoad.
639
     *
640
     * @return boolean
641
     */
642
    public boolean isFailOnNewLoad() {
643
        return failOnNewLoad;
644
    }
645
646
    /**
647
     * Sets the failOnNewLoad.
648
     *
649
     * @param failOnNewLoad The failOnNewLoad to set
650
     */
651
    public void setFailOnNewLoad(boolean failOnNewLoad) {
652
        if (classesURL != null) {
653
            this.failOnNewLoad = failOnNewLoad;
654
        }
655
    }
656
657
    /**
658
     * (non-Javadoc)
659
     *
660
     * @see org.jnode.vm.classmgr.VmClassLoader#disassemble(org.jnode.vm.classmgr.VmMethod,
661
     * int, boolean, java.io.Writer)
662
     */
663
    public void disassemble(VmMethod vmMethod, int optLevel,
664
                            boolean enableTestCompilers, Writer writer) {
665
        final NativeCodeCompiler cmps[];
666
        int index;
667
        if (enableTestCompilers) {
668
            index = optLevel;
669
            optLevel += arch.getCompilers().length;
670
            cmps = arch.getTestCompilers();
671
        } else {
672
            index = optLevel;
673
            cmps = arch.getCompilers();
674
        }
675
676
        final NativeCodeCompiler cmp;
677
        if (index < 0) {
678
            index = 0;
679
        } else if (index >= cmps.length) {
680
            index = cmps.length - 1;
681
        }
682
        cmp = cmps[index];
683
        cmp.disassemble(vmMethod, getResolver(), optLevel, writer);
684
    }
685
686
    /**
687
     * Compile the given IMT.
688
     *
689
     * @param builder
690
     * @return the compiled IMT
691
     */
692
    public CompiledIMT compileIMT(IMTBuilder builder) {
693
        final IMTCompiler cmp = arch.getIMTCompiler();
694
        return cmp.compile(resolver, builder.getImt(), builder.getImtCollisions());
695
    }
696
697
    /**
698
     * Initialize this classloader during the initialization of the VM. If
699
     * needed, the tree of classes is generated from the boot class list.
700
     */
701
    protected void initialize() {
702
        if (classInfos == null) {
703
            final TreeMap<String, ClassInfo> classInfos = new TreeMap<String, ClassInfo>();
704
            final VmType[] list = bootClasses;
705
            final int count = list.length;
706
            for (int i = 0; i < count; i++) {
707
                final VmType vmClass = list[i];
708
                final ClassInfo ci = new ClassInfo(vmClass);
709
                classInfos.put(ci.getName(), ci);
710
            }
711
            this.classInfos = classInfos;
712
        }
713
    }
714
715
    /**
716
     * @return ObjectResolver
717
     */
718
    public ObjectResolver getResolver() {
719
        if (resolver == null) {
720
            resolver = new Unsafe.UnsafeObjectResolver();
721
        }
722
        return resolver;
723
    }
724
725
    /**
726
     * Set the object resolver. This can be called only once.
727
     *
728
     * @param resolver
729
     */
730
    public void setResolver(ObjectResolver resolver) {
731
        if (this.resolver == null) {
732
            this.resolver = resolver;
733
        } else {
734
            throw new SecurityException("Cannot overwrite resolver");
735
        }
736
    }
737
738
    /**
739
     * Sets the systemRtJar. The given map must contains the resource names as
740
     * keys of type String, and the actual resources as byte array.
741
     *
742
     * @param resources The systemRtJar to set
743
     */
744
    public void setSystemRtJar(Map<String, byte[]> resources) {
745
        if (this.systemRtJar == null) {
746
            this.systemRtJar = resources;
747
        } else {
748
            throw new SecurityException("Cannot override system RT jar");
749
        }
750
    }
751
752
    /**
753
     * Is this loader the system classloader?
754
     *
755
     * @return boolean
756
     */
757
    public boolean isSystemClassLoader() {
758
        VmSystemClassLoader systemLoader = VmSystem.getSystemClassLoader();
759
        return ((systemLoader == this) || (systemLoader == null));
760
    }
761
762
    static class ClassLoaderWrapper extends ClassLoader {
763
        //TODO maybe it should be declared as 'protected' in ClassLoader ???
764
        private final VmSystemClassLoader vmClassLoader;
765
766
        public ClassLoaderWrapper(VmSystemClassLoader vmClassLoader) {
767
            super(vmClassLoader, 0);
768
            this.vmClassLoader = vmClassLoader;
769
        }
770
771
        protected URL findResource(String name) {
772
            return vmClassLoader.findResource(name);
773
        }
774
    }
775
776
    /**
777
     * Class that holds information of a loading &amp; loaded class.
778
     *
779
     * @author epr
780
     */
781
    static class ClassInfo {
782
783
        /**
784
         * Name of the class
785
         */
786
        private final String name;
787
788
        /**
789
         * The class itself
790
         */
791
        private VmType<?> vmClass;
792
793
        /**
794
         * Classloading got an error?
795
         */
796
        private boolean error = false;
797
798
        private String errorMsg;
799
800
        /**
801
         * Create a new instance
802
         *
803
         * @param name
804
         */
805
        public ClassInfo(String name) {
806
            this.name = name;
807
        }
808
809
        /**
810
         * Create a new instance
811
         *
812
         * @param vmClass
813
         */
814
        public ClassInfo(VmType<?> vmClass) {
815
            this.name = vmClass.getName();
816
            this.vmClass = vmClass;
817
            if (name.indexOf('/') >= 0) {
818
                throw new IllegalArgumentException(
819
                    "vmClass.getName() contains '/'");
820
            }
821
        }
822
823
        /**
824
         * Returns the name of the class
825
         *
826
         * @return the name of the class
827
         */
828
        public final String getName() {
829
            return name;
830
        }
831
832
        /**
833
         * @return Type
834
         * @throws ClassNotFoundException
835
         */
836
        public final synchronized VmType<?> getVmClass()
837
            throws ClassNotFoundException {
838
            while (vmClass == null) {
839
                if (error) {
840
                    throw new ClassNotFoundException(name + "; " + errorMsg);
841
                }
842
                try {
843
                    wait();
844
                } catch (InterruptedException ex) {
845
                    // Just ignore
846
                }
847
            }
848
            return vmClass;
849
        }
850
851
        /**
852
         * @param class1
853
         */
854
        public final synchronized void setVmClass(VmType class1) {
855
            if (this.vmClass == null) {
856
                this.vmClass = class1;
857
                notifyAll();
858
            } else {
859
                throw new SecurityException("Can only set the VmClass once.");
860
            }
861
        }
862
863
        /**
864
         * Signal a class loading error. This will release other threads waiting
865
         * for this class with a ClassNotFoundException.
866
         *
867
         * @param errorMsg
868
         */
869
        public final synchronized void setLoadError(String errorMsg) {
870
            this.error = true;
871
            this.errorMsg = errorMsg;
872
            notifyAll();
873
        }
874
875
        /**
876
         * Has the class wrapped by this object been loaded?
877
         *
878
         * @return if the class wrapped by this object has been loaded
879
         */
880
        public boolean isLoaded() {
881
            return (vmClass != null);
882
        }
883
    }
884
885
    /**
886
     * Gets the mapping between method name&types and selectors.
887
     *
888
     * @return The map
889
     */
890
    public final SelectorMap getSelectorMap() {
891
        return selectorMap;
892
    }
893
894
    /**
895
     * Gets the statics table.
896
     *
897
     * @return The statics table
898
     */
899
    public final VmSharedStatics getSharedStatics() {
900
        return sharedStatics;
901
    }
902
903
    /**
904
     * Gets the isolated statics table (of the current isolate)
905
     *
906
     * @return The statics table
907
     */
908
    public final VmIsolatedStatics getIsolatedStatics() {
909
        if (isolatedStatics != null) {
910
            return isolatedStatics;
911
        } else {
912
            return VmProcessor.current().getIsolatedStatics();
913
        }
914
    }
915
916
    /**
917
     * Gets the architecture used by this loader.
918
     *
919
     * @return The architecture
920
     */
921
    public final VmArchitecture getArchitecture() {
922
        return arch;
923
    }
924
925
    /**
926
     * Should prepared classes be compiled.
927
     *
928
     * @return boolean
929
     */
930
    public boolean isCompileRequired() {
931
        return requiresCompile;
932
    }
933
934
    /**
935
     * Should prepared classes be compiled.
936
     */
937
    public void setCompileRequired() {
938
        requiresCompile = true;
939
    }
940
941
    /**
942
     * @param loader
943
     */
944
    public void add(ResourceLoader loader) {
945
        resourceLoaders.add(loader);
946
    }
947
948
    /**
949
     * @param loader
950
     */
951
    public void remove(ResourceLoader loader) {
952
        resourceLoaders.remove(loader);
953
    }
954
}