Skip to content

Commit 9a4887f

Browse files
committed
Retain preserved Lua class methods safely
1 parent 781b39d commit 9a4887f

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
@@ -90,8 +90,11 @@ public void validate(Collection<CompilationUnit> toCheck) {
9090
guaranteedClassFieldInitCache.clear();
9191
moduleFieldCopiesCache.clear();
9292
moduleFieldCopiesIndexed = false;
93+
trveWrapperFuncs.clear();
94+
wrapperCalls.clear();
9395
NamePreservation.clearSyntheticMarkers(prog);
9496
runtimeNameIndex = NamePreservation.indexGlobals(prog);
97+
recomputeTrvePreservation();
9598

9699
lightValidation(toCheck);
97100

@@ -3678,19 +3681,11 @@ private void checkBannedFunctions(ExprFunctionCall e) {
36783681
return;
36793682
} else if (e.getArgs().get(1) instanceof ExprVarAccess) {
36803683
// Check if this is a two line hook... thanks Bribe
3681-
ExprVarAccess varAccess = (ExprVarAccess) e.getArgs().get(1);
3682-
@Nullable FunctionImplementation nearestFunc = e.attrNearestFuncDef();
3683-
WStatements fbody = nearestFunc.getBody();
3684-
if (e.getParent() instanceof StmtReturn && fbody.size() <= 4 && fbody.get(fbody.size() - 2).structuralEquals(e.getParent())) {
3685-
WParameters params = nearestFunc.getParameters();
3686-
if (params.size() == 4 && ((TypeExprSimple) params.get(0).getTyp()).getTypeName().equals("trigger")
3687-
&& ((TypeExprSimple) params.get(1).getTyp()).getTypeName().equals("string")
3688-
&& ((TypeExprSimple) params.get(2).getTyp()).getTypeName().equals("limitop")
3689-
&& ((TypeExprSimple) params.get(3).getTyp()).getTypeName().equals("real")) {
3690-
trveWrapperFuncs.add(nearestFunc.getName());
3691-
WLogger.info("found wrapper: " + nearestFunc.getName());
3692-
return;
3693-
}
3684+
String wrapper = trveWrapperName(e);
3685+
if (wrapper != null) {
3686+
trveWrapperFuncs.add(wrapper);
3687+
WLogger.info("found wrapper: " + wrapper);
3688+
return;
36943689
}
36953690
}
36963691
} else {
@@ -3731,6 +3726,66 @@ private void checkBannedFunctions(ExprFunctionCall e) {
37313726
}
37323727
}
37333728

3729+
private void recomputeTrvePreservation() {
3730+
prog.accept(new Element.DefaultVisitor() {
3731+
@Override
3732+
public void visit(ExprFunctionCall call) {
3733+
super.visit(call);
3734+
if (call.getFuncName().equals("TriggerRegisterVariableEvent") && call.getArgs().size() > 1) {
3735+
if (call.getArgs().get(1) instanceof ExprStringVal varName) {
3736+
preserveVariableName(varName.getValS());
3737+
} else if (call.getArgs().get(1) instanceof ExprVarAccess) {
3738+
String wrapper = trveWrapperName(call);
3739+
if (wrapper != null) {
3740+
trveWrapperFuncs.add(wrapper);
3741+
}
3742+
}
3743+
}
3744+
}
3745+
3746+
});
3747+
3748+
// Repeat the cheap call pass so calls which precede their wrapper declaration are covered.
3749+
prog.accept(new Element.DefaultVisitor() {
3750+
@Override
3751+
public void visit(ExprFunctionCall call) {
3752+
super.visit(call);
3753+
if (trveWrapperFuncs.contains(call.getFuncName())
3754+
&& call.getArgs().size() > 1
3755+
&& call.getArgs().get(1) instanceof ExprStringVal varName) {
3756+
preserveVariableName(varName.getValS());
3757+
}
3758+
}
3759+
});
3760+
}
3761+
3762+
private @Nullable String trveWrapperName(ExprFunctionCall e) {
3763+
@Nullable FunctionImplementation nearestFunc = e.attrNearestFuncDef();
3764+
if (nearestFunc == null) {
3765+
return null;
3766+
}
3767+
WStatements fbody = nearestFunc.getBody();
3768+
if (!(e.getParent() instanceof StmtReturn)
3769+
|| fbody.size() < 2
3770+
|| fbody.size() > 4
3771+
|| !fbody.get(fbody.size() - 2).structuralEquals(e.getParent())) {
3772+
return null;
3773+
}
3774+
WParameters params = nearestFunc.getParameters();
3775+
if (params.size() != 4
3776+
|| !(params.get(0).getTyp() instanceof TypeExprSimple triggerType)
3777+
|| !(params.get(1).getTyp() instanceof TypeExprSimple stringType)
3778+
|| !(params.get(2).getTyp() instanceof TypeExprSimple limitopType)
3779+
|| !(params.get(3).getTyp() instanceof TypeExprSimple realType)
3780+
|| !triggerType.getTypeName().equals("trigger")
3781+
|| !stringType.getTypeName().equals("string")
3782+
|| !limitopType.getTypeName().equals("limitop")
3783+
|| !realType.getTypeName().equals("real")) {
3784+
return null;
3785+
}
3786+
return nearestFunc.getName();
3787+
}
3788+
37343789
private void preserveVariableName(String variableName) {
37353790
runtimeNameIndex.preserve(variableName);
37363791
}

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)