Remove Assembly.
[SquirrelJME.git] / emulators / springcoat-vm / src / main / java / cc / squirreljme / vm / springcoat / SpringClassLoader.java
blob51f02caa3776a50ea18b779a7213369376a4c3c8
1 // -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
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;
24 import java.util.Map;
25 import net.multiphasicapps.classfile.ClassFile;
26 import net.multiphasicapps.classfile.ClassName;
27 import net.multiphasicapps.classfile.InvalidClassFormatException;
29 /**
30 * This class acts as the equivalent to {@code ClassLoader} in that it manages
31 * the class path and the eventual loading of classes.
33 * @since 2018/09/01
35 public final class SpringClassLoader
37 /** Class loading lock. */
38 protected final Object loaderlock =
39 new Object();
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 =
46 new HashMap<>();
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. */
56 private int _nexcsi =
59 /**
60 * Initializes the class loader.
62 * @param __classpath The classpath.
63 * @throws NullPointerException On null arguments.
64 * @since 2018/09/01
66 public SpringClassLoader(VMClassLibrary... __classpath)
67 throws NullPointerException
69 for (VMClassLibrary b : __classpath = (__classpath == null ?
70 new VMClassLibrary[0] : __classpath.clone()))
71 if (b == null)
72 throw new NullPointerException("NARG");
73 this._classpath = __classpath;
76 /**
77 * Returns the library that is used for booting, the main entry JAR.
79 * @return The boot library.
80 * @since 2018/09/13
82 public final VMClassLibrary bootLibrary()
84 VMClassLibrary[] classpath = this._classpath;
85 return classpath[classpath.length - 1];
88 /**
89 * Returns the class loading lock.
91 * @return The class loading lock.
92 * @since 2018/09/08
94 public final Object classLoadingLock()
96 return this.loaderlock;
99 /**
100 * Returns the class path.
102 * @return The classpath.
103 * @since 2018/12/06
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.
116 * @since 2018/10/07
118 public final VMClassLibrary findLibrary(String __n)
119 throws NullPointerException
121 if (__n == null)
122 throw new NullPointerException("NARG");
124 for (VMClassLibrary lib : this._classpath)
125 if (__n.equals(lib.name()))
126 return lib;
128 return null;
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
138 * properly.
139 * @throws SpringClassNotFoundException If the class was not found.
140 * @since 2018/09/01
142 public final SpringClass loadClass(ClassName __cn)
143 throws NullPointerException, SpringClassFormatException,
144 SpringClassNotFoundException
146 if (__cn == null)
147 throw new NullPointerException("NARG");
149 // Lock on classes
150 Map<ClassName, SpringClass> classes = this._classes;
151 synchronized (this.loaderlock)
153 // If the class has already been initialized, use that
154 SpringClass rv;
155 synchronized (this)
157 rv = classes.get(__cn);
160 if (rv != null)
161 return rv;
163 // Debug
164 /*todo.DEBUG.note("Loading class `%s`...", __cn);*/
166 // Load class file for this class
167 ClassFile cf;
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
176 // to load)}
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]);
193 // Component?
194 SpringClass component = null;
195 if (__cn.isArray())
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
203 synchronized (this)
205 classes.put(__cn, rv);
208 return rv;
213 * This goes through the classpath and loads the specified class file for
214 * the given class.
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
221 * properly.
222 * @throws SpringClassNotFoundException If the class was not found.
223 * @since 2018/09/01
225 public final ClassFile loadClassFile(ClassName __cn, VMClassLibrary[] __ij)
226 throws NullPointerException, SpringClassFormatException,
227 SpringClassNotFoundException
229 if (__cn == null)
230 throw new NullPointerException("NARG");
232 // Debug
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
244 byte[] data = null;
245 for (VMClassLibrary b : this._classpath)
246 try (InputStream in = b.resourceAsStream(fileform))
248 // Class or file does not exist
249 if (in == null)
250 continue;
252 // Read in the data
253 data = StreamUtils.readAll(in);
255 // Record the binary
256 if (__ij != null && __ij.length > 0)
257 __ij[0] = b;
259 break;
261 catch (IOException e)
263 // {@squirreljme.error BK13 Failed to read from the class
264 // path.}
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)}
271 if (data == null)
272 throw new SpringClassNotFoundException(__cn, String.format(
273 "BK14 %s %s", __cn, fileform));
275 // Decode class file
276 ClassFile cf;
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.
301 * @since 2021/03/14
303 public final SpringClass[] loadedClasses()
305 synchronized (this)
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.
318 * @since 2021/03/15
320 public final SpringMachine machine()
321 throws IllegalStateException
323 synchronized (this)
325 if (this._machine == null)
326 throw new IllegalStateException("No machine set.");
328 SpringMachine rv = this._machine.get();
329 if (rv == null)
330 throw new IllegalStateException("Owner GCed.");
332 return rv;
337 * Returns the root library.
339 * @return The root library.
340 * @since 2020/07/09
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.
353 * @since 2021/03/15
355 void __bind(SpringMachine __machine)
356 throws IllegalStateException, NullPointerException
358 if (__machine == null)
359 throw new NullPointerException("NARG");
361 synchronized (this)
363 if (null != this._machine)
364 throw new IllegalStateException("Classloader already bound.");
366 this._machine = new WeakReference<>(__machine);