Skip to content

Commit c14989c

Browse files
committed
Retain preserved Lua class methods safely
1 parent 8366b08 commit c14989c

3 files changed

Lines changed: 89 additions & 13 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/RemoveGarbage.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ private static void visitFunction(ImFunction f, Used used) {
222222
return;
223223
}
224224
used.addFunction(f);
225+
if (f.getParent() != null && f.getParent().getParent() instanceof ImClass owner) {
226+
// A preserved static method has no receiver type to retain its owner. Keep the class
227+
// because Lua emits class methods together with their class table.
228+
visitClass(owner, used, false);
229+
}
225230

226231
visitType(f.getReturnType(), used);
227232
f.accept(new Element.DefaultVisitor() {

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,11 @@ public void validate(Collection<CompilationUnit> toCheck) {
8787
if (runtimeNameIndex != null) {
8888
runtimeNameIndex.clearSyntheticMarkers();
8989
}
90+
trveWrapperFuncs.clear();
91+
wrapperCalls.clear();
9092
NamePreservation.clearSyntheticMarkers(prog);
9193
runtimeNameIndex = NamePreservation.indexGlobals(prog);
94+
recomputeTrvePreservation();
9295

9396
lightValidation(toCheck);
9497

@@ -3265,19 +3268,11 @@ private void checkBannedFunctions(ExprFunctionCall e) {
32653268
return;
32663269
} else if (e.getArgs().get(1) instanceof ExprVarAccess) {
32673270
// Check if this is a two line hook... thanks Bribe
3268-
ExprVarAccess varAccess = (ExprVarAccess) e.getArgs().get(1);
3269-
@Nullable FunctionImplementation nearestFunc = e.attrNearestFuncDef();
3270-
WStatements fbody = nearestFunc.getBody();
3271-
if (e.getParent() instanceof StmtReturn && fbody.size() <= 4 && fbody.get(fbody.size() - 2).structuralEquals(e.getParent())) {
3272-
WParameters params = nearestFunc.getParameters();
3273-
if (params.size() == 4 && ((TypeExprSimple) params.get(0).getTyp()).getTypeName().equals("trigger")
3274-
&& ((TypeExprSimple) params.get(1).getTyp()).getTypeName().equals("string")
3275-
&& ((TypeExprSimple) params.get(2).getTyp()).getTypeName().equals("limitop")
3276-
&& ((TypeExprSimple) params.get(3).getTyp()).getTypeName().equals("real")) {
3277-
trveWrapperFuncs.add(nearestFunc.getName());
3278-
WLogger.info("found wrapper: " + nearestFunc.getName());
3279-
return;
3280-
}
3271+
String wrapper = trveWrapperName(e);
3272+
if (wrapper != null) {
3273+
trveWrapperFuncs.add(wrapper);
3274+
WLogger.info("found wrapper: " + wrapper);
3275+
return;
32813276
}
32823277
}
32833278
} else {
@@ -3318,6 +3313,66 @@ private void checkBannedFunctions(ExprFunctionCall e) {
33183313
}
33193314
}
33203315

3316+
private void recomputeTrvePreservation() {
3317+
prog.accept(new Element.DefaultVisitor() {
3318+
@Override
3319+
public void visit(ExprFunctionCall call) {
3320+
super.visit(call);
3321+
if (call.getFuncName().equals("TriggerRegisterVariableEvent") && call.getArgs().size() > 1) {
3322+
if (call.getArgs().get(1) instanceof ExprStringVal varName) {
3323+
preserveVariableName(varName.getValS());
3324+
} else if (call.getArgs().get(1) instanceof ExprVarAccess) {
3325+
String wrapper = trveWrapperName(call);
3326+
if (wrapper != null) {
3327+
trveWrapperFuncs.add(wrapper);
3328+
}
3329+
}
3330+
}
3331+
}
3332+
3333+
});
3334+
3335+
// Repeat the cheap call pass so calls which precede their wrapper declaration are covered.
3336+
prog.accept(new Element.DefaultVisitor() {
3337+
@Override
3338+
public void visit(ExprFunctionCall call) {
3339+
super.visit(call);
3340+
if (trveWrapperFuncs.contains(call.getFuncName())
3341+
&& call.getArgs().size() > 1
3342+
&& call.getArgs().get(1) instanceof ExprStringVal varName) {
3343+
preserveVariableName(varName.getValS());
3344+
}
3345+
}
3346+
});
3347+
}
3348+
3349+
private @Nullable String trveWrapperName(ExprFunctionCall e) {
3350+
@Nullable FunctionImplementation nearestFunc = e.attrNearestFuncDef();
3351+
if (nearestFunc == null) {
3352+
return null;
3353+
}
3354+
WStatements fbody = nearestFunc.getBody();
3355+
if (!(e.getParent() instanceof StmtReturn)
3356+
|| fbody.size() < 2
3357+
|| fbody.size() > 4
3358+
|| !fbody.get(fbody.size() - 2).structuralEquals(e.getParent())) {
3359+
return null;
3360+
}
3361+
WParameters params = nearestFunc.getParameters();
3362+
if (params.size() != 4
3363+
|| !(params.get(0).getTyp() instanceof TypeExprSimple triggerType)
3364+
|| !(params.get(1).getTyp() instanceof TypeExprSimple stringType)
3365+
|| !(params.get(2).getTyp() instanceof TypeExprSimple limitopType)
3366+
|| !(params.get(3).getTyp() instanceof TypeExprSimple realType)
3367+
|| !triggerType.getTypeName().equals("trigger")
3368+
|| !stringType.getTypeName().equals("string")
3369+
|| !limitopType.getTypeName().equals("limitop")
3370+
|| !realType.getTypeName().equals("real")) {
3371+
return null;
3372+
}
3373+
return nearestFunc.getName();
3374+
}
3375+
33213376
private void preserveVariableName(String variableName) {
33223377
runtimeNameIndex.preserve(variableName);
33233378
}

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,22 @@ public void preserveNameAnnotationKeepsClassFunctionNameInLua() throws IOExcepti
587587
"Expected a preserved class function to keep its emitted name.\n" + output);
588588
}
589589

590+
@Test
591+
public void preserveNameAnnotationKeepsStaticClassFunctionReachableInLua() throws IOException {
592+
test().testLua(true).luaOnly(true).executeProg(false).lines(
593+
"package test",
594+
" class ExternalApi",
595+
" @preserveName static function callback()",
596+
" skip",
597+
"endpackage");
598+
599+
String output = Files.toString(
600+
new File("./test-output/lua/OptimizerTests_preserveNameAnnotationKeepsStaticClassFunctionReachableInLua.lua"),
601+
Charsets.UTF_8);
602+
assertTrue(output.contains("function callback("),
603+
"Expected a preserved static class function to keep its emitted name.\n" + output);
604+
}
605+
590606
@Test
591607
public void preservedClassFunctionReservesItsLuaNameBeforeClassVariables() throws IOException {
592608
test().testLua(true).luaOnly(true).executeProg(false).lines(

0 commit comments

Comments
 (0)