diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java index dc68b89f4..0099e46c3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/EvaluateExpr.java @@ -22,6 +22,10 @@ public class EvaluateExpr { + private static void mark(Element e, ProgramState globalState) { + globalState.setLastElement(e); + } + public static ILconst eval(ImBoolVal e, ProgramState globalState, LocalState localState) { return ILconstBool.instance(e.getValB()); } @@ -31,6 +35,8 @@ public static ILconst eval(ImFuncRef e, ProgramState globalState, LocalState loc } public static @Nullable ILconst eval(ImFunctionCall e, ProgramState globalState, LocalState localState) { + mark(e, globalState); + ImFunction f = e.getFunc(); ImExprs arguments = e.getArguments(); @@ -191,6 +197,7 @@ public static ILconst eval(ImVarArrayAccess e, ProgramState globalState, LocalSt ProgramState globalState, LocalState localState) { ILconstObject receiver = globalState.toObject(mc.getReceiver().evaluate(globalState, localState)); globalState.assertAllocated(receiver, mc.attrTrace()); + mark(mc, globalState); List args = mc.getArguments(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java index 312851fa4..1f546a952 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILInterpreter.java @@ -97,6 +97,9 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab if (args.length > 0 && args[0] instanceof ILconstObject) { receiverObj = (ILconstObject) args[0]; } + if (caller != null) { + globalState.setLastElement(caller); + } WPos pos = (caller != null) ? caller.attrTrace().attrErrorPos() : f.attrTrace().attrErrorPos(); globalState.pushStackframeWithTypes(f, receiverObj, args, pos, normalized); try { @@ -174,7 +177,9 @@ public static LocalState runFunc(ProgramState globalState, ImFunction f, @Nullab normalized.put(e.getKey(), rhs); } - // --- single push/pop responsibility --- + if (caller != null) { + globalState.setLastElement(caller); + } WPos pos = (caller != null) ? caller.attrTrace().attrErrorPos() : f.attrTrace().attrErrorPos(); globalState.pushStackframeWithTypes(f, receiverObj, args, pos, normalized); @@ -226,16 +231,36 @@ public static de.peeeq.wurstscript.ast.Element getTrace(ProgramState globalState public static String buildStacktrace(ProgramState globalState, Throwable e) { StringBuilder err = new StringBuilder(); + try { - WPos src = globalState.getLastStatement().attrTrace().attrSource(); - err.append("Trace : ").append(new File(src.getFile()).getName()).append(":").append(src.getLine()).append("\n"); - } catch (Exception _e) { + ILStackFrame top = globalState.getStackFrames().getStackFrames().isEmpty() + ? null + : globalState.getStackFrames().getStackFrames().get(0); + + WPos src = null; + if (top != null) { + src = top.getCurrentSourcePos(); + } + if (src == null && globalState.getLastStatement() != null) { + src = globalState.getLastStatement().attrTrace().attrSource(); + } + + if (src != null) { + err.append("Trace : ") + .append(new File(src.getFile()).getName()) + .append(":") + .append(src.getLine()) + .append("\n"); + } + } catch (Exception ignored) { // ignore } + globalState.getStackFrames().appendTo(err); return err.toString(); } + @SuppressWarnings("null") private static ILconst adjustTypeOfConstant(@Nullable ILconst retVal, ImType expectedType) { if (retVal instanceof ILconstInt && isTypeReal(expectedType)) { diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java index 769692eb8..76629b25b 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ILStackFrame.java @@ -13,14 +13,17 @@ import java.util.Map; public class ILStackFrame { - public final Either f; public final ILconst[] args; public final WPos trace; public final @Nullable ILconstObject receiver; public final Map typeSubstitutions; - public ILStackFrame(ImFunction f, @Nullable ILconstObject receiver, ILconst[] args2, WPos trace, Map typeSubstitutions) { + // last executed element *within this frame* + public @Nullable de.peeeq.wurstscript.jassIm.Element currentElement; + + public ILStackFrame(ImFunction f, @Nullable ILconstObject receiver, ILconst[] args2, WPos trace, + Map typeSubstitutions) { this.f = Either.left(f); this.receiver = receiver; this.args = args2; @@ -36,35 +39,40 @@ public ILStackFrame(ImCompiletimeExpr f, WPos trace) { this.typeSubstitutions = Collections.emptyMap(); } + public @Nullable WPos getCurrentSourcePos() { + if (currentElement != null) { + try { + return currentElement.attrTrace().attrSource(); + } catch (Exception ignored) {} + } + return trace; + } + public String getMessage() { StringBuilder sb = new StringBuilder(); - if (trace != null && !trace.isArtificial()) { - String file = new File(trace.getFile()).getName(); - sb.append(" ╚ ").append(file).append(":").append(trace.getLine()); + + WPos pos = getCurrentSourcePos(); + if (pos != null && !pos.isArtificial()) { + String file = new File(pos.getFile()).getName(); + sb.append(" ╚ ").append(file).append(":").append(pos.getLine()); } + if (f.isLeft()) { - sb.append(" calling ").append(f.getLeft().getName()).append("("); - boolean first = true; - for (ILconst arg : args) { - if (!first) { - sb.append(", "); - } - sb.append(arg); - first = false; + sb.append(" inside call ").append(f.getLeft().getName()).append("("); + for (int i = 0; i < args.length; i++) { + if (i > 0) sb.append(", "); + sb.append(args[i]); } sb.append(")"); } else { sb.append("... when executing compiletime expression "); } - return sb.toString(); } public CompileError makeCompileError() { - return new CompileError(trace, getMessage()); - } - - public WPos getTrace() { - return trace; + // Use current element position if available: + WPos pos = getCurrentSourcePos(); + return new CompileError(pos != null ? pos : trace, getMessage()); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java index 7a9488c4b..8a0d45672 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/interpreter/ProgramState.java @@ -118,8 +118,18 @@ private void identifyGenericStaticGlobals() { } } + public void setLastElement(de.peeeq.wurstscript.jassIm.Element e) { + lastStatement = e; + + ILStackFrame top = stackFrames.peek(); + if (top != null) { + top.currentElement = e; + } + } + + // keep old API, delegate: public void setLastStatement(ImStmt s) { - lastStatement = s; + setLastElement(s); } public de.peeeq.wurstscript.jassIm.Element getLastStatement() {