diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/LuaNativeLowering.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/LuaNativeLowering.java index a312b706b..b0736dfb3 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/LuaNativeLowering.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/LuaNativeLowering.java @@ -233,8 +233,7 @@ public void visit(ImOperatorCall call) { if (call.getOp() == WurstOperator.PLUS && args.size() == 2 && TypesHelper.isStringType(args.get(0).attrTyp()) && TypesHelper.isStringType(args.get(1).attrTyp())) { - call.replaceBy(JassIm.ImFunctionCall(call.attrTrace(), translator.stringConcatFunc, - JassIm.ImTypeArguments(), args.copy(), false, CallType.NORMAL)); + call.replaceBy(callWithStacktrace(call.attrTrace(), translator.stringConcatFunc, args.copy())); } } }); @@ -362,12 +361,29 @@ public void visit(ImVarArrayAccess access) { if (ensureFunc == null) { return; } - access.replaceBy(JassIm.ImFunctionCall(access.attrTrace(), ensureFunc, - JassIm.ImTypeArguments(), JassIm.ImExprs(access.copy()), false, CallType.NORMAL)); + access.replaceBy(callWithStacktrace(access.attrTrace(), ensureFunc, JassIm.ImExprs(access.copy()))); } }); } + private static ImFunctionCall callWithStacktrace(de.peeeq.wurstscript.ast.Element trace, ImFunction f, ImExprs args) { + int stacktraceIndex = stacktraceParamIndex(f); + if (stacktraceIndex >= 0) { + args.add(stacktraceIndex, JassIm.ImStringVal("when calling " + f.getName() + + StackTraceInjector2.getCallPos(trace.attrErrorPos()))); + } + return JassIm.ImFunctionCall(trace, f, JassIm.ImTypeArguments(), args, false, CallType.NORMAL); + } + + private static int stacktraceParamIndex(ImFunction f) { + for (int i = 0; i < f.getParameters().size(); i++) { + if (StackTraceInjector2.STACK_POS_PARAM.equals(f.getParameters().get(i).getName())) { + return i; + } + } + return -1; + } + private static ImFunction ensureFunctionFor(ImType type, ImTranslator translator) { if (TypesHelper.isIntType(type)) { return translator.ensureIntFunc; diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java index 0f2d1ce52..56ae55bc8 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java @@ -34,6 +34,10 @@ private String compiledLua(String testName) throws IOException { private String compileOptimizedLua(String testName, String... lines) { RunArgs runArgs = new RunArgs().with("-lua", "-inline", "-localOptimizations"); + return compileLuaWithRunArgs(testName, runArgs, lines); + } + + private String compileLuaWithRunArgs(String testName, RunArgs runArgs, String... lines) { WurstGuiCliImpl gui = new WurstGuiCliImpl(); WurstCompilerJassImpl compiler = new WurstCompilerJassImpl(null, gui, null, runArgs); WurstModel model = parseFiles(Collections.emptyList(), @@ -490,6 +494,30 @@ public void optimizedMovedImHelpersHaveNoDanglingReferences() { assertTrue("repro must exercise real mod lowering", compiled.contains("__wurst_rawFmodReal")); } + /** + * Stacktrace injection runs before Lua-native lowering. The lowering pass + * introduces fresh calls to existing IM helpers, so those calls must carry + * the injected stacktrace argument when the helper signature already has it. + */ + @Test + public void stacktracedLuaLoweringPassesHelperStacktraceArguments() { + compileLuaWithRunArgs( + "LuaBackendAuditTests_stacktracedLuaLoweringPassesHelperStacktraceArguments", + new RunArgs().with("-lua", "-inline", "-localOptimizations", "-stacktraces"), + "package Test", + "native print(string message)", + "native I2S(int value) returns string", + "int array values", + "function readValue(int index) returns int", + " return values[index]", + "function join(string left, string right) returns string", + " return left + right", + "init", + " values[1] = 7", + " print(join(\"value=\", I2S(readValue(1))))" + ); + } + private void assertHelperDefinedWhenCalled(String compiled, String helperName) { int definitions = countOccurrences(compiled, "function " + helperName + "("); int calls = countOccurrences(compiled, helperName + "(") - definitions;