package php.runtime.env; import php.runtime.Information; import php.runtime.Memory; import php.runtime.common.*; import php.runtime.env.handler.*; import php.runtime.env.message.CustomSystemMessage; import php.runtime.env.message.NoticeMessage; import php.runtime.env.message.SystemMessage; import php.runtime.env.message.WarningMessage; import php.runtime.exceptions.*; import php.runtime.exceptions.support.ErrorException; import php.runtime.exceptions.support.ErrorType; import php.runtime.ext.core.classes.WrapEnvironment; import php.runtime.ext.core.classes.WrapEnvironmentVariables; import php.runtime.ext.java.JavaReflection; import php.runtime.ext.support.Extension; import php.runtime.ext.support.compile.CompileConstant; import php.runtime.ext.support.compile.CompileFunctionSpec; import php.runtime.invoke.Invoker; import php.runtime.invoke.ObjectInvokeHelper; import php.runtime.lang.*; import php.runtime.lang.exception.BaseBaseException; import php.runtime.lang.exception.BaseError; import php.runtime.lang.exception.BaseParseError; import php.runtime.loader.dump.ModuleDumper; import php.runtime.loader.sourcemap.SourceMap; import php.runtime.memory.ArrayMemory; import php.runtime.memory.ObjectMemory; import php.runtime.memory.ReferenceMemory; import php.runtime.memory.StringMemory; import php.runtime.output.OutputBuffer; import php.runtime.reflection.*; import php.runtime.reflection.support.ReflectionUtils; import php.runtime.util.JVMStackTracer; import java.io.File; import java.io.OutputStream; import java.io.PrintStream; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import static php.runtime.exceptions.support.ErrorType.*; public class Environment { public final int id; public final CompileScope scope; public final Map<String, Memory> configuration = new HashMap<String, Memory>(); public final static Map<String, ConfigChangeHandler> configurationHandler; // call stack private CallStack callStack = new CallStack(this); private Set<String> includePaths; public SplClassLoader __autoload = null; public SplClassLoader defaultAutoLoader = null; protected final List<SplClassLoader> classLoaders = new LinkedList<SplClassLoader>(); // errors private int errorFlags = E_ALL.value ^ (E_NOTICE.value | E_STRICT.value | E_DEPRECATED.value); private Stack<Integer> silentFlags = new Stack<Integer>(); private SystemMessage lastMessage; private ErrorReportHandler errorReportHandler; private ErrorHandler previousErrorHandler; private ErrorHandler errorHandler; private ShellExecHandler shellExecHandler = ShellExecHandler.DEFAULT; private ExceptionHandler previousExceptionHandler; private ExceptionHandler exceptionHandler = ExceptionHandler.DEFAULT; private OutputBuffer defaultBuffer; private Stack<OutputBuffer> outputBuffers; private List<ShutdownHandler> shutdownFunctions = new LinkedList<ShutdownHandler>(); protected Map<String, SourceMap> sourceMaps = new HashMap<>(); // charset, locale private Locale locale = Locale.getDefault(); private Charset defaultCharset = Charset.forName("UTF-8"); // vars protected final ArrayMemory globals; protected final Map<String, ReferenceMemory> statics; protected final Map<String, Object> userValues = new HashMap<String, Object>(); // classes, funcs, consts public final Map<String, ClassEntity> classMap = new LinkedHashMap<String, ClassEntity>(); protected final Map<String, FunctionEntity> functionMap = new LinkedHashMap<String, FunctionEntity>(); protected final Map<String, ConstantEntity> constantMap = new LinkedHashMap<String, ConstantEntity>(); protected final ModuleManager moduleManager; protected final PackageManager packageManager; protected static final ThreadLocal<Environment> environment = new ThreadLocal<Environment>(); /** * Gets Environment for current thread context * * @return */ @Deprecated public static Environment current() { return environment.get(); } private final ReferenceQueue<IObject> gcObjectRefQueue = new ReferenceQueue<IObject>(); private final Set<WeakReference<IObject>> gcObjects = new HashSet<WeakReference<IObject>>(); private static final AtomicInteger ids = new AtomicInteger(); private static final Stack<Integer> freeIds = new Stack<Integer>(); public static void catchThrowable(Throwable e, Environment environment) { if (e instanceof BaseBaseException) { BaseBaseException baseException = (BaseBaseException) e; baseException.getEnvironment().catchUncaught(baseException); return; } else if (e instanceof Exception) { Environment env = environment == null ? null : environment; if (env != null) { try { env.catchUncaught((Exception) e); } catch (RuntimeException e2) { if (env.getDefaultBuffer() == null || env.getDefaultBuffer().getOutput() == null) { e2.printStackTrace(); } else { e2.getCause().printStackTrace(new PrintStream(env.getDefaultBuffer().getOutput())); } } return; } } Environment env = environment == null ? null : environment; if (env != null) { e.printStackTrace(new PrintStream(env.getDefaultBuffer().getOutput())); } else { e.printStackTrace(); } } public static void addThreadSupport(Environment env) { addThreadSupport(Thread.currentThread(), env); } public static void addThreadSupport(Thread thread, final Environment env) { thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { Environment.catchThrowable(e, env); } }); } public Environment(Environment parent) { this(parent, parent.defaultBuffer.getOutput()); } public Environment(Environment parent, OutputStream output) { this(parent.scope, parent.defaultBuffer.getOutput()); configuration.putAll(parent.configuration); //constants.putAll(parent.constants); classMap.putAll(parent.classMap); for (ClassEntity e : classMap.values()) { try { e.initEnvironment(this); } catch (Throwable throwable) { throw new RuntimeException(throwable); } } functionMap.putAll(parent.functionMap); constantMap.putAll(parent.constantMap); moduleManager.apply(parent.moduleManager); packageManager.apply(parent.packageManager); } public Environment(CompileScope scope, OutputStream output) { Environment.addThreadSupport(this); this.scope = scope; synchronized (freeIds) { if (freeIds.empty()) { this.id = ids.getAndIncrement(); } else { this.id = freeIds.peek(); } } this.moduleManager = new ModuleManager(this); this.packageManager = new PackageManager(this); this.outputBuffers = new Stack<OutputBuffer>(); this.defaultBuffer = new OutputBuffer(this, null); this.defaultBuffer.setOutput(output); Stack<OutputBuffer> buffers = this.getOutputBuffers(); if (buffers.isEmpty()) { buffers.push(defaultBuffer); } this.includePaths = new HashSet<String>(); this.globals = new ArrayMemory(); this.statics = new HashMap<String, ReferenceMemory>(); this.setErrorReportHandler(new ErrorReportHandler() { @Override public boolean onError(SystemMessage error) { Environment.this.echo(error.getDebugMessage()); Environment.this.echo("\n"); return false; } @Override public boolean onFatal(ErrorException error) { Environment.this.echo("\n"); Environment.this.echo(error.getType().getTypeName() + ": " + error.getMessage()); if (error.getTraceInfo() != null) { Environment.this.echo( " in " + error.getTraceInfo().getFileName() + " on line " + (error.getTraceInfo().getStartLine() + 1) + ", position " + (error.getTraceInfo().getStartPosition() + 1) ); } return false; } }); this.globals.put("GLOBALS", this.globals); this.globals.put("_ENV", ObjectMemory.valueOf(new WrapEnvironmentVariables(this))); //classMap.putAll(scope.getClassMap()); functionMap.putAll(scope.getFunctionMap()); constantMap.putAll(scope.getConstantMap()); Memory splAutoloader = new StringMemory("__$jphp_spl_autoload"); Invoker invoker = Invoker.valueOf(this, null, splAutoloader); if (invoker != null) this.defaultAutoLoader = new SplClassLoader(invoker, splAutoloader); for (Extension e : scope.extensions.values()) { e.onLoad(this); if (scope.getLangMode() == LangMode.MODERN) { String[] packageNames = e.getPackageNames(); if (packageNames != null) { Package aPackage = null; for (String packageName : packageNames) { if (aPackage == null) { aPackage = getPackageManager().fetch(packageName); for (Class<?> aClass : e.getClasses().values()) { aPackage.addClass(ReflectionUtils.getClassName(aClass)); } for (CompileFunctionSpec functionSpec : e.getFunctions().values()) { aPackage.addFunction(functionSpec.getName()); } for (CompileConstant constant : e.getConstants().values()) { aPackage.addConstant(constant.name); } } else { Package aPackage2 = getPackageManager().fetch(packageName); for (String s : aPackage.getClasses()) aPackage2.addClass(s); for (String s : aPackage.getFunctions()) aPackage2.addFunction(s); for (String s : aPackage.getConstants()) aPackage2.addConstant(s); } } } } } environment.set(this); } public void doFinal() throws Throwable { for (ShutdownHandler handler : shutdownFunctions) { try { handler.call(); } catch (DieException e) { finalizeObjects(); catchUncaught(e); break; } catch (BaseBaseException e) { catchUncaught(e); break; } catch (ErrorException e) { catchUncaught(e); break; } catch (Exception e) { catchUncaught(e); break; } } finalizeObjects(); flushAll(); lastMessage = null; } @Override protected void finalize() throws Throwable { super.finalize(); freeIds.push(id); } /** * Remove all method which does not destruct * * @throws Throwable */ public void finalizeObjects() throws Throwable { cleanGcObjects(); for (WeakReference<IObject> el : gcObjects) { IObject o = el.get(); if (o == null) continue; ClassEntity entity = o.getReflection(); if (entity.methodDestruct != null) { if (!o.isFinalized()) { o.doFinalize(); pushCall(o, entity.methodDestruct.getName()); try { entity.methodDestruct.invokeDynamic(o, this); } finally { popCall(); } } } } gcObjects.clear(); } private void cleanGcObjects() throws Throwable { Reference<? extends IObject> object; while (true) { object = gcObjectRefQueue.poll(); if (object == null) break; else gcObjects.remove(object); } } public void pushCall(CallStackItem stackItem) { getCallStack().push(stackItem); } public void pushCall(TraceInfo trace, IObject self, Memory[] args, String function, String clazz, String staticClazz) { getCallStack().push(trace, self, args, function, clazz, staticClazz); } public void pushCall(IObject self, String method, Memory... args) { getCallStack().push(self, method, args); } public void pushCall(TraceInfo trace, IObject self, String method, Memory... args) { getCallStack().push(trace, self, method, args); } public void popCall() { getCallStack().pop(); } public CallStackItem peekCall(int depth) { return getCallStack().peekCall(depth); } public TraceInfo trace() { return getCallStack().trace(); } public TraceInfo trace(int systemOffsetStackTrace) { return getCallStack().trace(systemOffsetStackTrace); } public int getCallStackTop() { return getCallStack().getTop(); } public CallStackItem[] getCallStackSnapshot() { return getCallStack().getSnapshot(); } public Environment(OutputStream output) { this(new CompileScope(), output); } public Environment(CompileScope scope) { this(scope, null); } public Environment() { this((OutputStream) null); } public Stack<OutputBuffer> getOutputBuffers() { return outputBuffers; } public Locale getLocale() { return locale; } public void setLocale(Locale locale) { this.locale = locale; } public String findInIncludePaths(String path) { if (new File(path).exists()) return path; for (String e : includePaths) if (new File(e, path).exists()) return e + path; return null; } public ModuleManager getModuleManager() { return moduleManager; } public PackageManager getPackageManager() { return packageManager; } public CompileScope getScope() { return scope; } public Collection<FunctionEntity> getFunctions() { return functionMap.values(); } public Collection<ClassEntity> getClasses() { return classMap.values(); } public ArrayMemory getGlobals() { return globals; } public Charset getDefaultCharset() { return defaultCharset; } public Set<String> getIncludePaths() { return includePaths; } public void addIncludePath(String value) { includePaths.add(value); } public void setIncludePaths(Set<String> includePaths) { this.includePaths = includePaths; } public Map<String, ClassEntity> getLoadedClasses() { return classMap; } public Map<String, FunctionEntity> getLoadedFunctions() { return functionMap; } public boolean isLoadedClass(String lowerName) { return classMap.containsKey(lowerName); } public boolean isLoadedFunction(String lowerName) { return functionMap.containsKey(lowerName); } public boolean isLoadedConstant(String lowerName) { return constantMap.containsKey(lowerName); } private final Set<String> autoloadLocks = new HashSet<String>(); public ClassEntity autoloadCall(String name, String lowerName) { synchronized (autoloadLocks) { // detect recursion in autoload if (StringUtils.isValidClassName(name) && autoloadLocks.add(lowerName)) { StringMemory tmp = new StringMemory(name); for (SplClassLoader loader : classLoaders) { loader.load(tmp); ClassEntity classEntity = fetchClass(name, false); if (classEntity != null) { autoloadLocks.remove(lowerName); return classEntity; } } if (defaultAutoLoader != null) { defaultAutoLoader.load(tmp); } autoloadLocks.remove(lowerName); return fetchClass(name, false); } else { return null; } } } public ClassEntity fetchClass(Class<?> clazz) { ClassEntity result = fetchClass(ReflectionUtils.getClassName(clazz), false); if (result == null) throw new CriticalException("Native class is not registered - " + clazz.getName()); return result; } public ClassEntity fetchClass(String name) { return fetchClass(name, false); } public ClassEntity fetchClass(String name, boolean autoLoad) { return fetchClass(name, name.toLowerCase(), autoLoad); } public ClassEntity fetchMagicClass(String name) { return fetchMagicClass(name, name.toLowerCase()); } public ClassEntity fetchMagicClass(String name, String nameL) { if ("self".equals(nameL)) { ClassEntity e = getContextClass(); if (e == null) e = getLateStaticClass(); return e; } else if ("static".equals(nameL)) { return getLateStaticClass(); }/* else if ("parent".equals(nameL)){ ClassEntity e = getContextClass(); if (e == null) e = getLateStaticClass(); return e == null ? null : e.getParent(); }*/ else return null; } public ClassEntity fetchClass(String name, String nameL, boolean autoLoad) { ClassEntity entity = classMap.get(nameL); if (entity != null) { return entity; } synchronized (classMap) { entity = classMap.get(nameL); // try retry! if (entity == null) { entity = scope.fetchUserClass(name, nameL); if (entity != null) { try { entity.initEnvironment(this); } catch (Throwable throwable) { throw new CriticalException(throwable); } classMap.put(entity.getLowerName(), entity); return entity; } ClassEntity classEntity = autoLoad ? autoloadCall(name, nameL) : null; return classEntity; } else { return entity; } } } public FunctionEntity fetchFunction(String name) { return fetchFunction(name, name.toLowerCase()); } public FunctionEntity fetchFunction(String name, String nameL) { FunctionEntity r = functionMap.get(nameL); if (r == null) { r = scope.findUserFunction(name); } return r; } public void registerFunction(FunctionEntity entity) { synchronized (functionMap) { if (functionMap.containsKey(entity.getLowerName())) exception("Function '%s' already registered", entity.getName()); functionMap.put(entity.getLowerName(), entity); } } public void registerClass(ClassEntity entity) { synchronized (classMap) { if (classMap.containsKey(entity.getLowerName())) exception("Class '%s' already registered", entity.getName()); classMap.put(entity.getLowerName(), entity); entity.initEnvironment(this); } } public Map<String, ConstantEntity> getConstants() { return constantMap; } public Memory findConstant(String name, String nameLower) { ConstantEntity entity = constantMap.get(nameLower); if (entity != null) { if (!entity.caseSensitise || name.equals(entity.getName())) return entity.getValue(); } CompileConstant constant = scope.findCompileConstant(name); if (constant != null) return constant.value; return null; } public Memory findConstant(String name) { return findConstant(name, name.toLowerCase()); } public boolean defineConstant(String name, Memory value, boolean caseSensitise) { Memory constant = findConstant(name); if (constant != null) return false; constantMap.put(name.toLowerCase(), new ConstantEntity(name, value, caseSensitise)); return true; } public Memory getConfigValue(String name, Memory defaultValue) { Memory result = null; if (scope.configuration == null || (result = configuration.get(name)) != null) { if (result == null) result = configuration.get(name); if (result == null) return defaultValue; return result; } result = scope.configuration.get(name); return result == null ? defaultValue : result; } public Memory getConfigValue(String name) { return getConfigValue(name, null); } public ArrayMemory getConfigValues(String prefix, boolean includingGlobal) { if (prefix != null) prefix = prefix + "."; ArrayMemory result = new ArrayMemory(); if (includingGlobal) if (scope.configuration != null) { for (Map.Entry<String, Memory> entry : scope.configuration.entrySet()) { String key = entry.getKey(); if (prefix != null && !key.startsWith(prefix)) continue; result.put(key, entry.getValue().toImmutable()); } } for (Map.Entry<String, Memory> entry : configuration.entrySet()) { String key = entry.getKey(); if (prefix != null && !key.startsWith(prefix)) continue; result.put(key, entry.getValue().toImmutable()); } return result; } public Memory setConfigValue(String name, Memory value) { value = value.toValue(); if (!value.isString()) value = new StringMemory(value.toString()); // fix capability with zend php ConfigChangeHandler handler = configurationHandler.get(name); if (handler != null) handler.onChange(this, value); return configuration.put(name, value); } public void restoreConfigValue(String name) { configuration.remove(name); ConfigChangeHandler handler = configurationHandler.get(name); if (handler != null) handler.onChange(this, getConfigValue(name)); } public Memory getOrCreateGlobal(String name) { return globals.refOfIndex(name); } public Memory getOrCreateStatic(String name, Memory initValue) { ReferenceMemory result = statics.get(name); if (result == null) { result = new ReferenceMemory(initValue); statics.put(name, result); } return result; // globals.getByScalarOrCreate(name, initValue); } public Memory getStatic(String name) { return statics.get(name); } @SuppressWarnings("unchecked") public <T> T getUserValue(String name, Class<T> clazz) { return (T) userValues.get(name); } public void setUserValue(String name, Object value) { userValues.put(name, value); } public <T> T getUserValue(Class<T> clazz) { Objects.requireNonNull(clazz); return getUserValue(clazz.getName(), clazz); } public <T> void setUserValue(T object) { Objects.requireNonNull(object); setUserValue(object.getClass().getName(), object); } public boolean removeUserValue(String name) { return userValues.remove(name) != null; } public int getErrorFlags() { return errorFlags; } public void setErrorFlags(int errorFlags) { this.errorFlags = errorFlags; } public SystemMessage getLastMessage() { return lastMessage; } public ShellExecHandler getShellExecHandler() { return shellExecHandler; } public void setShellExecHandler(ShellExecHandler shellExecHandler) { this.shellExecHandler = shellExecHandler; } public ExceptionHandler getExceptionHandler() { return exceptionHandler; } public void setExceptionHandler(ExceptionHandler exceptionHandler) { this.previousExceptionHandler = this.exceptionHandler; this.exceptionHandler = exceptionHandler == null ? ExceptionHandler.DEFAULT : exceptionHandler; } public ErrorReportHandler getErrorReportHandler() { return errorReportHandler; } public void setErrorReportHandler(ErrorReportHandler errorReportHandler) { this.errorReportHandler = errorReportHandler; } public ExceptionHandler getPreviousExceptionHandler() { return previousExceptionHandler; } public ErrorHandler getPreviousErrorHandler() { return previousErrorHandler; } public ErrorHandler getErrorHandler() { return errorHandler; } protected void triggerError(ErrorException err) { ErrorType type = err.getType(); if (type.isFatal() || isHandleErrors(type)) { lastMessage = new CustomSystemMessage( err.getType(), new CallStackItem(err.getTraceInfo()), new Messages.Item(err.getMessage()) ); throw err; } } public void forwardThrow(Throwable throwable) { if (throwable instanceof RuntimeException) { throw (RuntimeException) throwable; } else { throw new RuntimeException(throwable); } } public void wrapThrow(Throwable throwable) { if (throwable instanceof Exception) { catchUncaught((Exception) throwable); } else { throw new RuntimeException(throwable); } } public boolean catchUncaught(Exception e) { return catchUncaught(e, false); } public boolean catchUncaught(Exception e, boolean retry) { if (e instanceof UncaughtException) return catchUncaught((UncaughtException) e); else if (e instanceof DieException) { System.exit(((DieException) e).getExitCode()); return true; } else if (e instanceof ErrorException) { ErrorException er = (ErrorException) e; getErrorReportHandler().onFatal(er); JVMStackTracer tracer = scope.getStackTracer(e); int i = 0; for (JVMStackTracer.Item el : tracer) { if (!el.isInternal()) { echo("\n\t #" + (i++) + " " + el); } } echo("\n"); for (JVMStackTracer.Item el : tracer) { if (!el.isSystem()) { echo("\n\t " + (el.isInternal() ? "" : "->") + " " + el); } } return true; } else if (e instanceof FinallyException) { // nop return true; } else if (e instanceof BaseBaseException) { BaseBaseException be = (BaseBaseException) e; if (exceptionHandler != null) { try { exceptionHandler.onException(this, be); } catch (BaseBaseException _e) { if (retry) { throw new RuntimeException(_e); } else { catchUncaught(_e, true); } } catch (Throwable throwable) { throw new RuntimeException(throwable); } return true; } else { try { ExceptionHandler.DEFAULT.onException(this, be); return true; } catch (Throwable throwable) { throw new RuntimeException(throwable); } } } else { throw new RuntimeException(e); } } public boolean catchUncaught(UncaughtException e) { if (exceptionHandler != null) { try { if (!(e.getException() instanceof FinallyException)) { exceptionHandler.onException(this, e.getException()); } } catch (BaseBaseException _e) { catchUncaught(_e); } catch (Throwable throwable) { throw new RuntimeException(throwable); } return true; } else return false; } public void error(TraceInfo trace, ErrorType type, String message, Object... args) { error(trace, type, new Messages.Item(message), args); } public void error(TraceInfo trace, ErrorType type, Messages.Item message, Object... args) { if (type.isFatal()) { if (scope.getLangMode() == LangMode.MODERN) { if (type == E_PARSE) { exception(trace, new BaseParseError(this), message.fetch(args)); } else { exception(trace, new BaseError(this, type), message.fetch(args)); } } if (type.isHandled() && errorHandler != null && ErrorType.check(errorHandler.errorHandlerFlags, type)) { triggerMessage(new CustomSystemMessage(type, new CallStackItem(trace), message, args)); } else triggerError(new CustomErrorException(type, message.fetch(args), trace)); } else { triggerMessage(new CustomSystemMessage(type, new CallStackItem(trace), message, args)); } } public void error(ErrorType type, String message, Object... args) { error(trace(), type, message, args); } public void error(TraceInfo trace, String message, Object... args) { error(trace, E_ERROR, message, args); } public void triggerMessage(SystemMessage message) { lastMessage = message; if (errorHandler != null) { if (errorHandler.onError(this, message)) return; } if (errorReportHandler != null && isHandleErrors(message.getType())) errorReportHandler.onError(message); } public void exception(TraceInfo trace, String message, Object... args) { BaseException e = new BaseException(this); exception(trace, e, message, args); } public void exception(String message, Object... args) { exception(trace(), message, args); } public void exception(TraceInfo trace, BaseError e, String message, Object... args) { __clearSilent(); if (args == null || args.length == 0) { e.__construct(this, new StringMemory( message )); } else { e.__construct(this, new StringMemory( String.format(message, args) )); } e.setTraceInfo(this, trace); throw e; } public void exception(TraceInfo trace, BaseException e, String message, Object... args) { __clearSilent(); if (args == null || args.length == 0) { e.__construct(this, new StringMemory( message )); } else { e.__construct(this, new StringMemory( String.format(message, args) )); } e.setTraceInfo(this, trace); throw e; } public void exception(BaseException e, String message, Object... args) { exception(trace(), e, message, args); } public void exception(BaseError e, String message, Object... args) { exception(trace(), e, message, args); } public void exception(Class<? extends BaseBaseException> e, String message, Object... args) { exception(trace(), e, message, args); } public void exception(TraceInfo trace, Class<? extends BaseBaseException> e, String message, Object... args) { ClassEntity entity = fetchClass(e); IObject object = entity.newObjectWithoutConstruct(this); if (object instanceof BaseException) { exception(trace, (BaseException) object, message, args); } else if (object instanceof BaseError) { exception(trace, (BaseError) object, message, args); } else { throw new CriticalException("Unable to create extension object from class " + e.getName()); } } public boolean isHandleErrors(ErrorType type) { return ErrorType.check(errorFlags, type); } public void warning(String message, Object... args) { triggerMessage(new WarningMessage(peekCall(0), new Messages.Item(message), args)); } public void warning(TraceInfo trace, String message, Object... args) { error(trace, E_WARNING, message, args); } public void warning(TraceInfo trace, Messages.Item message, Object... args) { error(trace, E_WARNING, message, args); } public void notice(String message, Object... args) { triggerMessage(new NoticeMessage(peekCall(0), new Messages.Item(message), args)); } public OutputBuffer getDefaultBuffer() { return defaultBuffer; } public OutputBuffer pushOutputBuffer(Memory callback, int chunkSize, boolean erase) { Stack<OutputBuffer> outputBuffers = getOutputBuffers(); OutputBuffer buffer = new OutputBuffer(this, peekOutputBuffer(), callback, chunkSize, erase); buffer.setLevel(outputBuffers.size()); buffer.setType(OutputBuffer.Type.USER); outputBuffers.push(buffer); return buffer; } public OutputBuffer popOutputBuffer() throws Throwable { Stack<OutputBuffer> outputBuffers = getOutputBuffers(); if (outputBuffers.empty()) return null; if (outputBuffers.peek().isRoot()) return null; OutputBuffer result = outputBuffers.pop(); result.close(); return result; } public List<OutputBuffer> allOutputBuffers() { Stack<OutputBuffer> outputBuffers = getOutputBuffers(); List<OutputBuffer> result = new ArrayList<OutputBuffer>(); for (OutputBuffer el : outputBuffers) result.add(el); return result; } public OutputBuffer peekOutputBuffer() { Stack<OutputBuffer> outputBuffers = getOutputBuffers(); return outputBuffers.empty() ? null : outputBuffers.peek(); } public void echo(byte[] bytes, int length) { OutputBuffer buffer = peekOutputBuffer(); if (buffer != null) try { buffer.write(bytes, length); } catch (RuntimeException e) { throw e; } catch (Throwable throwable) { throw new RuntimeException(throwable); } } public void echo(Memory value) { OutputBuffer buffer = peekOutputBuffer(); if (buffer != null) try { buffer.write(value); } catch (RuntimeException e) { throw e; } catch (Throwable throwable) { throw new RuntimeException(throwable); } } public void echo(String value) { OutputBuffer buffer = peekOutputBuffer(); if (buffer != null) try { buffer.write(value); } catch (RuntimeException e) { throw e; } catch (Throwable throwable) { throw new RuntimeException(throwable); } } public void flushAll() throws Throwable { while (popOutputBuffer() != null) ; defaultBuffer.close(); } public ModuleEntity importCompiledModule(Context context, boolean debugInformation) throws Throwable { String moduleName = context.getModuleName(); ModuleEntity module = moduleName == null ? null : scope.findUserModule(moduleName); if (module == null) { ModuleDumper moduleDumper = new ModuleDumper(context, this, debugInformation); module = moduleDumper.load(context.getInputStream(getDefaultCharset())); synchronized (scope) { scope.loadModule(module); } } registerModule(module); scope.addUserModule(module); return module; } public ModuleEntity importModule(Context context) throws Throwable { String moduleName = context.getModuleName(); ModuleEntity module = moduleName == null ? null : scope.findUserModule(moduleName); if (module == null) { AbstractCompiler compiler = scope.createCompiler(this, context); module = compiler.compile(true); synchronized (scope) { scope.loadModule(module); } } registerModule(module); return module; } public Memory eval(String code) throws Throwable { return eval(code, getGlobals()); } public Memory eval(String code, ArrayMemory locals) throws Throwable { Context context = new Context(code); AbstractCompiler compiler = scope.createCompiler(this, context); ModuleEntity module = compiler.compile(); scope.loadModule(module); registerModule(module); return module.include(this, locals); } public void registerSourceMap(SourceMap sourceMap) { sourceMaps.put(sourceMap.getModuleName(), sourceMap); } public void unregisterSourceMap(SourceMap sourceMap) { sourceMaps.remove(sourceMap.getModuleName()); } public void registerModule(ModuleEntity module) { registerModule(module, false); } public void registerModule(ModuleEntity module, boolean ignoreErrors) { for (ClassEntity entity : module.getClasses()) { if (entity.isStatic()) { entity.setModule(module); if (classMap.put(entity.getLowerName(), entity) != null && !ignoreErrors) { error(entity.getTrace(), Messages.ERR_CANNOT_REDECLARE_CLASS.fetch(entity.getName())); } entity.register(this); } } for (FunctionEntity entity : module.getFunctions()) { if (entity.isStatic()) { entity.setModule(module); if (functionMap.put(entity.getLowerName(), entity) != null && !ignoreErrors) { error(entity.getTrace(), Messages.ERR_CANNOT_REDECLARE_FUNCTION.fetch(entity.getName())); } entity.register(this); } } for (ConstantEntity entity : module.getConstants()) { entity.setModule(module); if (constantMap.put(entity.getLowerName(), entity) != null && !ignoreErrors) { error(entity.getTrace(), Messages.ERR_CANNOT_REDECLARE_CONSTANT.fetch(entity.getName())); } entity.register(this); } } /***** UTILS *****/ public void __tick(TraceInfo trace, ArrayMemory locals) { TickHandler tickHandler = scope.getTickHandler(); if (tickHandler != null) { IObject $this = this.getLateObject(); if ($this != null) { Memory value = ObjectMemory.valueOf($this); if ($this instanceof Closure) { value = ((Closure) $this).getSelf(); } if (value.isObject()) { locals.putAsKeyString("this", value); } } tickHandler.onTick(this, trace, locals); } } public Memory __getConstant(String name, String lowerName, TraceInfo trace) { Memory constant = findConstant(name, lowerName); if (constant == null) { int p = name.lastIndexOf(Information.NAMESPACE_SEP_CHAR); if (p > -1) // for global scope { name = name.substring(p + 1); lowerName = lowerName.substring(p + 1); constant = findConstant(name, lowerName); } } if (constant == null) { error(trace, E_NOTICE, Messages.ERR_USE_UNDEFINED_CONSTANT, name, name); return StringMemory.valueOf(name); } return constant; } /*public Memory __getConstant(String name, String lowerName, TraceInfo trace){ Memory constant = findConstant(name, lowerName); if (constant == null){ error(trace, E_NOTICE, Messages.ERR_USE_UNDEFINED_CONSTANT, name, name); int p = name.lastIndexOf(Information.NAMESPACE_SEP_CHAR); if (p > -1) // for global scope return StringMemory.valueOf(name.substring(p + 1)); else return StringMemory.valueOf(name); } return constant; }*/ private Memory __import(String path, ArrayMemory locals, TraceInfo trace, String funcName, boolean once, Callback<Void, Void> callback) throws Throwable { synchronized (moduleManager) { if (once && moduleManager.hasModule(path)) { return Memory.TRUE; } ModuleEntity module = moduleManager.fetchCachedModule(path, path.endsWith(".phb")); if (module == null) { callback.call(null); return Memory.FALSE; } pushCall(trace, null, new Memory[]{StringMemory.valueOf(path)}, funcName, null, null); try { if (locals == null) { locals = new ArrayMemory(); } return module.include(this, locals); } finally { popCall(); } } } public Memory __include(String path) throws Throwable { return __include(path, globals, null); } public Memory __includeOnce(final String path, ArrayMemory locals, final TraceInfo trace) throws Throwable { return __import(path, locals, trace, "include_once", true, new Callback<Void, Void>() { @Override public Void call(Void param) { warning(trace, Messages.ERR_INCLUDE_FAILED, "include_once", path); return null; } }); } public Memory __include(final String fileName, ArrayMemory locals, final TraceInfo trace) throws Throwable { return __import(fileName, locals, trace, "include", false, new Callback<Void, Void>() { @Override public Void call(Void param) { warning(trace, Messages.ERR_INCLUDE_FAILED, "include", fileName); return null; } }); } public Memory __require(final String fileName, ArrayMemory locals, final TraceInfo trace) throws Throwable { return __import(fileName, locals, trace, "require", false, new Callback<Void, Void>() { @Override public Void call(Void param) { error(trace, Messages.ERR_REQUIRE_FAILED.fetch("require", fileName)); return null; } }); } public Memory __requireOnce(final String fileName, ArrayMemory locals, final TraceInfo trace) throws Throwable { return __import(fileName, locals, trace, "require_once", true, new Callback<Void, Void>() { @Override public Void call(Void param) { error(trace, Messages.ERR_REQUIRE_FAILED.fetch("require_once", fileName)); return null; } }); } public void registerObjectInGC(IObject object) { ClassEntity entity = object.getReflection(); if (entity != null && entity.methodDestruct != null) { try { cleanGcObjects(); } catch (Throwable throwable) { throw new RuntimeException(throwable); } WeakReference<IObject> wr = new WeakReference<IObject>(object, gcObjectRefQueue); gcObjects.add(wr); } } public Memory __newObject(String originName, String lowerName, TraceInfo trace, Memory[] args) throws Throwable { ClassEntity entity = fetchClass(originName, lowerName, true); if (entity == null) { error(trace, E_ERROR, Messages.ERR_CLASS_NOT_FOUND.fetch(originName)); return Memory.NULL; } IObject object = entity.newObject(this, trace, true, args); registerObjectInGC(object); return new ObjectMemory(object); } private static ForeachIterator invalidIterator = new ForeachIterator(false, false, false) { @Override protected boolean init() { return false; } @Override protected boolean nextValue() { return false; } @Override protected boolean prevValue() { return false; } @Override public void reset() { } }; public ForeachIterator __getIterator(TraceInfo trace, Memory memory, boolean getReferences, boolean getKeyReferences) { ForeachIterator iterator = memory.getNewIterator(this, getReferences, getKeyReferences); if (iterator == null) { warning(trace, "Invalid argument supplied for foreach()"); return invalidIterator; } iterator.setTrace(trace); return iterator; } public ClassEntity __getGenerator(String moduleIndex, int index) { ModuleEntity moduleEntity = scope.moduleIndexMap.get(moduleIndex); if (moduleEntity == null) throw new CriticalException("Cannot find the module (" + moduleIndex + ") for getting a generator object"); return moduleEntity.findGenerator(index); } public ClassEntity __getClosure(String moduleIndex, int index) { ModuleEntity moduleEntity = scope.moduleIndexMap.get(moduleIndex); if (moduleEntity == null) throw new CriticalException("Cannot find the module (" + moduleIndex + ") for getting a closure object"); return moduleEntity.findClosure(index); } public Memory __getSingletonClosure(String moduleIndex, int index) { Memory result = scope.moduleIndexMap.get(moduleIndex).findClosure(index).getSingleton(); assert result != null; return result; } public Memory __throwException(InvocationTargetException e) { Throwable throwable = e.getTargetException(); if (throwable instanceof FinallyException) { return Memory.NULL; } if (throwable instanceof JPHPException) throw (RuntimeException) throwable; else { JavaReflection.exception(this, throwable); } return Memory.NULL; } public void __throwException(BaseBaseException e) { __throwException(e, true); } public void __throwException(BaseBaseException e, boolean clearSilent) { if (clearSilent) { __clearSilent(); } e.setTraceInfo(this, trace()); throw e; } public void __throwException(TraceInfo trace, Memory exception) { if (exception.isObject()) { IObject object; if ((object = exception.toValue(ObjectMemory.class).value) instanceof BaseBaseException) { __clearSilent(); BaseBaseException e = (BaseBaseException) object; e.setTraceInfo(this, trace); throw e; } else { triggerError(new FatalException( "Exceptions must be valid objects derived from the Exception base class", trace )); } } else { triggerError(new FatalException( "Can only throw objects", trace )); } } public void __throwFailedCatch(BaseBaseException e) { if (e instanceof FinallyException) { return; } throw e; } public Memory __throwCatch(BaseBaseException e, String className, String lowerClassName) { ClassEntity origin = e.getReflection(); ClassEntity cause = fetchClass(className, lowerClassName, false); if (cause != null && origin.isInstanceOf(cause)) return new ObjectMemory(e); if (origin.isInstanceOfLower(lowerClassName)) return new ObjectMemory(e); else return Memory.NULL; } public void __pushSilent() { silentFlags.push(errorFlags); setErrorFlags(0); } public void __popSilent() { Integer flags = silentFlags.pop(); setErrorFlags(flags); } public void __clearSilent() { Stack<Integer> silents = silentFlags; Integer flags = null; while (!silents.empty()) flags = silents.pop(); if (flags != null) { setErrorFlags(flags); } } public Memory __getMacroClass() { CallStackItem item = peekCall(0); if (item != null && item.clazz != null) { if (item.classEntity == null) item.classEntity = fetchClass(item.clazz, false); if (item.classEntity == null) return Memory.CONST_EMPTY_STRING; else { MethodEntity method = item.classEntity.findMethod(item.function); if (method == null) return Memory.CONST_EMPTY_STRING; return new StringMemory(method.getClazz().getName()); } } else return Memory.CONST_EMPTY_STRING; } public void __defineFunction(TraceInfo trace, String moduleInternalName, int index) { ModuleEntity module = scope.moduleIndexMap.get(moduleInternalName); if (module == null) throw new CriticalException("Cannot find module: " + moduleInternalName); FunctionEntity function = module.findFunction(index); if (functionMap.put(function.getLowerName(), function) != null) { triggerError(new FatalException( Messages.ERR_CANNOT_REDECLARE_FUNCTION.fetch(function.getName()), trace )); } } public String __shellExecute(String s) { if (shellExecHandler != null) return shellExecHandler.onExecute(s); return ""; } public void die(Memory value) { if (value != null) { if (!value.isNumber()) echo(value.toString()); throw new DieException(value); } else throw new DieException(Memory.NULL); } public Memory invokeMethod(TraceInfo trace, IObject object, String name, Memory... args) throws Throwable { return ObjectInvokeHelper.invokeMethod(new ObjectMemory(object), name, name.toLowerCase(), this, trace, args); } public Memory invokeMethod(IObject object, String name, Memory... args) throws Throwable { return ObjectInvokeHelper.invokeMethod(new ObjectMemory(object), name, name.toLowerCase(), this, trace(), args); } public Memory invokeMethodNoThrow(IObject object, String name, Memory... args) { try { return invokeMethod(object, name, args); } catch (RuntimeException e) { throw e; } catch (Exception e) { this.catchUncaught(e); return Memory.NULL; } catch (Throwable e) { throw new RuntimeException(e); } } public Memory invokeMethod(TraceInfo trace, Memory object, String name, Memory... args) throws Throwable { return ObjectInvokeHelper.invokeMethod(object, name, name.toLowerCase(), this, trace, args); } public Memory invokeMethod(Memory object, String name, Memory... args) throws Throwable { return ObjectInvokeHelper.invokeMethod(object, name, name.toLowerCase(), this, trace(), args); } public String getLateStatic() { CallStackItem item = peekCall(0); if (item == null || item.clazz == null) return ""; else return item.staticClazz != null ? item.staticClazz : item.clazz; } public IObject getLateObject() { CallStackItem item = peekCall(0); if (item == null) return null; else return item.object; } public ClassEntity getLateStaticClass() { CallStackItem item = peekCall(0); if (item == null || item.clazz == null) return null; else { if (item.staticClassEntity != null) return item.staticClassEntity; if (item.object instanceof Closure) { Memory self = ((Closure) item.object).getSelf(); if (self.isObject()) return self.toValue(ObjectMemory.class).getReflection(); else return null; } return item.staticClassEntity = fetchClass(item.staticClazz != null ? item.staticClazz : item.clazz, false); } } public String getContext() { CallStackItem item = peekCall(1); return item == null ? "" : item.clazz == null ? "" : item.clazz; } public ClassEntity __getContextClass(int offset) { CallStackItem item = peekCall(offset); if (item == null || item.clazz == null) return null; else { if (item.classEntity != null) return item.classEntity; ClassEntity e = item.classEntity = fetchClass(item.clazz, false); if (e == null) throw new IllegalStateException("Cannot find '" + item.clazz + "' in the current environment"); return e; } } public ClassEntity getContextClass() { return __getContextClass(1); } public ClassEntity getLastClassOnStack() { return getLastClassOnStack(false); } public ClassEntity getLastClassOnStack(boolean includeClosures) { int N = getCallStackTop(); for (int i = 0; i < N; i++) { CallStackItem item = peekCall(i); if (item != null && item.clazz != null) { if (item.classEntity != null) { if (item.object instanceof Closure) { Memory self = ((Closure) item.object).getSelf(); if (self.isObject()) return self.toValue(ObjectMemory.class).getReflection(); else return null; } return item.classEntity; } ClassEntity e = item.classEntity = fetchClass(item.clazz, false); if (e == null) throw new IllegalStateException("Cannot find '" + item.clazz + "' in the current environment"); return e; } } return null; } public ClassEntity __getParentClass(TraceInfo trace) { ClassEntity context = getLastClassOnStack(false); if (context == null) { error(trace, "Cannot access parent:: when no class scope is active"); return null; } ClassEntity parent = context.getParent(); if (parent == null) { error(trace, "Cannot access parent:: when current class scope has no parent"); return null; } return parent; } public String __getParent(TraceInfo trace) { return __getParentClass(trace).getName(); } public String __getParent(TraceInfo trace, String className) { ClassEntity o = fetchClass(className, true); if (o == null) { error(trace, ErrorType.E_ERROR, Messages.ERR_CLASS_NOT_FOUND, className); return null; } if (o.getParent() == null) { error(trace, "Cannot access parent:: when current class scope has no parent"); return null; } return o.getParent().getName(); } public void registerAutoloader(SplClassLoader classLoader, boolean prepend) { for (SplClassLoader loader : classLoaders) if (loader.equals(classLoader)) return; if (prepend) { classLoaders.add(0, classLoader); } else classLoaders.add(classLoader); } public List<SplClassLoader> getClassLoaders() { return classLoaders; } public boolean unRegisterAutoloader(SplClassLoader classLoader) { boolean result = false; Iterator<SplClassLoader> iterator = classLoaders.iterator(); while (iterator.hasNext()) { if (iterator.next().equals(classLoader)) { result = true; iterator.remove(); } } return result; } public void setErrorHandler(ErrorHandler handler) { previousErrorHandler = errorHandler; errorHandler = handler; } public TraceInfo getTraceAppliedSourceMap(TraceInfo trace) { if (trace == null) { return null; } if (trace.getFile() == null) { return trace; } if (trace instanceof SourceMappedTraceInfo) { return trace; } SourceMap sourceMap = sourceMaps.get(trace.getFileName()); if (sourceMap != null) { int sourceLine = sourceMap.getSourceLine(trace.getStartLine() + 1); if (sourceLine != trace.getStartLine() && sourceLine != -1) { trace = new SourceMappedTraceInfo( trace.getContext(), sourceLine - 1, trace.getEndLine(), trace.getStartPosition(), trace.getEndPosition() ); } } return trace; } public void applySourceMap(CallStackItem[] callStack) { if (sourceMaps.isEmpty()) { return; } for (CallStackItem stackItem : callStack) { stackItem.trace = getTraceAppliedSourceMap(stackItem.trace); } } public CallStack getCallStack() { return callStack; } public void __replaceCallStack(CallStack stack) { this.callStack = stack; } public void registerShutdownFunction(ShutdownHandler handler) { shutdownFunctions.add(handler); } static { configurationHandler = new HashMap<String, ConfigChangeHandler>(); configurationHandler.put("include_path", new ConfigChangeHandler() { @Override public void onChange(Environment env, Memory value) { if (value == null) env.setIncludePaths(Collections.<String>emptySet()); else { String[] files = StringUtils.split(value.toString(), Constants.PATH_SEPARATOR, 255); Set<String> paths = new HashSet<String>(); Collections.addAll(paths, files); env.setIncludePaths(paths); } } }); } }