package php.runtime.lang.exception; import php.runtime.Memory; import php.runtime.annotation.Reflection; import php.runtime.annotation.Reflection.BaseType; import php.runtime.annotation.Reflection.Name; import php.runtime.annotation.Reflection.Signature; import php.runtime.common.HintType; import php.runtime.common.Modifier; import php.runtime.env.CallStackItem; import php.runtime.env.Environment; import php.runtime.env.TraceInfo; import php.runtime.exceptions.JPHPException; import php.runtime.lang.BaseException; import php.runtime.lang.IObject; import php.runtime.memory.ArrayMemory; import php.runtime.memory.StringMemory; import php.runtime.reflection.ClassEntity; import java.lang.ref.WeakReference; abstract public class BaseBaseException extends RuntimeException implements IObject, JPHPException { protected final ArrayMemory props; protected ClassEntity clazz; protected WeakReference<Environment> env; protected TraceInfo trace; protected CallStackItem[] callStack; private boolean init = true; private boolean isFinalized = false; private String nativeMessage = null; public BaseBaseException(String message) { this((Environment) null); nativeMessage = message; } public BaseBaseException(Environment env){ this(env, null); clazz = env == null ? null : env.fetchClass(getClass()); } public BaseBaseException(Environment env, ClassEntity clazz) { this.clazz = clazz; this.props = new ArrayMemory(); this.env = env == null ? null : new WeakReference<>(env); } public void setTraceInfo(Environment env, TraceInfo trace) { this.callStack = env.getCallStackSnapshot(); this.trace = trace; this.init = false; } @Override public ClassEntity getReflection() { return clazz; } @Override public ArrayMemory getProperties() { if (!init){ init = true; if (trace != null){ Memory m; m = clazz.refOfProperty(props, "file"); if (m.isNull()) m.assign(trace.getFileName()); m = clazz.refOfProperty(props, "line"); if (m.isNull()) m.assign(trace.getStartLine() + 1); m = clazz.refOfProperty(props, "position"); if (m.isNull()) m.assign(trace.getStartPosition() + 1); ArrayMemory backTrace = new ArrayMemory(); for(CallStackItem el : callStack) backTrace.add(el.toArray()); clazz.refOfProperty(props, "trace").assign(backTrace); } } return props; } @Override final public int getPointer() { return super.hashCode(); } @Override public boolean isMock() { return clazz == null; } @Override public void setAsMock() { clazz = null; } public CallStackItem[] getCallStack() { Environment env = getEnvironment(); if (env != null) { env.applySourceMap(callStack); } return callStack; } public TraceInfo getTrace() { Environment env = getEnvironment(); if (env != null) { return env.getTraceAppliedSourceMap(trace); } return trace == null ? TraceInfo.UNKNOWN : trace; } @Signature public Memory getMessage(Environment env, Memory... args) { if (nativeMessage != null) { return StringMemory.valueOf(nativeMessage); } return clazz.refOfProperty(getProperties(), "message").toValue(); } @Signature public Memory getCode(Environment env, Memory... args){ return clazz.refOfProperty(getProperties(), "code").toValue(); } @Signature public Memory getLine(Environment env, Memory... args){ return clazz.refOfProperty(getProperties(), "line").toValue(); } @Signature public Memory getPosition(Environment env, Memory... args){ return clazz.refOfProperty(getProperties(), "position").toValue(); } @Signature public Memory getFile(Environment env, Memory... args){ return clazz.refOfProperty(getProperties(), "file").toValue(); } @Signature public Memory getTrace(Environment env, Memory... args){ return clazz.refOfProperty(getProperties(), "trace").toValue(); } @Signature public Memory getPrevious(Environment env, Memory... args) { return clazz.refOfProperty(getProperties(), "previous").toValue(); } @Signature public Memory __toString(Environment env, Memory... args){ StringBuilder sb = new StringBuilder(); sb.append("exception '") .append(clazz.getName()).append("' with message '") .append(clazz.refOfProperty(getProperties(), "message")) .append("' in ") .append(clazz.refOfProperty(getProperties(), "file")) .append(":").append(clazz.refOfProperty(getProperties(), "line")); sb.append("\nStack Trace:\n"); sb.append(getTraceAsString(env)); return new StringMemory(sb.toString()); } @Signature public Memory getTraceAsString(Environment env, Memory... args){ int i = 0; StringBuilder sb = new StringBuilder(); if (callStack != null){ for (CallStackItem e : getCallStack()){ if (i != 0) sb.append("\n"); sb.append("#").append(i).append(" ").append(e.toString(false)); i++; } if (i != 0) sb.append("\n"); sb.append("#").append(i).append(" {main}"); } return new StringMemory(sb.toString()); } /** * Since we override this method, no stacktrace is generated - much faster * @return always null */ @Override public Throwable fillInStackTrace() { return null; } @Override public Environment getEnvironment() { return env.get(); } @Override public boolean isFinalized() { return isFinalized; } @Override public void doFinalize() { isFinalized = true; } @Override public String toString() { if (clazz.methodMagicToString != null) { Environment environment = getEnvironment(); if (environment != null) { return environment.invokeMethodNoThrow(this, "__toString").toString(); } } return super.toString(); } }