1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the GNU General Public License v3+, or later.
7 // See license.mkd for licensing and copyright information.
8 // ---------------------------------------------------------------------------
10 package cc
.squirreljme
.vm
.springcoat
;
12 import cc
.squirreljme
.runtime
.cldc
.util
.StreamUtils
;
13 import cc
.squirreljme
.vm
.VMClassLibrary
;
14 import cc
.squirreljme
.vm
.springcoat
.exceptions
.SpringClassFormatException
;
15 import cc
.squirreljme
.vm
.springcoat
.exceptions
.SpringClassNotFoundException
;
16 import cc
.squirreljme
.vm
.springcoat
.exceptions
.SpringVirtualMachineException
;
17 import java
.io
.ByteArrayInputStream
;
18 import java
.io
.IOException
;
19 import java
.io
.InputStream
;
20 import java
.lang
.ref
.Reference
;
21 import java
.lang
.ref
.WeakReference
;
22 import java
.util
.Collection
;
23 import java
.util
.HashMap
;
25 import net
.multiphasicapps
.classfile
.ClassFile
;
26 import net
.multiphasicapps
.classfile
.ClassName
;
27 import net
.multiphasicapps
.classfile
.InvalidClassFormatException
;
30 * This class acts as the equivalent to {@code ClassLoader} in that it manages
31 * the class path and the eventual loading of classes.
35 public final class SpringClassLoader
37 /** Class loading lock. */
38 protected final Object loaderlock
=
41 /** The class path for the machine. */
42 private final VMClassLibrary
[] _classpath
;
44 /** The classes which have been loaded by the virtual machine. */
45 private final Map
<ClassName
, SpringClass
> _classes
=
48 /** Reference to self. */
49 private final Reference
<SpringClassLoader
> _machineRef
=
50 new WeakReference
<>(this);
52 /** Reference to the machine owning this. */
53 private volatile Reference
<SpringMachine
> _machine
;
55 /** Next special class index. */
60 * Initializes the class loader.
62 * @param __classpath The classpath.
63 * @throws NullPointerException On null arguments.
66 public SpringClassLoader(VMClassLibrary
... __classpath
)
67 throws NullPointerException
69 for (VMClassLibrary b
: __classpath
= (__classpath
== null ?
70 new VMClassLibrary
[0] : __classpath
.clone()))
72 throw new NullPointerException("NARG");
73 this._classpath
= __classpath
;
77 * Returns the library that is used for booting, the main entry JAR.
79 * @return The boot library.
82 public final VMClassLibrary
bootLibrary()
84 VMClassLibrary
[] classpath
= this._classpath
;
85 return classpath
[classpath
.length
- 1];
89 * Returns the class loading lock.
91 * @return The class loading lock.
94 public final Object
classLoadingLock()
96 return this.loaderlock
;
100 * Returns the class path.
102 * @return The classpath.
105 public final VMClassLibrary
[] classPath()
107 return this._classpath
.clone();
111 * Finds the specified library.
113 * @param __n The library to find.
114 * @return The given library or {@code null} if it was not found.
115 * @throws NullPointerException On null arguments.
118 public final VMClassLibrary
findLibrary(String __n
)
119 throws NullPointerException
122 throw new NullPointerException("NARG");
124 for (VMClassLibrary lib
: this._classpath
)
125 if (__n
.equals(lib
.name()))
132 * Loads the specified class.
134 * @param __cn The name of the class to load.
135 * @return The loaded class.
136 * @throws NullPointerException On null arguments.
137 * @throws SpringClassFormatException If the class is not formatted
139 * @throws SpringClassNotFoundException If the class was not found.
142 public final SpringClass
loadClass(ClassName __cn
)
143 throws NullPointerException
, SpringClassFormatException
,
144 SpringClassNotFoundException
147 throw new NullPointerException("NARG");
150 Map
<ClassName
, SpringClass
> classes
= this._classes
;
151 synchronized (this.loaderlock
)
153 // If the class has already been initialized, use that
157 rv
= classes
.get(__cn
);
164 /*todo.DEBUG.note("Loading class `%s`...", __cn);*/
166 // Load class file for this class
168 VMClassLibrary
[] inJar
= new VMClassLibrary
[1];
171 cf
= this.loadClassFile(__cn
, inJar
);
173 catch (InvalidClassFormatException e
)
175 // {@squirreljme.error BK12 Could not load class. (The class
177 throw new InvalidClassFormatException(
178 String
.format("BK12 %s", __cn
), e
);
181 // Load the super class
182 ClassName supername
= cf
.superName();
183 SpringClass superclass
= (supername
== null ?
null :
184 this.loadClass(supername
));
186 // Load any interfaces
187 ClassName
[] interfacenames
= cf
.interfaceNames().toArray();
188 int numinterfaces
= interfacenames
.length
;
189 SpringClass
[] interfaceclasses
= new SpringClass
[numinterfaces
];
190 for (int i
= 0; i
< numinterfaces
; i
++)
191 interfaceclasses
[i
] = this.loadClass(interfacenames
[i
]);
194 SpringClass component
= null;
196 component
= this.loadClass(__cn
.componentType());
198 // Load class information
199 rv
= new SpringClass(superclass
, interfaceclasses
, cf
,
200 component
, inJar
[0], this._machineRef
);
202 // Store for later use
205 classes
.put(__cn
, rv
);
213 * This goes through the classpath and loads the specified class file for
216 * @param __cn The class to load.
217 * @param __ij The input JAR file for the class.
218 * @return The loaded class file data.
219 * @throws NullPointerException On null arguments.
220 * @throws SpringClassFormatException If the class is not formatted
222 * @throws SpringClassNotFoundException If the class was not found.
225 public final ClassFile
loadClassFile(ClassName __cn
, VMClassLibrary
[] __ij
)
226 throws NullPointerException
, SpringClassFormatException
,
227 SpringClassNotFoundException
230 throw new NullPointerException("NARG");
233 /*todo.DEBUG.note("Loading class file `%s`...", __cn);*/
235 // If this is an array type use virtual class representation
236 if (__cn
.isPrimitive() || __cn
.isArray())
237 return ClassFile
.special(__cn
.field());
239 // This is the class that is read, in binary form
240 String fileform
= __cn
.toString() + ".class";
242 // Otherwise we need to go through every single binary to find
243 // the class we want, which can take awhile
245 for (VMClassLibrary b
: this._classpath
)
246 try (InputStream in
= b
.resourceAsStream(fileform
))
248 // Class or file does not exist
253 data
= StreamUtils
.readAll(in
);
256 if (__ij
!= null && __ij
.length
> 0)
261 catch (IOException e
)
263 // {@squirreljme.error BK13 Failed to read from the class
265 throw new SpringException("BK13", e
);
268 // {@squirreljme.error BK14 Could not locate the specified class.
269 // (The class which was not found; The class file which was
270 // attempted to be located)}
272 throw new SpringClassNotFoundException(__cn
, String
.format(
273 "BK14 %s %s", __cn
, fileform
));
277 try (ByteArrayInputStream bais
= new ByteArrayInputStream(data
))
279 return ClassFile
.decode(bais
);
281 catch (IOException e
)
283 // {@squirreljme.error BK15 Could not read from the source
284 // class file. (The class being read)}
285 throw new SpringVirtualMachineException(String
.format(
286 "BK15 %s", __cn
), e
);
288 catch (InvalidClassFormatException e
)
290 // {@squirreljme.error BK16 The class is not formatted
291 // correctly. (The class being read)}
292 throw new SpringClassFormatException(__cn
, String
.format(
293 "BK16 %s", __cn
), e
);
298 * Returns all of the loaded classes.
300 * @return All of the loaded virtual machine classes.
303 public final SpringClass
[] loadedClasses()
307 Collection
<SpringClass
> classes
= this._classes
.values();
308 return classes
.<SpringClass
>toArray(
309 new SpringClass
[classes
.size()]);
314 * Returns the machine that owns this.
316 * @return The machine that owns this.
317 * @throws IllegalStateException If it was not set or GCed.
320 public final SpringMachine
machine()
321 throws IllegalStateException
325 if (this._machine
== null)
326 throw new IllegalStateException("No machine set.");
328 SpringMachine rv
= this._machine
.get();
330 throw new IllegalStateException("Owner GCed.");
337 * Returns the root library.
339 * @return The root library.
342 public final VMClassLibrary
rootLibrary()
344 return this._classpath
[0];
348 * Binds this class loader to the given machine.
350 * @param __machine The machine to bind to.
351 * @throws IllegalStateException If this is already bound.
352 * @throws NullPointerException On null arguments.
355 void __bind(SpringMachine __machine
)
356 throws IllegalStateException
, NullPointerException
358 if (__machine
== null)
359 throw new NullPointerException("NARG");
363 if (null != this._machine
)
364 throw new IllegalStateException("Classloader already bound.");
366 this._machine
= new WeakReference
<>(__machine
);