package php.runtime.loader; import php.runtime.Memory; import php.runtime.common.Callback; import php.runtime.env.Environment; import php.runtime.memory.ArrayMemory; import php.runtime.reflection.*; import php.runtime.reflection.helper.ClosureEntity; import php.runtime.reflection.helper.GeneratorEntity; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class RuntimeClassLoader extends URLClassLoader { protected Map<String, ClassEntity> internalClasses = new HashMap<String, ClassEntity>(); protected Map<String, FunctionEntity> internalFunctions = new HashMap<String, FunctionEntity>(); protected Map<String, ModuleEntity> internalModules = new HashMap<String, ModuleEntity>(); protected List<Callback<Void, URL>> addLibraryListeners = new ArrayList<>(); public RuntimeClassLoader() { this(Thread.currentThread().getContextClassLoader()); } public RuntimeClassLoader(ClassLoader parent) { super(new URL[0], parent); } public ClassEntity getClass(String internalName) { return internalClasses.get(internalName); } public FunctionEntity getFunction(String internalName) { return internalFunctions.get(internalName); } public ModuleEntity getModule(String internalName) { return internalModules.get(internalName); } public Class<?> loadClass(ClassEntity clazz, boolean withBytecode) throws NoSuchMethodException, NoSuchFieldException { if (withBytecode) { byte[] data = translateData(clazz.getData()); Class<?> result = null; if (result == null) { result = defineClass(null, data, 0, data.length); } clazz.setNativeClazz(result); } for (MethodEntity method : clazz.getMethods().values()) { if (!(method instanceof CompileMethodEntity) && method.getNativeMethod() == null && !method.isAbstractable()) { method.setNativeMethod( clazz.getNativeClass().getDeclaredMethod(method.getInternalName(), Environment.class, Memory[].class) ); method.getNativeMethod().setAccessible(true); } } internalClasses.put(clazz.getCompiledInternalName(), clazz); return clazz.getNativeClass(); } protected Class<?> loadClosure(ClosureEntity closure, boolean withBytecode) throws NoSuchMethodException, NoSuchFieldException { return loadClass(closure, withBytecode); } protected Class<?> loadFunction(FunctionEntity function, boolean withBytecode) throws NoSuchMethodException { String className = function.getInternalName(); if (withBytecode) { byte[] data = translateData(function.getData()); Class<?> result = defineClass(null, data, 0, data.length); function.setNativeClazz(result); } Method method = function.getNativeClazz().getDeclaredMethod( "__invoke", Environment.class, Memory[].class ); function.setNativeMethod(method); internalFunctions.put(className, function); return function.getNativeClazz(); } protected Class<?> loadGenerator(GeneratorEntity generator, boolean withBytecode) throws NoSuchMethodException, NoSuchFieldException { return loadClass(generator, withBytecode); } public boolean loadModule(ModuleEntity module, boolean withBytecode) { String internal = module.getInternalName(); boolean ret = false; if (!module.isLoaded()) { internalModules.put(internal, module); try { for (ClosureEntity closure : module.getClosures()) loadClosure(closure, withBytecode); for (GeneratorEntity generator : module.getGenerators()) loadGenerator(generator, withBytecode); for (ClassEntity clazz : module.getClasses()) { if (clazz.getType() != ClassEntity.Type.INTERFACE) loadClass(clazz, withBytecode); } for (FunctionEntity function : module.getFunctions()) { loadFunction(function, withBytecode); } } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } module.setLoaded(true); ret = true; } if (withBytecode) { byte[] data = translateData(module.getData()); Class<?> result = defineClass( null, data, 0, module.getData().length ); module.setNativeClazz(result); } try { Method method = module.getNativeClazz().getDeclaredMethod( "__include", Environment.class, Memory[].class, ArrayMemory.class ); module.setNativeMethod(method); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } return ret; } protected byte[] translateData(byte[] data) { return data; } synchronized public void addLibrary(URL url) { addURL(url); for (Callback<Void, URL> listener : addLibraryListeners) { listener.call(url); } } public URL[] getLibraries() { return getURLs(); } public void onAddLibrary(Callback<Void, URL> listener) { addLibraryListeners.add(listener); } }