package php.runtime.env; import php.runtime.Memory; import php.runtime.Startup; import php.runtime.common.AbstractCompiler; import php.runtime.common.CompilerFactory; import php.runtime.common.LangMode; import php.runtime.common.Messages; import php.runtime.env.handler.EntityFetchHandler; import php.runtime.env.handler.ProgramShutdownHandler; import php.runtime.env.handler.TickHandler; import php.runtime.exceptions.ConflictException; import php.runtime.exceptions.CriticalException; import php.runtime.ext.CoreExtension; import php.runtime.ext.JavaExtension; import php.runtime.ext.NetExtension; import php.runtime.ext.java.JavaException; import php.runtime.ext.support.Extension; import php.runtime.ext.support.compile.CompileClass; import php.runtime.ext.support.compile.CompileConstant; import php.runtime.ext.support.compile.CompileFunction; import php.runtime.ext.support.compile.CompileFunctionSpec; import php.runtime.lang.*; import php.runtime.lang.exception.*; import php.runtime.lang.spl.ArrayAccess; import php.runtime.lang.spl.ErrorException; import php.runtime.lang.spl.Serializable; import php.runtime.lang.spl.Traversable; import php.runtime.lang.spl.iterator.IteratorAggregate; import php.runtime.loader.RuntimeClassLoader; import php.runtime.reflection.*; import php.runtime.reflection.support.ReflectionUtils; import php.runtime.util.JVMStackTracer; import php.runtime.wrap.ClassWrapper; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; public class CompileScope { public final int id; protected RuntimeClassLoader classLoader; public final Set<String> superGlobals; protected final static AtomicInteger scopeCount = new AtomicInteger(0); protected final AtomicInteger moduleCount = new AtomicInteger(0); protected final AtomicLong classCount = new AtomicLong(0); protected final AtomicLong methodCount = new AtomicLong(0); public final ConcurrentHashMap<String, ModuleEntity> moduleMap; public final ConcurrentHashMap<String, ModuleEntity> moduleIndexMap; protected final Map<String, ClassEntity> classMap; protected final Map<String, FunctionEntity> functionMap; protected final Map<String, ConstantEntity> constantMap; protected final Map<Class<? extends Throwable>, Class<? extends JavaException>> exceptionMap; protected final Map<String, Class<? extends JavaException>> exceptionMapForContext; protected Map<String, Extension> extensions; protected Map<String, CompileConstant> compileConstantMap; protected Map<String, CompileFunction> compileFunctionMap; protected Map<String, CompileClass> compileClassMap; protected Map<String, CompileFunctionSpec> compileFunctionSpecMap; protected CompilerFactory compilerFactory; protected List<EntityFetchHandler> classEntityFetchHandler; protected List<EntityFetchHandler> functionEntityFetchHandler; protected List<EntityFetchHandler> constantEntityFetchHandler; protected TickHandler tickHandler; protected List<ProgramShutdownHandler> programShutdownHandlers; public Map<String, Memory> configuration; // flags public boolean debugMode = false; protected LangMode langMode = LangMode.MODERN; public CompileScope(CompileScope parent) { id = scopeCount.getAndIncrement(); classLoader = parent.classLoader; langMode = parent.langMode; moduleMap = new ConcurrentHashMap<>(); moduleIndexMap = new ConcurrentHashMap<>(); classMap = new HashMap<>(); functionMap = new HashMap<>(); constantMap = new HashMap<>(); exceptionMap = new HashMap<>(); exceptionMapForContext = new HashMap<>(); extensions = new LinkedHashMap<>(); compileConstantMap = new HashMap<>(); compileFunctionMap = new HashMap<>(); compileFunctionSpecMap = new HashMap<>(); compileClassMap = new HashMap<>(); superGlobals = new HashSet<>(); superGlobals.addAll(parent.superGlobals); classMap.putAll(parent.classMap); compileClassMap.putAll(parent.compileClassMap); functionMap.putAll(parent.functionMap); compileFunctionMap.putAll(parent.compileFunctionMap); compileFunctionSpecMap.putAll(parent.compileFunctionSpecMap); constantMap.putAll(parent.constantMap); compileConstantMap.putAll(parent.compileConstantMap); exceptionMap.putAll(parent.exceptionMap); moduleMap.putAll(parent.moduleMap); moduleIndexMap.putAll(parent.moduleIndexMap); classCount.set(parent.classCount.longValue()); moduleCount.set(parent.moduleCount.intValue()); methodCount.set(parent.methodCount.longValue()); compilerFactory = parent.compilerFactory; classEntityFetchHandler = new ArrayList<>(parent.classEntityFetchHandler); functionEntityFetchHandler = new ArrayList<>(parent.functionEntityFetchHandler); constantEntityFetchHandler = new ArrayList<>(parent.constantEntityFetchHandler); extensions.putAll(parent.extensions); tickHandler = parent.tickHandler; programShutdownHandlers = new ArrayList<>(parent.programShutdownHandlers); } public CompileScope() { this(new RuntimeClassLoader(Thread.currentThread().getContextClassLoader())); } public CompileScope(RuntimeClassLoader classLoader) { id = scopeCount.getAndIncrement(); this.classLoader = classLoader; moduleMap = new ConcurrentHashMap<>(); moduleIndexMap = new ConcurrentHashMap<>(); classMap = new HashMap<>(); functionMap = new HashMap<>(); constantMap = new HashMap<>(); extensions = new LinkedHashMap<>(); compileConstantMap = new HashMap<>(); compileFunctionMap = new HashMap<>(); compileFunctionSpecMap = new HashMap<>(); compileClassMap = new HashMap<>(); exceptionMap = new HashMap<>(); exceptionMapForContext = new HashMap<>(); classEntityFetchHandler = new ArrayList<>(); constantEntityFetchHandler = new ArrayList<>(); functionEntityFetchHandler = new ArrayList<>(); programShutdownHandlers = new ArrayList<>(); superGlobals = new HashSet<>(); superGlobals.add("GLOBALS"); superGlobals.add("_ENV"); try { final Class<?> jvmCompilerClass = Class.forName("org.develnext.jphp.core.compiler.jvm.JvmCompiler"); final Constructor<?> jvmConstructor = jvmCompilerClass.getConstructor(Environment.class, Context.class); compilerFactory = new CompilerFactory() { @Override public AbstractCompiler getCompiler(Environment env, Context context) throws Throwable { return (AbstractCompiler) jvmConstructor.newInstance(env, context); } }; } catch (ClassNotFoundException | NoSuchMethodException e) { // nop. } CoreExtension extension = new CoreExtension(); registerLazyClass(extension, Closure.class); registerLazyClass(extension, Generator.class); registerLazyClass(extension, StdClass.class); registerLazyClass(extension, BaseThrowable.class); registerLazyClass(extension, BaseException.class); registerLazyClass(extension, BaseError.class); registerLazyClass(extension, BaseParseError.class); registerLazyClass(extension, BaseTypeError.class); registerLazyClass(extension, BaseArithmeticError.class); registerLazyClass(extension, BaseDivisionByZeroError.class); registerLazyClass(extension, ErrorException.class); registerLazyClass(extension, ArrayAccess.class); // iterators registerLazyClass(extension, Traversable.class); registerLazyClass(extension, php.runtime.lang.spl.iterator.Iterator.class); registerLazyClass(extension, IteratorAggregate.class); registerLazyClass(extension, Serializable.class); registerExtension(new JavaExtension()); registerExtension(new NetExtension()); registerExtension(extension); } public AbstractCompiler createCompiler(Environment env, Context context) throws Throwable { if (compilerFactory == null) { throw new CriticalException("Cannot find a compiler to compile php code"); } try { return compilerFactory.getCompiler(env, context); } catch (InvocationTargetException e) { env.__throwException(e); return null; } } public RuntimeClassLoader getClassLoader() { return classLoader; } public void setNativeClassLoader(ClassLoader classLoader) { this.classLoader = new RuntimeClassLoader(classLoader); } public LangMode getLangMode() { return langMode; } public void setLangMode(LangMode langMode) { this.langMode = langMode; } public boolean isDebugMode() { return debugMode; } public void setDebugMode(boolean debugMode) { this.debugMode = debugMode; } public Map<String, ClassEntity> getClassMap() { return classMap; } public Map<String, FunctionEntity> getFunctionMap() { return functionMap; } public Map<String, ConstantEntity> getConstantMap() { return constantMap; } public int nextModuleIndex(){ return moduleCount.incrementAndGet(); } public long nextClassIndex(){ return classCount.incrementAndGet(); } public long nextMethodIndex(){ return methodCount.incrementAndGet(); } public void setTickHandler(TickHandler tickHandler) { this.tickHandler = tickHandler; } public TickHandler getTickHandler() { return tickHandler; } public void registerLazyClass(Extension extension, Class<?> clazz) { CompileClass el = new CompileClass(extension, clazz); compileClassMap.put(el.getLowerName(), el); } @SuppressWarnings("unchecked") public void registerExtension(String extClass) { try { registerExtension((Class<? extends Extension>) Class.forName(extClass)); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public void registerExtension(Class<? extends Extension> extClass) { try { registerExtension(extClass.newInstance()); } catch (Exception e) { throw new CriticalException(e); } } public void registerExtension(Extension extension) { long t = System.currentTimeMillis(); if (extensions.containsKey(extension.getName())) return; // required for(String dep : extension.getRequiredExtensions()){ try { Extension el = (Extension) Class.forName(dep).newInstance(); registerExtension(el); } catch (Exception e) { throw new CriticalException(e); } } // optional for (String dep : extension.getOptionalExtensions()) { try { Extension el = (Extension) Class.forName(dep).newInstance(); registerExtension(el); } catch (ClassNotFoundException e) { // do nothing ... } catch (Exception e) { throw new CriticalException(e); } } // conflicts for(String dep : extension.getConflictExtensions()) { if (extensions.containsKey(dep)) throw new ConflictException( "'" + dep + "' extension conflicts with '" + extension.getClass().getName() + "'" ); } extension.onRegister(this); compileConstantMap.putAll(extension.getConstants()); compileFunctionSpecMap.putAll(extension.getFunctions()); for(Class<?> clazz : extension.getClasses().values()) { registerLazyClass(extension, clazz); } for(CompileFunctionSpec function : extension.getFunctions().values()) { functionMap.put(function.getLowerName(), new CompileFunctionEntity(extension, function)); } extensions.put(extension.getName().toLowerCase(), extension); if (Startup.isTracing()) { Startup.traceWithTime("Register extension '" + extension.getName() + "'", t); } } public Extension getExtension(String name){ return extensions.get(name.toLowerCase()); } public Set<String> getExtensions(){ return extensions.keySet(); } public void addClassEntityFetchHandler(EntityFetchHandler classEntityFetchHandler) { this.classEntityFetchHandler.add(classEntityFetchHandler); } public void addFunctionEntityFetchHandler(EntityFetchHandler functionEntityFetchHandler) { this.functionEntityFetchHandler.add(functionEntityFetchHandler); } public void addConstantEntityFetchHandler(EntityFetchHandler constantEntityFetchHandler) { this.constantEntityFetchHandler.add(constantEntityFetchHandler); } public void registerJavaException(Class<? extends JavaException> clazz, Class<? extends Throwable> throwClazz) { exceptionMap.put(throwClazz, clazz); } public void registerJavaExceptionForContext(Class<? extends JavaException> clazz, Class<? extends IObject> context) { exceptionMapForContext.put(ReflectionUtils.getClassName(context).toLowerCase(), clazz); } public void registerClass(String name, ClassEntity entity) { classMap.put(name.toLowerCase(), entity); } public void registerClass(ClassEntity clazz) { classMap.put(clazz.getLowerName(), clazz); } public void registerModule(ModuleEntity module) { addUserModule(module); for(ClassEntity entity : module.getClasses()) { if (entity.isStatic()){ if (classMap.put(entity.getLowerName(), entity) != null) { throw new CriticalException(Messages.ERR_CANNOT_REDECLARE_CLASS.fetch(entity.getName())); } } } for(FunctionEntity entity : module.getFunctions()) { if (entity.isStatic()) { if (functionMap.put(entity.getLowerName(), entity) != null) { throw new CriticalException(Messages.ERR_CANNOT_REDECLARE_FUNCTION.fetch(entity.getName())); } } } for(ConstantEntity entity : module.getConstants()) { if (constantMap.put(entity.getLowerName(), entity) != null) { throw new CriticalException(Messages.ERR_CANNOT_REDECLARE_CONSTANT.fetch(entity.getName())); } } } public void registerProgramShutdownHandler(ProgramShutdownHandler shutdownHandler) { programShutdownHandlers.add(shutdownHandler); } public void addUserModule(ModuleEntity module){ moduleMap.put(module.getName(), module); moduleIndexMap.put(module.getInternalName(), module); } public void registerFunction(FunctionEntity function){ functionMap.put(function.getLowerName(), function); } public void registerConstant(ConstantEntity constant){ constantMap.put(constant.getLowerName(), constant); } public ModuleEntity findUserModule(String name){ return moduleMap.get(name); } public ClassEntity fetchUserClass(Class<? extends IObject> clazz) { return fetchUserClass(ReflectionUtils.getClassName(clazz)); } public ClassEntity fetchUserClass(String name) { return fetchUserClass(name, name.toLowerCase()); } public ClassEntity fetchUserClass(String name, String nameLower) { ClassEntity entity; if (classEntityFetchHandler != null) { for (EntityFetchHandler handler : classEntityFetchHandler) { handler.fetch(this, name, nameLower); } } entity = classMap.get(nameLower); if (entity != null) { return entity; } CompileClass compileClass = compileClassMap.get(nameLower); if (compileClass == null) return null; entity = new ClassEntity(new ClassWrapper( compileClass.getExtension(), this, compileClass.getNativeClass() )); entity.setId(nextClassIndex()); synchronized (classMap) { classMap.put(name, entity); } return entity; } public FunctionEntity findUserFunction(String name) { String originName = name; name = name.toLowerCase(); FunctionEntity entity = functionMap.get(name); if (entity == null && functionEntityFetchHandler != null) { for (EntityFetchHandler handler : functionEntityFetchHandler) { handler.fetch(this, originName, name); } entity = functionMap.get(name); } return entity; } public ConstantEntity findUserConstant(String name){ String originName = name; name = name.toLowerCase(); ConstantEntity entity = constantMap.get(name.toLowerCase()); if (entity == null && constantEntityFetchHandler != null) { for (EntityFetchHandler handler : constantEntityFetchHandler) { handler.fetch(this, originName, name); } entity = constantMap.get(name); } return entity; } public Collection<ConstantEntity> getConstants(){ return constantMap.values(); } public CompileConstant findCompileConstant(String name){ return compileConstantMap.get(name); } public CompileFunction findCompileFunction(String name) { name = name.toLowerCase(); CompileFunction function = compileFunctionMap.get(name); if (function != null) { return function; } synchronized (this) { CompileFunctionSpec functionSpec = compileFunctionSpecMap.get(name); if (functionSpec == null) { return null; } compileFunctionMap.put(name, function = functionSpec.toFunction()); } return function; } public CompileClass findCompileClass(String name) { return compileClassMap.get(name.toLowerCase()); } public Class<? extends JavaException> findJavaException(Class<? extends Throwable> clazz) { return exceptionMap.get(clazz); } public Class<? extends JavaException> findJavaExceptionForContext(String className) { return exceptionMapForContext.get(className); } /** * Loads a module by name (filename) for classLoader * @param name a filename * @return */ public ModuleEntity loadModule(String name, boolean withBytecode) { ModuleEntity entity = findUserModule(name); if (entity == null) return null; classLoader.loadModule(entity, withBytecode); return entity; } public ModuleEntity loadModule(String name) { return loadModule(name, true); } /** * Loads a module for classLoader * @param module a module */ public void loadModule(ModuleEntity module, boolean withBytecode) { classLoader.loadModule(module, withBytecode); } public void loadModule(ModuleEntity module) { loadModule(module, true); } public JVMStackTracer getStackTracer(StackTraceElement[] elements) { return new JVMStackTracer(classLoader, elements); } public JVMStackTracer getStackTracer(Throwable throwable){ return getStackTracer(throwable.getStackTrace()); } public JVMStackTracer getStackTracer(){ return getStackTracer(Thread.currentThread().getStackTrace()); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CompileScope)) return false; CompileScope that = (CompileScope) o; return id == that.id; } @Override public int hashCode() { return id; } public void triggerProgramShutdown(Environment env) { for (ProgramShutdownHandler handler : programShutdownHandlers) { handler.onShutdown(this, env); } } }