Skip to content

Commit 1eb32b2

Browse files
committed
Handle preserved names across validation and Lua emission
1 parent de84a79 commit 1eb32b2

4 files changed

Lines changed: 114 additions & 17 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,9 @@ private void translateClass(ImClass c) {
10471047
// translate functions
10481048
for (ImFunction f : c.getFunctions()) {
10491049
translateFunc(f);
1050-
luaFunc.getFor(f).setName(uniqueName(c.getName() + "_" + f.getName()));
1050+
if (!NamePreservation.isPreserved(f)) {
1051+
luaFunc.getFor(f).setName(uniqueName(c.getName() + "_" + f.getName()));
1052+
}
10511053
}
10521054

10531055
createClassInitFunction(c, classVar, initMethod);

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

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
import de.peeeq.wurstscript.jassIm.ImFunction;
55
import de.peeeq.wurstscript.jassIm.ImVar;
66
import de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum;
7+
import de.peeeq.wurstscript.types.WurstType;
8+
import de.peeeq.wurstscript.types.WurstTypeArray;
9+
import de.peeeq.wurstscript.types.WurstTypeTuple;
10+
import org.eclipse.jdt.annotation.Nullable;
11+
12+
import java.util.ArrayList;
13+
import java.util.LinkedHashMap;
14+
import java.util.List;
15+
import java.util.Map;
716

817
/** Metadata for names which are part of the Warcraft III-facing API. */
918
public final class NamePreservation {
@@ -33,29 +42,73 @@ public static void preserve(ImFunction function) {
3342
* name-based side table. The marker remains attached to the AST definition and is copied to
3443
* the corresponding IM variable through its trace.
3544
*/
36-
public static void preserve(GlobalVarDef variable) {
37-
if (!variable.hasAnnotation(ANNOTATION)) {
38-
Annotation marker = Ast.Annotation(variable.getSource(),
39-
Ast.Identifier(variable.getSource(), ANNOTATION.substring(1)), Ast.Arguments());
40-
variable.getModifiers().add(marker);
45+
public static @Nullable Annotation preserve(GlobalVarDef variable) {
46+
if (variable.hasAnnotation(ANNOTATION)) {
47+
return null;
4148
}
49+
Annotation marker = Ast.Annotation(variable.getSource(),
50+
Ast.Identifier(variable.getSource(), ANNOTATION.substring(1)), Ast.Arguments());
51+
variable.getModifiers().add(marker);
52+
return marker;
4253
}
4354

4455
/**
45-
* Finds globals by their emitted runtime name, without consulting lexical name resolution.
46-
* This is needed for native APIs such as TriggerRegisterVariableEvent whose string argument
47-
* refers to the generated global name rather than a source-level variable access.
56+
* Resolves globals by their emitted runtime name, without consulting lexical name resolution.
57+
* The index is scoped to one validation run; the preservation marker itself remains attached to
58+
* the AST definition and is copied to the corresponding IM variables through their trace.
4859
*/
49-
public static void preserveGlobalWithRuntimeName(WurstModel model, String runtimeName) {
60+
public static RuntimeNameIndex indexGlobals(WurstModel model) {
61+
RuntimeNameIndex result = new RuntimeNameIndex();
5062
model.accept(new Element.DefaultVisitor() {
5163
@Override
5264
public void visit(GlobalVarDef variable) {
5365
super.visit(variable);
54-
if (runtimeName(variable).equals(runtimeName)) {
55-
preserve(variable);
56-
}
66+
String name = runtimeName(variable);
67+
result.add(name, variable);
68+
addTupleComponentNames(result, name, variable.attrTyp(), variable);
5769
}
5870
});
71+
return result;
72+
}
73+
74+
private static void addTupleComponentNames(RuntimeNameIndex index, String name, WurstType type,
75+
GlobalVarDef variable) {
76+
if (type instanceof WurstTypeArray array) {
77+
type = array.getBaseType();
78+
}
79+
if (!(type instanceof WurstTypeTuple tuple)) {
80+
return;
81+
}
82+
for (WParameter parameter : tuple.getTupleDef().getParameters()) {
83+
String componentName = name + "_" + parameter.getName();
84+
index.add(componentName, variable);
85+
addTupleComponentNames(index, componentName, parameter.attrTyp(), variable);
86+
}
87+
}
88+
89+
public static final class RuntimeNameIndex {
90+
private final Map<String, List<GlobalVarDef>> globalsByName = new LinkedHashMap<>();
91+
private final Map<GlobalVarDef, Annotation> syntheticMarkers = new LinkedHashMap<>();
92+
93+
private void add(String name, GlobalVarDef variable) {
94+
globalsByName.computeIfAbsent(name, ignored -> new ArrayList<>()).add(variable);
95+
}
96+
97+
public void preserve(String runtimeName) {
98+
for (GlobalVarDef variable : globalsByName.getOrDefault(runtimeName, List.of())) {
99+
Annotation marker = NamePreservation.preserve(variable);
100+
if (marker != null) {
101+
syntheticMarkers.put(variable, marker);
102+
}
103+
}
104+
}
105+
106+
public void clearSyntheticMarkers() {
107+
for (Map.Entry<GlobalVarDef, Annotation> entry : syntheticMarkers.entrySet()) {
108+
entry.getKey().getModifiers().remove(entry.getValue());
109+
}
110+
syntheticMarkers.clear();
111+
}
59112
}
60113

61114
private static String runtimeName(GlobalVarDef variable) {

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ private enum Phase { LIGHT, HEAVY }
6262
private final Map<ClassDef, Map<GlobalVarDef, Integer>> classVarInitOrderCache = new HashMap<>();
6363
private final Map<GlobalVarDef, Boolean> guaranteedClassFieldInitCache = new IdentityHashMap<>();
6464
private final Map<GlobalVarDef, List<GlobalVarDef>> moduleFieldCopiesCache = new IdentityHashMap<>();
65+
private NamePreservation.RuntimeNameIndex runtimeNameIndex;
6566
private boolean moduleFieldCopiesIndexed;
6667

6768
/**
@@ -89,6 +90,10 @@ public void validate(Collection<CompilationUnit> toCheck) {
8990
guaranteedClassFieldInitCache.clear();
9091
moduleFieldCopiesCache.clear();
9192
moduleFieldCopiesIndexed = false;
93+
if (runtimeNameIndex != null) {
94+
runtimeNameIndex.clearSyntheticMarkers();
95+
}
96+
runtimeNameIndex = NamePreservation.indexGlobals(prog);
9297

9398
lightValidation(toCheck);
9499

@@ -177,7 +182,7 @@ private void postChecks(Collection<CompilationUnit> toCheck) {
177182
for (FunctionCall call : wrapperCalls.get(wrapper)) {
178183
if (call.getArgs().size() > 1 && call.getArgs().get(1) instanceof ExprStringVal) {
179184
ExprStringVal varName = (ExprStringVal) call.getArgs().get(1);
180-
preserveVariableName(call, varName.getValS());
185+
preserveVariableName(varName.getValS());
181186
WLogger.info("keep: " + varName.getValS());
182187
} else {
183188
call.addError("Map contains TriggerRegisterVariableEvent with non-constant arguments. Can't be optimized.");
@@ -3670,7 +3675,7 @@ private void checkBannedFunctions(ExprFunctionCall e) {
36703675
if (e.getArgs().size() > 1) {
36713676
if (e.getArgs().get(1) instanceof ExprStringVal) {
36723677
ExprStringVal varName = (ExprStringVal) e.getArgs().get(1);
3673-
preserveVariableName(e, varName.getValS());
3678+
preserveVariableName(varName.getValS());
36743679
WLogger.info("keep: " + varName.getValS());
36753680
return;
36763681
} else if (e.getArgs().get(1) instanceof ExprVarAccess) {
@@ -3728,8 +3733,8 @@ private void checkBannedFunctions(ExprFunctionCall e) {
37283733
}
37293734
}
37303735

3731-
private void preserveVariableName(Element useSite, String variableName) {
3732-
NamePreservation.preserveGlobalWithRuntimeName(prog, variableName);
3736+
private void preserveVariableName(String variableName) {
3737+
runtimeNameIndex.preserve(variableName);
37333738
}
37343739

37353740
private boolean isViableSwitchtype(Expr expr) {

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,43 @@ public void trvePreservesGlobalDespiteLexicalShadow() throws IOException {
550550
"Expected TRVE to preserve the global despite a local shadow.\n" + output);
551551
}
552552

553+
@Test
554+
public void trvePreservesLoweredTupleComponent() throws IOException {
555+
test().optimize().lines(
556+
"type trigger extends handle",
557+
"type event extends handle",
558+
"type limitop extends handle",
559+
"package test",
560+
" tuple pair(real x, real y)",
561+
" pair value = pair(0., 0.)",
562+
" @extern native TriggerRegisterVariableEvent(trigger whichTrigger, string varName, limitop opcode, real limitval) returns event",
563+
" init",
564+
" TriggerRegisterVariableEvent(null, \"test_value_x\", null, 0.0)",
565+
"endpackage");
566+
567+
String output = Files.toString(
568+
new File("./test-output/OptimizerTests_trvePreservesLoweredTupleComponent_opt.j"),
569+
Charsets.UTF_8);
570+
assertTrue(output.contains("real test_value_x"),
571+
"Expected TRVE to preserve the lowered tuple component.\n" + output);
572+
}
573+
574+
@Test
575+
public void preserveNameAnnotationKeepsClassFunctionNameInLua() throws IOException {
576+
test().testLua(true).luaOnly(true).executeProg(false).lines(
577+
"package test",
578+
" class ExternalApi",
579+
" @preserveName function callback()",
580+
" skip",
581+
"endpackage");
582+
583+
String output = Files.toString(
584+
new File("./test-output/lua/OptimizerTests_preserveNameAnnotationKeepsClassFunctionNameInLua.lua"),
585+
Charsets.UTF_8);
586+
assertTrue(output.contains("function ExternalApi_callback"),
587+
"Expected a preserved class function to keep its emitted name.\n" + output);
588+
}
589+
553590
@Test
554591
public void test_tempVarRemover() throws IOException {
555592
test().lines(

0 commit comments

Comments
 (0)