• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wurstscript / WurstScript / 271

29 Sep 2025 12:12PM UTC coverage: 64.649% (+2.4%) from 62.222%
271

Pull #1096

circleci

Frotty
Merge branch 'perf-improvements' of https://github.com/wurstscript/WurstScript into perf-improvements
Pull Request #1096: Perf improvements

18202 of 28155 relevant lines covered (64.65%)

0.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.9
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/ImTranslator.java
1
package de.peeeq.wurstscript.translation.imtranslation;
2

3
import com.google.common.base.Preconditions;
4
import com.google.common.collect.*;
5
import com.google.common.collect.ImmutableList.Builder;
6
import de.peeeq.datastructures.Partitions;
7
import de.peeeq.datastructures.TransitiveClosure;
8
import de.peeeq.wurstscript.RunArgs;
9
import de.peeeq.wurstscript.WLogger;
10
import de.peeeq.wurstscript.WurstOperator;
11
import de.peeeq.wurstscript.ast.*;
12
import de.peeeq.wurstscript.attributes.CompileError;
13
import de.peeeq.wurstscript.attributes.names.FuncLink;
14
import de.peeeq.wurstscript.attributes.names.NameLink;
15
import de.peeeq.wurstscript.attributes.names.PackageLink;
16
import de.peeeq.wurstscript.jassIm.*;
17
import de.peeeq.wurstscript.jassIm.Element;
18
import de.peeeq.wurstscript.parser.WPos;
19
import de.peeeq.wurstscript.types.*;
20
import de.peeeq.wurstscript.utils.Pair;
21
import de.peeeq.wurstscript.utils.Utils;
22
import de.peeeq.wurstscript.validation.TRVEHelper;
23
import de.peeeq.wurstscript.validation.WurstValidator;
24
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
25
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
26
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
27
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
28
import org.eclipse.jdt.annotation.Nullable;
29
import org.jetbrains.annotations.NotNull;
30

31
import java.util.*;
32
import java.util.function.Function;
33
import java.util.stream.Collectors;
34
import java.util.stream.Stream;
35

36
import static de.peeeq.wurstscript.jassIm.JassIm.*;
37
import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*;
38
import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath;
39

40
public class ImTranslator {
41

42

43
    public static final String $DEBUG_PRINT = "$debugPrint";
44

45
    private static final de.peeeq.wurstscript.ast.Element emptyTrace = Ast.NoExpr();
1✔
46

47
    // existing fields (keep callRelations as Guava Multimap to avoid ripple effects)
48
    private Multimap<ImFunction, ImFunction> callRelations;
49

50
    // swap these Sets to fastutil; keep accessors returning Set if you have them
51
    private ReferenceOpenHashSet<ImFunction> usedFunctions;
52
    private ObjectOpenHashSet<ImVar> usedVariables;  // or ReferenceOpenHashSet<ImVar> if identity semantics are intended
53
    private ObjectOpenHashSet<ImVar> readVariables;  // ditto
54

55
    private @Nullable ImFunction debugPrintFunction;
56

57
    private final Map<TranslatedToImFunction, ImFunction> functionMap = new Object2ObjectLinkedOpenHashMap<>();
1✔
58
    private @Nullable ImFunction globalInitFunc;
59

60
    private final ImProg imProg;
61

62
    final Map<WPackage, ImFunction> initFuncMap = new Object2ObjectLinkedOpenHashMap<>();
1✔
63

64
    private final Map<TranslatedToImFunction, ImVar> thisVarMap = new Object2ObjectLinkedOpenHashMap<>();
1✔
65

66
    private final Set<WPackage> translatedPackages = new ObjectLinkedOpenHashSet<>();
1✔
67
    private final Set<ClassDef> translatedClasses = new ObjectLinkedOpenHashSet<>();
1✔
68

69

70
    private final Map<VarDef, ImVar> varMap = new Object2ObjectLinkedOpenHashMap<>();
1✔
71

72
    private final WurstModel wurstProg;
73

74
    private @Nullable ImFunction mainFunc = null;
1✔
75

76
    private @Nullable ImFunction configFunc = null;
1✔
77

78
    @Nullable public ImFunction ensureIntFunc = null;
1✔
79
    @Nullable public ImFunction ensureBoolFunc = null;
1✔
80
    @Nullable public ImFunction ensureRealFunc = null;
1✔
81
    @Nullable public ImFunction ensureStrFunc = null;
1✔
82
    @Nullable public ImFunction stringConcatFunc = null;
1✔
83

84
    private final Map<ImVar, VarsForTupleResult> varsForTupleVar = new Object2ObjectLinkedOpenHashMap<>();
1✔
85

86
    private final boolean isUnitTestMode;
87

88
    private final ImVar lastInitFunc = JassIm.ImVar(emptyTrace, WurstTypeString.instance().imTranslateType(this), "lastInitFunc", false);
1✔
89

90
    private int compiletimeOrderCounter = 1;
1✔
91
    private final Map<TranslatedToImFunction, FunctionFlagCompiletime> compiletimeFlags = new HashMap<>();
1✔
92
    private final Map<ExprFunctionCall, Integer> compiletimeExpressionsOrder = new HashMap<>();
1✔
93

94
    de.peeeq.wurstscript.ast.Element lasttranslatedThing;
95
    private final boolean debug = false;
1✔
96
    private final RunArgs runArgs;
97

98
    public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArgs) {
1✔
99
        this.wurstProg = wurstProg;
1✔
100
        this.lasttranslatedThing = wurstProg;
1✔
101
        this.isUnitTestMode = isUnitTestMode;
1✔
102
        imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new Object2ObjectLinkedOpenHashMap<>());
1✔
103
        this.runArgs = runArgs;
1✔
104
    }
1✔
105

106

107
    /**
108
     * translates a program
109
     */
110
    public ImProg translateProg() {
111
        try {
112
            globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags());
1✔
113
            addFunction(getGlobalInitFunc());
1✔
114
            debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg",
1✔
115
                    false)), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
116

117
            if(isLuaTarget()) {
1✔
118
                ensureIntFunc = JassIm.ImFunction(emptyTrace, "intEnsure", ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeInt.instance().imTranslateType(this), "x", false)), WurstTypeInt.instance().imTranslateType(this), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
119
                ensureBoolFunc = JassIm.ImFunction(emptyTrace, "boolEnsure", ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeBool.instance().imTranslateType(this), "x", false)), WurstTypeBool.instance().imTranslateType(this), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
120
                ensureRealFunc = JassIm.ImFunction(emptyTrace, "realEnsure", ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeReal.instance().imTranslateType(this), "x", false)), WurstTypeReal.instance().imTranslateType(this), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
121
                ensureStrFunc = JassIm.ImFunction(emptyTrace, "stringEnsure", ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "x", false)), WurstTypeString.instance().imTranslateType(this), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
122
                stringConcatFunc =JassIm.ImFunction(emptyTrace, "stringConcat", ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "x", false),JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "y", false)), WurstTypeString.instance().imTranslateType(this), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
123
                addFunction(ensureIntFunc);
1✔
124
                addFunction(ensureBoolFunc);
1✔
125
                addFunction(ensureRealFunc);
1✔
126
                addFunction(ensureStrFunc);
1✔
127
                addFunction(stringConcatFunc);
1✔
128
            }
129

130
            calculateCompiletimeOrder();
1✔
131

132
            for (CompilationUnit cu : wurstProg) {
1✔
133
                translateCompilationUnit(cu);
1✔
134
            }
1✔
135

136
            if (mainFunc == null) {
1✔
137
                mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags());
1✔
138
                addFunction(mainFunc);
1✔
139
            }
140
            if (configFunc == null) {
1✔
141
                configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags());
1✔
142
                addFunction(configFunc);
1✔
143
            }
144

145
            finishInitFunctions();
1✔
146
            EliminateCallFunctionsWithAnnotation.process(imProg);
1✔
147
            removeDuplicateNatives(imProg);
1✔
148
            sortEverything();
1✔
149
            return imProg;
1✔
150
        } catch (CompileError t) {
1✔
151
            throw t;
1✔
152
        } catch (Throwable t) {
×
153
            WLogger.severe(t);
×
154
            throw new RuntimeException("There was a Wurst bug in the translation of "
×
155
                    + Utils.printElementWithSource(Optional.of(lasttranslatedThing))
×
156
                    + ": "
157
                    + t.getMessage()
×
158
                    + "\nPlease open a ticket with source code and the error log.", t);
159
        }
160
    }
161

162
    /**
163
     * Number all the compiletime functions and expressions,
164
     * so that the one with the lowest number can be executed first.
165
     * <p>
166
     * Dependendend packages are executed first and inside a package
167
     * it goes from top to bottom.
168
     */
169
    private void calculateCompiletimeOrder() {
170
        Set<WPackage> visited = new HashSet<>();
1✔
171
        ImmutableCollection<WPackage> packages = wurstProg.attrPackages().values();
1✔
172

173
        for (WPackage p : packages) {
1✔
174
            calculateCompiletimeOrder_walk(p, visited);
1✔
175
        }
1✔
176
    }
1✔
177

178
    private void calculateCompiletimeOrder_walk(WPackage p, Set<WPackage> visited) {
179
        if (!visited.add(p)) {
1✔
180
            return;
1✔
181
        }
182
        for (WPackage dep : p.attrInitDependencies()) {
1✔
183
            calculateCompiletimeOrder_walk(dep, visited);
1✔
184
        }
1✔
185
        p.accept(new de.peeeq.wurstscript.ast.Element.DefaultVisitor() {
1✔
186
            @Override
187
            public void visit(FuncDef funcDef) {
188
                super.visit(funcDef);
1✔
189
                if (funcDef.attrIsCompiletime()) {
1✔
190
                    compiletimeFlags.put(funcDef, new FunctionFlagCompiletime(compiletimeOrderCounter++));
1✔
191
                }
192
            }
1✔
193

194
            @Override
195
            public void visit(ExprFunctionCall fc) {
196
                super.visit(fc);
1✔
197
                if (fc.getFuncName().equals("compiletime")) {
1✔
198
                    compiletimeExpressionsOrder.put(fc, compiletimeOrderCounter++);
1✔
199
                }
200
            }
1✔
201
        });
202
    }
1✔
203

204

205
    /**
206
     * sorting everything is supposed to make the translation deterministic
207
     */
208
    private void sortEverything() {
209
        sortList(imProg.getClasses());
1✔
210
        sortList(imProg.getGlobals());
1✔
211
        sortList(imProg.getFunctions());
1✔
212
        for (ImClass c : imProg.getClasses()) {
1✔
213
            sortList(c.getFields());
1✔
214
            sortList(c.getMethods());
1✔
215
        }
1✔
216
    }
1✔
217

218

219
    private <T extends Element> void sortList(List<T> list) {
220
        List<T> classes = removeAll(list);
1✔
221
        Comparator<T> comparator = Comparator.comparing(this::getQualifiedClassName);
1✔
222
        classes.sort(comparator);
1✔
223
        list.addAll(classes);
1✔
224
    }
1✔
225

226
    public <T> List<T> removeAll(List<T> list) {
227
        List<T> result = new ArrayList<>();
1✔
228
        while (!list.isEmpty()) {
1✔
229
            result.add(0, list.remove(list.size() - 1));
1✔
230
        }
231
        return result;
1✔
232
    }
233

234
    private String getQualifiedClassName(Element c) {
235
        return getQualifiedClassName(c.attrTrace());
1✔
236
    }
237

238

239
    private String getQualifiedClassName(de.peeeq.wurstscript.ast.Element e) {
240
        String result = "";
1✔
241
        if (e instanceof NamedScope) {
1✔
242
            NamedScope ns = (NamedScope) e;
1✔
243
            result = ns.getName();
1✔
244
        }
245
        de.peeeq.wurstscript.ast.Element parent = e.getParent();
1✔
246
        if (parent == null) {
1✔
247
            return result;
1✔
248
        }
249
        parent = parent.attrNearestNamedScope();
1✔
250
        if (parent == null) {
1✔
251
            return result;
1✔
252
        }
253
        return getQualifiedClassName(parent) + "_" + result;
1✔
254
    }
255

256

257
    /***
258
     * this phase removes duplicate native declarations
259
     */
260
    private void removeDuplicateNatives(ImProg imProg) {
261
        Map<String, ImFunction> natives = new HashMap<>();
1✔
262
        Map<ImFunction, ImFunction> removed = new HashMap<>();
1✔
263
        ListIterator<ImFunction> it = imProg.getFunctions().listIterator();
1✔
264
        while (it.hasNext()) {
1✔
265
            ImFunction f = it.next();
1✔
266
            if (f.isNative() && natives.containsKey(f.getName())) {
1✔
267
                ImFunction existing = natives.get(f.getName());
1✔
268
                if (!compatibleTypes(f, existing)) {
1✔
269
                    throw new CompileError(f, "Native function definition conflicts with other native function defined in " +
×
270
                            existing.attrTrace().attrErrorPos());
×
271
                }
272
                // remove duplicate
273
                it.remove();
1✔
274
                removed.put(f, existing);
1✔
275
            } else {
1✔
276
                natives.put(f.getName(), f);
1✔
277
            }
278
        }
1✔
279
        // rewrite removed links
280
        imProg.accept(new ImProg.DefaultVisitor() {
1✔
281
            public void visit(ImFunctionCall e) {
282
                super.visit(e);
1✔
283
                if (removed.containsKey(e.getFunc())) {
1✔
284
                    e.setFunc(removed.get(e.getFunc()));
1✔
285
                }
286
            }
1✔
287

288
            public void visit(ImFuncRef e) {
289
                super.visit(e);
1✔
290
                if (removed.containsKey(e.getFunc())) {
1✔
291
                    e.setFunc(removed.get(e.getFunc()));
×
292
                }
293
            }
1✔
294
        });
295
    }
1✔
296

297

298
    /**
299
     * checks if two functions f and g have compatible types
300
     */
301
    private boolean compatibleTypes(ImFunction f, ImFunction g) {
302
        if (!f.getReturnType().equalsType(g.getReturnType())) {
1✔
303
            return false;
×
304
        }
305
        if (f.getParameters().size() != g.getParameters().size()) {
1✔
306
            return false;
×
307
        }
308
        for (int i = 0; i < f.getParameters().size(); i++) {
1✔
309
            if (!f.getParameters().get(i).getType().equalsType(g.getParameters().get(i).getType())) {
×
310
                return false;
×
311
            }
312
        }
313
        return true;
1✔
314
    }
315

316

317
    private ArrayList<FunctionFlag> flags(FunctionFlag... flags) {
318
        return Lists.newArrayList(flags);
1✔
319
    }
320

321

322
    private void translateCompilationUnit(CompilationUnit cu) {
323
        lasttranslatedThing = cu;
1✔
324
        // TODO can we make this smarter? Only translate functions which are actually called...
325
        for (WPackage p : cu.getPackages()) {
1✔
326
            lasttranslatedThing = p;
1✔
327
            p.imTranslateTLD(this);
1✔
328
        }
1✔
329
        for (JassToplevelDeclaration tld : cu.getJassDecls()) {
1✔
330
            lasttranslatedThing = tld;
1✔
331
            tld.imTranslateTLD(this);
1✔
332
        }
1✔
333
    }
1✔
334

335

336
    private void finishInitFunctions() {
337
        // init globals, at beginning of main func:
338
        getMainFunc().getBody().add(0, ImFunctionCall(emptyTrace, globalInitFunc, ImTypeArguments(), ImExprs(), false, CallType.NORMAL));
1✔
339

340

341
        for (ImFunction initFunc : initFuncMap.values()) {
1✔
342
            addFunction(initFunc);
1✔
343
        }
1✔
344
        Set<WPackage> calledInitializers = Sets.newLinkedHashSet();
1✔
345

346
        ImVar initTrigVar = prepareTrigger();
1✔
347

348
        for (WPackage p : Utils.sortByName(initFuncMap.keySet())) {
1✔
349
            callInitFunc(calledInitializers, p, initTrigVar);
1✔
350
        }
1✔
351

352
        ImFunction native_DestroyTrigger = getNativeFunc("DestroyTrigger");
1✔
353
        if (native_DestroyTrigger != null) {
1✔
354
            getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, ImTypeArguments(),
1✔
355
                    JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL));
1✔
356
        }
357
    }
1✔
358

359
    @NotNull
360
    private ImVar prepareTrigger() {
361
        ImVar initTrigVar = JassIm.ImVar(emptyTrace, JassIm.ImSimpleType("trigger"), "initTrig", false);
1✔
362
        getMainFunc().getLocals().add(initTrigVar);
1✔
363

364
        // initTrigVar = CreateTrigger()
365
        ImFunction createTrigger = getNativeFunc("CreateTrigger");
1✔
366
        if (createTrigger != null) {
1✔
367
            getMainFunc().getBody().add(ImSet(getMainFunc().getTrace(), ImVarAccess(initTrigVar), JassIm.ImFunctionCall(getMainFunc().getTrace(), getNativeFunc("CreateTrigger"), ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL)));
1✔
368
        }
369
        return initTrigVar;
1✔
370
    }
371

372

373
    private ImFunction getNativeFunc(String funcName) {
374
        ImmutableCollection<FuncLink> wurstFunc = wurstProg.lookupFuncs(funcName);
1✔
375
        if (wurstFunc.isEmpty()) {
1✔
376
            return null;
1✔
377
        }
378
        return getFuncFor(Utils.getFirst(wurstFunc).getDef());
1✔
379
    }
380

381
    private void callInitFunc(Set<WPackage> calledInitializers, WPackage p, ImVar initTrigVar) {
382
        Preconditions.checkNotNull(p);
1✔
383
        if (calledInitializers.contains(p)) {
1✔
384
            return;
1✔
385
        }
386
        calledInitializers.add(p);
1✔
387
        // first initialize all packages imported by this package:
388
        for (WPackage dep : p.attrInitDependencies()) {
1✔
389
            callInitFunc(calledInitializers, dep, initTrigVar);
1✔
390
        }
1✔
391
        ImFunction initFunc = initFuncMap.get(p);
1✔
392
        if (initFunc == null) {
1✔
393
            return;
1✔
394
        }
395
        if (initFunc.getBody().size() == 0) {
1✔
396
            return;
×
397
        }
398
        boolean successful = createInitFuncCall(p, initTrigVar, initFunc);
1✔
399

400
        if (!successful) {
1✔
401
            getMainFunc().getBody().add(ImFunctionCall(initFunc.getTrace(), initFunc, ImTypeArguments(), ImExprs(), false, CallType.NORMAL));
1✔
402
        }
403
    }
1✔
404

405

406
    private boolean createInitFuncCall(WPackage p, ImVar initTrigVar, ImFunction initFunc) {
407
        ImStmts mainBody = getMainFunc().getBody();
1✔
408

409
        ImFunction native_ClearTrigger = getNativeFunc("TriggerClearConditions");
1✔
410
        ImFunction native_TriggerAddCondition = getNativeFunc("TriggerAddCondition");
1✔
411
        ImFunction native_Condition = getNativeFunc("Condition");
1✔
412
        ImFunction native_TriggerEvaluate = getNativeFunc("TriggerEvaluate");
1✔
413
        ImFunction native_DisplayTimedTextToPlayer = getNativeFunc("DisplayTimedTextToPlayer");
1✔
414
        ImFunction native_GetLocalPlayer = getNativeFunc("GetLocalPlayer");
1✔
415

416
        if (native_ClearTrigger == null
1✔
417
                || native_TriggerAddCondition == null
418
                || native_Condition == null
419
                || native_TriggerEvaluate == null
420
                || native_DisplayTimedTextToPlayer == null
421
                || native_GetLocalPlayer == null
422
        ) {
423
            return false;
1✔
424
        }
425

426

427
        // rewrite init func to return boolean true:
428
        initFunc.setReturnType(WurstTypeBool.instance().imTranslateType(this));
1✔
429
        initFunc.accept(new ImFunction.DefaultVisitor() {
1✔
430
            @Override
431
            public void visit(ImReturn imReturn) {
432
                super.visit(imReturn);
×
433
                imReturn.setReturnValue(JassIm.ImBoolVal(true));
×
434
            }
×
435
        });
436
        de.peeeq.wurstscript.ast.Element trace = initFunc.getTrace();
1✔
437
        initFunc.getBody().add(JassIm.ImReturn(trace, JassIm.ImBoolVal(true)));
1✔
438

439

440
        // TriggerAddCondition(initTrigVar, Condition(function myInit))
441
        mainBody.add(ImFunctionCall(trace, native_TriggerAddCondition, ImTypeArguments(), JassIm.ImExprs(
1✔
442
                JassIm.ImVarAccess(initTrigVar),
1✔
443
                ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs(
1✔
444
                        JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL)
1✔
445
        ), true, CallType.NORMAL));
446
        // if not TriggerEvaluate(initTrigVar) ...
447
        mainBody.add(JassIm.ImIf(trace,
1✔
448
                JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(
1✔
449
                        ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)
1✔
450
                )),
451
                // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package")
452
                JassIm.ImStmts(
1✔
453
                    imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + "."))
1✔
454
                ),
455
                // else:
456
                JassIm.ImStmts()));
1✔
457
        mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL));
1✔
458
        return true;
1✔
459
    }
460

461
    private void addFunction(ImFunction f, StructureDef s) {
462
        ImClass c = getClassFor(s.attrNearestClassOrInterface());
1✔
463
        c.getFunctions().add(f);
1✔
464
    }
1✔
465

466
    private void addFunction(ImFunction f, TranslatedToImFunction funcDef) {
467
        ImClass classForFunc = getClassForFunc(funcDef);
1✔
468
        if (classForFunc != null) {
1✔
469
            classForFunc.getFunctions().add(f);
1✔
470
        } else {
471
            addFunction(f);
1✔
472
        }
473
    }
1✔
474

475
    private void addFunction(ImFunction f) {
476
        imProg.getFunctions().add(f);
1✔
477
    }
1✔
478

479
    public void addGlobal(ImVar v) {
480
        imProg.getGlobals().add(v);
1✔
481
    }
1✔
482

483

484
    public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarInitialization initialExpr) {
485
        if (initialExpr instanceof NoExpr) {
1✔
486
            // nothing to initialize
487
            return;
1✔
488
        }
489

490

491
        ImFunction f;
492
        if (packageOrGlobal instanceof WPackage) {
1✔
493
            WPackage p = (WPackage) packageOrGlobal;
1✔
494
            f = getInitFuncFor(p);
1✔
495
        } else {
1✔
496
            f = globalInitFunc;
1✔
497
        }
498
        de.peeeq.wurstscript.ast.Element trace = packageOrGlobal == null ? emptyTrace : packageOrGlobal;
1✔
499
        if (initialExpr instanceof Expr) {
1✔
500
            Expr expr = (Expr) initialExpr;
1✔
501
            ImExpr translated = expr.imTranslateExpr(this, f);
1✔
502
            ImSet imSet = ImSet(trace, ImVarAccess(v), translated);
1✔
503
            if (!v.getIsBJ()) {
1✔
504
                // add init statement for non-bj vars
505
                // bj-vars are already initalized by blizzard
506
                f.getBody().add(imSet);
1✔
507
            }
508
            imProg.getGlobalInits().put(v, Collections.singletonList(imSet));
1✔
509
        } else if (initialExpr instanceof ArrayInitializer) {
1✔
510
            ArrayInitializer arInit = (ArrayInitializer) initialExpr;
1✔
511
            List<ImExpr> translatedExprs = arInit.getValues().stream()
1✔
512
                    .map(expr -> expr.imTranslateExpr(this, f))
1✔
513
                    .collect(Collectors.toList());
1✔
514
            List<ImSet> imSets = new ArrayList<>();
1✔
515
            for (int i = 0; i < arInit.getValues().size(); i++) {
1✔
516
                ImExpr translated = translatedExprs.get(i);
1✔
517
                ImSet imSet = ImSet(trace, ImVarArrayAccess(trace, v, ImExprs(JassIm.ImIntVal(i))), translated);
1✔
518
                imSets.add(imSet);
1✔
519
            }
520
            f.getBody().addAll(imSets);
1✔
521
            // add list of init-values to translatedExprs
522
            imProg.getGlobalInits().put(v, imSets);
1✔
523
        }
524
    }
1✔
525

526
    public void addGlobalWithInitalizer(ImVar g, ImExpr initial) {
527
        imProg.getGlobals().add(g);
1✔
528
        ImSet imSet = ImSet(g.getTrace(), ImVarAccess(g), initial);
1✔
529
        getGlobalInitFunc().getBody().add(imSet);
1✔
530
        imProg.getGlobalInits().put(g, Collections.singletonList(imSet));
1✔
531
    }
1✔
532

533

534
    public ImExpr getDefaultValueForJassType(ImType type) {
535
        if (type instanceof ImSimpleType) {
1✔
536
            ImSimpleType imSimpleType = (ImSimpleType) type;
1✔
537
            return ImHelper.defaultValueForType(imSimpleType);
1✔
538
        } else if (type instanceof ImAnyType) {
1✔
539
            return JassIm.ImIntVal(0);
1✔
540
        } else if (type instanceof ImTupleType) {
×
541
            ImTupleType imTupleType = (ImTupleType) type;
×
542
            return getDefaultValueForJassType(imTupleType.getTypes().get(0));
×
543
        } else {
544
            throw new IllegalArgumentException("could not get default value for type " + type);
×
545
        }
546
    }
547

548
    public GetAForB<StructureDef, ImFunction> destroyFunc = new GetAForB<StructureDef, ImFunction>() {
1✔
549

550
        @Override
551
        public ImFunction initFor(StructureDef classDef) {
552
            ImVars params = ImVars(JassIm.ImVar(classDef, selfType(classDef), "this", false));
1✔
553

554
            ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags());
1✔
555
            addFunction(f, classDef);
1✔
556
            return f;
1✔
557
        }
558
    };
559

560
    public GetAForB<StructureDef, ImMethod> destroyMethod = new GetAForB<StructureDef, ImMethod>() {
1✔
561

562
        @Override
563
        public ImMethod initFor(StructureDef classDef) {
564
            ImFunction impl = destroyFunc.getFor(classDef);
1✔
565
            ImMethod m = JassIm.ImMethod(classDef, selfType(classDef), "destroy" + classDef.getName(),
1✔
566
                    impl, Lists.newArrayList(), false);
1✔
567
            return m;
1✔
568
        }
569
    };
570

571
    private ImType selfType(TranslatedToImFunction f) {
572
        return f.match(new TranslatedToImFunction.Matcher<ImType>() {
1✔
573
            @Override
574
            public ImType case_FuncDef(FuncDef f) {
575
                return selfType(f);
1✔
576
            }
577

578
            @Override
579
            public ImType case_ConstructorDef(ConstructorDef f) {
580
                return selfType(f.attrNearestClassOrInterface());
1✔
581
            }
582

583
            @Override
584
            public ImType case_NativeFunc(NativeFunc f) {
585
                throw new CompileError(f, "Cannot use 'this' here.");
×
586
            }
587

588
            @Override
589
            public ImType case_OnDestroyDef(OnDestroyDef f) {
590
                return selfType(f.attrNearestClassOrInterface());
1✔
591
            }
592

593
            @Override
594
            public ImType case_TupleDef(TupleDef f) {
595
                throw new CompileError(f, "Cannot use 'this' here.");
×
596
            }
597

598
            @Override
599
            public ImType case_ExprClosure(ExprClosure f) {
600
                return selfType(getClassForClosure(f));
1✔
601
            }
602

603
            @Override
604
            public ImType case_InitBlock(InitBlock f) {
605
                throw new CompileError(f, "Cannot use 'this' here.");
×
606
            }
607

608
            @Override
609
            public ImType case_ExtensionFuncDef(ExtensionFuncDef f) {
610
                return f.getExtendedType().attrTyp().imTranslateType(ImTranslator.this);
1✔
611
            }
612
        });
613
    }
614

615
    private ImClassType selfType(FuncDef f) {
616
        return selfType(f.attrNearestClassOrInterface());
1✔
617
    }
618

619
    public ImClassType selfType(StructureDef classDef) {
620
        ImClass imClass = getClassFor(classDef.attrNearestClassOrInterface());
1✔
621
        return selfType(imClass);
1✔
622
    }
623

624
    public ImClassType selfType(ImClass imClass) {
625
        ImTypeArguments typeArgs = JassIm.ImTypeArguments();
1✔
626
        for (ImTypeVar tv : imClass.getTypeVariables()) {
1✔
627
            typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap()));
1✔
628
        }
1✔
629
        return JassIm.ImClassType(imClass, typeArgs);
1✔
630
    }
631

632
    public GetAForB<ImClass, ImFunction> allocFunc = new GetAForB<ImClass, ImFunction>() {
1✔
633

634
        @Override
635
        public ImFunction initFor(ImClass c) {
636

637
            return ImFunction(c.getTrace(), "alloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(), TypesHelper.imInt(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList());
1✔
638
        }
639

640
    };
641

642
    public GetAForB<ImClass, ImFunction> deallocFunc = new GetAForB<ImClass, ImFunction>() {
1✔
643

644
        @Override
645
        public ImFunction initFor(ImClass c) {
646

647
            return ImFunction(c.getTrace(), "dealloc_" + c.getName(), ImTypeVars(), JassIm.ImVars(JassIm.ImVar(c.getTrace(), TypesHelper.imInt(), "obj", false)), TypesHelper.imVoid(), JassIm.ImVars(), JassIm.ImStmts(), Collections.emptyList());
1✔
648
        }
649

650
    };
651

652
    private final Map<ImTypeVar, TypeParamDef> typeVariableReverse = new HashMap<>();
1✔
653

654
    private final GetAForB<TypeParamDef, ImTypeVar> typeVariable = new GetAForB<TypeParamDef, ImTypeVar>() {
1✔
655

656
        @Override
657
        public ImTypeVar initFor(TypeParamDef a) {
658
            ImTypeVar v = JassIm.ImTypeVar(a.getName());
1✔
659
            typeVariableReverse.put(v, a);
1✔
660
            return v;
1✔
661
        }
662
    };
663

664

665
    public ImFunction getFuncFor(TranslatedToImFunction funcDef) {
666
        if (functionMap.containsKey(funcDef)) {
1✔
667
            return functionMap.get(funcDef);
1✔
668
        }
669
        String name = getNameFor(funcDef);
1✔
670
        List<FunctionFlag> flags = flags();
1✔
671
        if (funcDef instanceof NativeFunc) {
1✔
672
            flags.add(IS_NATIVE);
1✔
673
        }
674
        if (isBJ(funcDef.getSource())) {
1✔
675
            flags.add(IS_BJ);
1✔
676
        }
677
        if (isExtern(funcDef)) {
1✔
678
            flags.add(FunctionFlagEnum.IS_EXTERN);
1✔
679
        }
680
        if (funcDef instanceof FuncDef) {
1✔
681
            FuncDef funcDef2 = (FuncDef) funcDef;
1✔
682
            if (funcDef2.attrIsCompiletime()) {
1✔
683
                FunctionFlagCompiletime flag = compiletimeFlags.get(funcDef);
1✔
684
                if (flag == null) {
1✔
685
                    throw new CompileError(funcDef.getSource(), "Compiletime flag not supported here.");
×
686
                }
687
                flags.add(flag);
1✔
688
            }
689
            if (funcDef2.attrHasAnnotation("compiletimenative")) {
1✔
690
                flags.add(FunctionFlagEnum.IS_COMPILETIME_NATIVE);
1✔
691
            }
692
            if (funcDef2.attrHasAnnotation("test")) {
1✔
693
                flags.add(IS_TEST);
1✔
694
            }
695
        }
696

697
        // Check if last parameter is vararg
698
        if (funcDef instanceof AstElementWithParameters) {
1✔
699
            WParameters params = ((AstElementWithParameters) funcDef).getParameters();
1✔
700
            if (params.size() >= 1 && params.get(params.size() - 1).attrIsVararg()) {
1✔
701
                flags.add(IS_VARARG);
1✔
702
            }
703
        }
704

705

706
        if (funcDef instanceof HasModifier) {
1✔
707
            HasModifier awm = (HasModifier) funcDef;
1✔
708
            for (Modifier m : awm.getModifiers()) {
1✔
709
                if (m instanceof Annotation) {
1✔
710
                    Annotation annotation = (Annotation) m;
1✔
711
                    flags.add(new FunctionFlagAnnotation(annotation.getAnnotationType()));
1✔
712
                }
713
            }
1✔
714
        }
715

716
        ImTypeVars typeVars = collectTypeVarsForFunction(funcDef);
1✔
717
        ImFunction f = ImFunction(funcDef, name, typeVars, ImVars(), ImVoid(), ImVars(), ImStmts(), flags);
1✔
718
        funcDef.imCreateFuncSkeleton(this, f);
1✔
719

720
        addFunction(f, funcDef);
1✔
721
        functionMap.put(funcDef, f);
1✔
722
        return f;
1✔
723
    }
724

725
    private ImClass getClassForFunc(TranslatedToImFunction funcDef) {
726
        if (funcDef == null) {
1✔
727
            return null;
×
728
        }
729
        return funcDef.match(new TranslatedToImFunction.Matcher<ImClass>() {
1✔
730
            @Override
731
            public ImClass case_TupleDef(TupleDef tupleDef) {
732
                return null;
×
733
            }
734

735
            @Override
736
            public ImClass case_FuncDef(FuncDef funcDef) {
737
                if (funcDef.attrIsDynamicClassMember()) {
1✔
738
                    return getClassFor(funcDef.attrNearestClassOrInterface());
1✔
739
                }
740
                return null;
1✔
741
            }
742

743
            @Override
744
            public ImClass case_NativeFunc(NativeFunc nativeFunc) {
745
                return null;
1✔
746
            }
747

748
            @Override
749
            public ImClass case_OnDestroyDef(OnDestroyDef funcDef) {
750
                return getClassFor(funcDef.attrNearestClassOrInterface());
1✔
751
            }
752

753
            @Override
754
            public ImClass case_InitBlock(InitBlock initBlock) {
755
                return null;
×
756
            }
757

758
            @Override
759
            public ImClass case_ExtensionFuncDef(ExtensionFuncDef extensionFuncDef) {
760
                return null;
1✔
761
            }
762

763
            @Override
764
            public ImClass case_ConstructorDef(ConstructorDef funcDef) {
765
                return getClassFor(funcDef.attrNearestClassOrInterface());
1✔
766
            }
767

768
            @Override
769
            public ImClass case_ExprClosure(ExprClosure exprClosure) {
770
                return null;
1✔
771
            }
772
        });
773
    }
774

775
    private ImTypeVars collectTypeVarsForFunction(TranslatedToImFunction funcDef) {
776
        ImTypeVars typeVars = ImTypeVars();
1✔
777
        funcDef.match(new TranslatedToImFunction.MatcherVoid() {
1✔
778
            @Override
779
            public void case_FuncDef(FuncDef funcDef) {
780
                handleTypeParameters(funcDef.getTypeParameters());
1✔
781
            }
1✔
782

783

784
            private void handleTypeParameters(TypeParamDefs tps) {
785
                for (TypeParamDef tp : tps) {
1✔
786
                    handleTypeParameter(tp);
1✔
787
                }
1✔
788
            }
1✔
789

790
            private void handleTypeParameter(TypeParamDef tp) {
791
                if (tp.getTypeParamConstraints() instanceof TypeExprList) {
1✔
792
                    typeVars.add(typeVariable.getFor(tp));
1✔
793
                }
794
            }
1✔
795

796
            @Override
797
            public void case_ConstructorDef(ConstructorDef constructorDef) {
798
            }
×
799

800
            @Override
801
            public void case_NativeFunc(NativeFunc nativeFunc) {
802
            }
1✔
803

804
            @Override
805
            public void case_OnDestroyDef(OnDestroyDef onDestroyDef) {
806
            }
1✔
807

808
            @Override
809
            public void case_TupleDef(TupleDef tupleDef) {
810
            }
×
811

812
            @Override
813
            public void case_ExprClosure(ExprClosure exprClosure) {
814
                // TODO where to set closure parameters?
815
            }
1✔
816

817
            @Override
818
            public void case_InitBlock(InitBlock initBlock) {
819

820
            }
×
821

822
            @Override
823
            public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) {
824
                handleTypeParameters(funcDef.getTypeParameters());
1✔
825
            }
1✔
826
        });
827
        return typeVars;
1✔
828
    }
829

830

831
    private boolean isExtern(TranslatedToImFunction funcDef) {
832
        if (funcDef instanceof HasModifier) {
1✔
833
            HasModifier f = (HasModifier) funcDef;
1✔
834
            for (Modifier m : f.getModifiers()) {
1✔
835
                if (m instanceof Annotation) {
1✔
836
                    Annotation a = (Annotation) m;
1✔
837
                    if (a.getAnnotationType().equals("@extern")) {
1✔
838
                        return true;
1✔
839
                    }
840
                }
841
            }
1✔
842
        }
843
        return false;
1✔
844
    }
845

846

847
    private boolean isBJ(WPos source) {
848
        String f = source.getFile().toLowerCase();
1✔
849
        return f.endsWith("blizzard.j") || f.endsWith("common.j");
1✔
850
    }
851

852
    public ImFunction getInitFuncFor(WPackage p) {
853
        // TODO more precise trace
854
        return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()));
1✔
855
    }
856

857
    /**
858
     * returns a suitable name for the given element
859
     * the returned name is a valid jass identifier
860
     */
861
    public String getNameFor(de.peeeq.wurstscript.ast.Element e) {
862
        if (e instanceof FuncDef) {
1✔
863
            FuncDef f = (FuncDef) e;
1✔
864
            if (f.attrNearestStructureDef() != null) {
1✔
865
                return getNameFor(f.attrNearestStructureDef()) + "_" + f.getName();
1✔
866
            }
867
        } else if (e instanceof ExtensionFuncDef) {
1✔
868
            ExtensionFuncDef f = (ExtensionFuncDef) e;
1✔
869
            return getNameFor(f.getExtendedType()) + "_" + f.getName();
1✔
870
        } else if (e instanceof TypeExprSimple) {
1✔
871
            TypeExprSimple t = (TypeExprSimple) e;
1✔
872
            return t.getTypeName();
1✔
873
        } else if (e instanceof TypeExprThis) {
1✔
874
            return "thistype";
×
875
        } else if (e instanceof TypeExprArray) {
1✔
876
            TypeExprArray t = (TypeExprArray) e;
×
877
            return getNameFor(t.getBase()) + "Array";
×
878
        } else if (e instanceof ModuleInstanciation) {
1✔
879
            ModuleInstanciation mi = (ModuleInstanciation) e;
1✔
880
            return getNameFor(mi.getParent().attrNearestNamedScope()) + "_" + mi.getName();
1✔
881
        }
882

883

884
        if (e instanceof AstElementWithNameId) {
1✔
885
            AstElementWithNameId wn = (AstElementWithNameId) e;
1✔
886
            return wn.getNameId().getName();
1✔
887
        } else if (e instanceof ConstructorDef) {
1✔
888
            return "new_" + e.attrNearestClassDef().getName();
×
889
        } else if (e instanceof OnDestroyDef) {
1✔
890
            return "ondestroy_" + e.attrNearestClassDef().getName();
1✔
891
        } else if (e instanceof ExprClosure) {
1✔
892
            return e.attrNearestNamedScope().getName() + "_closure";
1✔
893
        }
894
        throw new RuntimeException("unhandled case: " + e.getClass().getName());
×
895
    }
896

897
    public ImVar getThisVar(TranslatedToImFunction f) {
898
        if (f instanceof OnDestroyDef) {
1✔
899
            // special case for onDestroy defs
900
            // TODO also special case for constructors needed?
901
            OnDestroyDef od = (OnDestroyDef) f;
1✔
902
            if (od.getParent() instanceof ModuleInstanciation) {
1✔
903
                ModuleInstanciation mi = (ModuleInstanciation) od.getParent();
1✔
904
                ClassDef c = mi.attrNearestClassDef();
1✔
905
                f = c.getOnDestroy();
1✔
906
            }
907
        }
908
        if (thisVarMap.containsKey(f)) {
1✔
909
            return thisVarMap.get(f);
1✔
910
        }
911
        ImVar v = JassIm.ImVar(f, selfType(f), "this", false);
1✔
912
        thisVarMap.put(f, v);
1✔
913
        return v;
1✔
914
    }
915

916
    public ImVar getThisVar(ImFunction f, ExprThis e) {
917
        return getThisVarForNode(f, e);
1✔
918
    }
919

920
    public ImVar getThisVar(ImFunction f, ExprSuper e) {
921
        return getThisVarForNode(f, e);
×
922
    }
923

924
    private ImVar getThisVarForNode(ImFunction f, de.peeeq.wurstscript.ast.Element node1) {
925
        de.peeeq.wurstscript.ast.Element node = node1;
1✔
926
        while (node != null) {
1✔
927
            if (node instanceof TranslatedToImFunction && !(node instanceof ExprClosure)) {
1✔
928
                return getThisVar((TranslatedToImFunction) node);
1✔
929
            }
930
            node = node.getParent();
1✔
931
        }
932
        if (f.getParameters().isEmpty()) {
1✔
933
            throw new CompileError(node1.attrSource(), "Could not get 'this'.");
×
934
        }
935
        return f.getParameters().get(0);
1✔
936
    }
937

938

939
    public int getTupleIndex(TupleDef tupleDef, VarDef parameter) {
940
        int i = 0;
×
941
        for (WParameter p : tupleDef.getParameters()) {
×
942
            if (p == parameter) {
×
943
                return i;
×
944
            }
945
            i++;
×
946
        }
×
947
        throw new Error("");
×
948
    }
949

950

951
    public ImVar getVarFor(VarDef varDef) {
952
        ImVar v = varMap.get(varDef);
1✔
953
        if (v == null) {
1✔
954
            ImType type = varDef.attrTyp().imTranslateType(this);
1✔
955
            String name = varDef.getName();
1✔
956
            if (isNamedScopeVar(varDef)) {
1✔
957
                name = getNameFor(varDef.attrNearestNamedScope()) + "_" + name;
1✔
958
            }
959
            boolean isBj = isBJ(varDef.getSource());
1✔
960
            v = JassIm.ImVar(varDef, type, name, isBj);
1✔
961
            varMap.put(varDef, v);
1✔
962
        }
963
        return v;
1✔
964
    }
965

966
    private boolean isNamedScopeVar(VarDef varDef) {
967
        if (varDef.getParent() == null) {
1✔
968
            return false;
×
969
        }
970
        return varDef.getParent().getParent() instanceof NamedScope;
1✔
971
    }
972

973
    public WurstModel getWurstProg() {
974
        return wurstProg;
×
975
    }
976

977
    public ImProg imProg() {
978
        return imProg;
1✔
979
    }
980

981
    public boolean isTranslated(WPackage pack) {
982
        return translatedPackages.contains(pack);
1✔
983
    }
984

985
    public void setTranslated(WPackage pack) {
986
        translatedPackages.add(pack);
1✔
987
    }
1✔
988

989
    public boolean isTranslated(ClassDef c) {
990
        return translatedClasses.contains(c);
1✔
991
    }
992

993
    public void setTranslated(ClassDef c) {
994
        translatedClasses.add(c);
1✔
995
    }
1✔
996

997
    public List<ImStmt> translateStatements(ImFunction f, List<WStatement> statements) {
998
        List<ImStmt> result = Lists.newArrayList();
1✔
999
        for (WStatement s : statements) {
1✔
1000
            lasttranslatedThing = s;
1✔
1001
            ImStmt translated = s.imTranslateStmt(this, f);
1✔
1002
            result.add(translated);
1✔
1003
        }
1✔
1004
        return result;
1✔
1005
    }
1006

1007
    public void setMainFunc(ImFunction f) {
1008
        if (mainFunc != null) {
1✔
1009
            throw new Error("mainFunction already set");
×
1010
        }
1011
        mainFunc = f;
1✔
1012
    }
1✔
1013

1014
    public void setConfigFunc(ImFunction f) {
1015
        if (configFunc != null) {
1✔
1016
            throw new Error("configFunction already set");
×
1017
        }
1018
        configFunc = f;
1✔
1019
    }
1✔
1020

1021
    public Multimap<ImFunction, ImFunction> getCalledFunctions() {
1022
        if (callRelations == null) {
1✔
1023
            calculateCallRelationsAndUsedVariables();
×
1024
        }
1025
        return callRelations;
1✔
1026
    }
1027

1028

1029

1030
    public void calculateCallRelationsAndUsedVariables() {
1031
        // estimate sizes to reduce rehashing
1032
        final int funcEstimate = Math.max(16, imProg.getFunctions().size());
1✔
1033
        final int varEstimate  = Math.max(32, imProg.getGlobals().size());
1✔
1034

1035
        callRelations = com.google.common.collect.LinkedHashMultimap.create(); // keep Guava type externally
1✔
1036

1037
        usedFunctions = new ReferenceOpenHashSet<>(funcEstimate);
1✔
1038
        usedVariables = new ObjectOpenHashSet<>(varEstimate);
1✔
1039
        readVariables = new ObjectOpenHashSet<>(varEstimate);
1✔
1040

1041
        final ImFunction main = getMainFunc();
1✔
1042
        if (main != null) calculateCallRelations(main);
1✔
1043

1044
        final ImFunction conf = getConfFunc();
1✔
1045
        if (conf != null && conf != main) calculateCallRelations(conf);
1✔
1046

1047
        // mark protected globals as read
1048
        // TRVEHelper.protectedVariables is presumably a HashSet<String> (O(1) contains)
1049
        for (ImVar global : imProg.getGlobals()) {
1✔
1050
            if (TRVEHelper.protectedVariables.contains(global.getName())) {
1✔
1051
                readVariables.add(global);
1✔
1052
            }
1053
        }
1✔
1054
    }
1✔
1055

1056
    private void calculateCallRelations(ImFunction rootFunction) {
1057
        // nothing to do
1058
        if (rootFunction == null) return;
1✔
1059

1060
        // if already processed, skip entirely
1061
        if (usedFunctions.contains(rootFunction)) return;
1✔
1062

1063
        final ArrayDeque<ImFunction> work = new ArrayDeque<>();
1✔
1064
        work.add(rootFunction);
1✔
1065

1066
        while (!work.isEmpty()) {
1✔
1067
            final ImFunction f = work.removeLast(); // LIFO (DFS); change to removeFirst() for BFS
1✔
1068

1069
            if (!usedFunctions.add(f)) {
1✔
1070
                // was already processed; skip
1071
                continue;
1✔
1072
            }
1073

1074
            // Only computed once per function thanks to usedFunctions.add() gate
1075
            usedVariables.addAll(f.calcUsedVariables());
1✔
1076
            readVariables.addAll(f.calcReadVariables());
1✔
1077

1078
            final Set<ImFunction> called = f.calcUsedFunctions();
1✔
1079
            // Avoid streams/alloc; avoid pushing functions we've already seen
1080
            for (ImFunction g : called) {
1✔
1081
                if (g == null) continue;
1✔
1082
                if (g != f) { // ignore self-calls in relation
1✔
1083
                    callRelations.put(f, g);
1✔
1084
                }
1085
                if (!usedFunctions.contains(g)) {
1✔
1086
                    work.add(g);
1✔
1087
                }
1088
            }
1✔
1089
        }
1✔
1090
    }
1✔
1091

1092
    private Multimap<ImFunction, ImFunction> getCallRelations() {
1093
        return callRelations;
×
1094
    }
1095

1096
    public ImFunction getMainFunc() { return mainFunc; }
1✔
1097
    public ImFunction getConfFunc() { return configFunc; }
1✔
1098

1099

1100

1101
    /**
1102
     * returns a list of classes and functions implementing funcDef
1103
     */
1104
    public Map<ClassDef, FuncDef> getClassesWithImplementation(Collection<ClassDef> instances, FuncDef func) {
1105
        if (func.attrIsPrivate()) {
1✔
1106
            // private functions cannot be overridden
1107
            return Collections.emptyMap();
1✔
1108
        }
1109
        Map<ClassDef, FuncDef> result = Maps.newLinkedHashMap();
1✔
1110
        for (ClassDef c : instances) {
1✔
1111
            FuncLink funcNameLink = null;
1✔
1112
            WurstTypeClass cType = c.attrTypC();
1✔
1113
            for (FuncLink nameLink : func.lookupMemberFuncs(cType, func.getName())) {
1✔
1114
                if (nameLink.getDef() == func) {
1✔
1115
                    funcNameLink = nameLink;
1✔
1116
                }
1117
            }
1✔
1118
            if (funcNameLink == null) {
1✔
1119
                throw new Error("Could not find "
×
1120
                    + Utils.printElementWithSource(Optional.of(func))
×
1121
                    + " in "
1122
                    + Utils.printElementWithSource(Optional.of(c)));
×
1123
            }
1124
            for (NameLink nameLink : c.attrNameLinks().get(func.getName())) {
1✔
1125
                NameDef nameDef = nameLink.getDef();
1✔
1126
                if (nameLink.getDefinedIn() == c) {
1✔
1127
                    if (nameLink instanceof FuncLink && nameLink.getDef() instanceof FuncDef) {
1✔
1128
                        FuncLink funcLink = (FuncLink) nameLink;
1✔
1129
                        FuncDef f = (FuncDef) funcLink.getDef();
1✔
1130
                        // check if function f overrides func
1131
                        if (WurstValidator.canOverride(funcLink, funcNameLink, false)) {
1✔
1132
                            result.put(c, f);
1✔
1133
                        }
1134
                    }
1135
                }
1136
            }
1✔
1137
        }
1✔
1138
        return result;
1✔
1139
    }
1140

1141

1142
    private final Map<ClassDef, List<Pair<ImVar, VarInitialization>>> classDynamicInitMap = Maps.newLinkedHashMap();
1✔
1143
    private final Map<ClassDef, List<WStatement>> classInitStatements = Maps.newLinkedHashMap();
1✔
1144

1145
    public List<Pair<ImVar, VarInitialization>> getDynamicInits(ClassDef c) {
1146
        return classDynamicInitMap.computeIfAbsent(c, k -> Lists.newArrayList());
1✔
1147
    }
1148

1149

1150
    private final Map<ConstructorDef, ImFunction> constructorFuncs = Maps.newLinkedHashMap();
1✔
1151

1152
    public ImFunction getConstructFunc(ConstructorDef constr) {
1153
        ImFunction f = constructorFuncs.get(constr);
1✔
1154
        if (f == null) {
1✔
1155
            String name = constructorName(constr);
1✔
1156
            ImVars params = ImVars(getThisVar(constr));
1✔
1157
            for (WParameter p : constr.getParameters()) {
1✔
1158
                params.add(getVarFor(p));
1✔
1159
            }
1✔
1160

1161
            f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), flags());
1✔
1162
            addFunction(f, constr);
1✔
1163
            constructorFuncs.put(constr, f);
1✔
1164
        }
1165
        return f;
1✔
1166
    }
1167

1168

1169
    private String constructorName(ConstructorDef constr) {
1170
        ArrayDeque<String> names = new ArrayDeque<>();
1✔
1171
        de.peeeq.wurstscript.ast.Element e = constr;
1✔
1172
        while (e != null) {
1✔
1173
            if (e instanceof ClassOrModuleInstanciation) {
1✔
1174
                ClassOrModuleInstanciation mi = (ClassOrModuleInstanciation) e;
1✔
1175
                int index = mi.getConstructors().indexOf(constr);
1✔
1176
                names.addFirst(mi.getName() + (index > 0 ? 1 + index : ""));
1✔
1177
                if (e instanceof ClassDef) {
1✔
1178
                    break;
1✔
1179
                }
1180
            }
1181
            e = e.getParent();
1✔
1182
        }
1183
        return "construct_" + String.join("_", names);
1✔
1184
    }
1185

1186

1187
    Map<ConstructorDef, ImFunction> constrNewFuncs = Maps.newLinkedHashMap();
1✔
1188

1189
    public ImFunction getConstructNewFunc(ConstructorDef constr) {
1190
        ImFunction f = constrNewFuncs.get(constr);
1✔
1191
        if (f == null) {
1✔
1192
            String name = "new_" + constr.attrNearestClassDef().getName();
1✔
1193

1194
            f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags());
1✔
1195
            addFunction(f, constr);
1✔
1196
            constrNewFuncs.put(constr, f);
1✔
1197
        }
1198
        return f;
1✔
1199
    }
1200

1201
    public ImProg getImProg() {
1202
        return imProg;
1✔
1203
    }
1204

1205

1206
    private Multimap<InterfaceDef, ClassDef> interfaceInstances = null;
1✔
1207

1208
    public Collection<ClassDef> getInterfaceInstances(InterfaceDef interfaceDef) {
1209
        if (interfaceInstances == null) {
1✔
1210
            calculateInterfaceInstances();
1✔
1211
        }
1212
        return interfaceInstances.get(interfaceDef);
1✔
1213
    }
1214

1215
    private void calculateInterfaceInstances() {
1216
        interfaceInstances = HashMultimap.create();
1✔
1217
        for (CompilationUnit cu : wurstProg) {
1✔
1218
            for (ClassDef c : cu.attrGetByType().classes) {
1✔
1219
                for (WurstTypeInterface i : c.attrTypC().transitiveSuperInterfaces()) {
1✔
1220
                    interfaceInstances.put(i.getDef(), c);
1✔
1221
                }
1✔
1222
            }
1✔
1223
        }
1✔
1224
    }
1✔
1225

1226

1227
    private TransitiveClosure<ClassDef> subclasses = null;
1✔
1228
    private Multimap<ClassDef, ClassDef> directSubclasses = null;
1✔
1229

1230
    private boolean isEclipseMode = false;
1✔
1231

1232
    public List<ClassDef> getSubClasses(ClassDef classDef) {
1233
        calculateSubclasses();
1✔
1234
        return subclasses.getAsList(classDef);
1✔
1235
    }
1236

1237
    private void calculateSubclasses() {
1238
        if (subclasses != null) {
1✔
1239
            return;
1✔
1240
        }
1241
        calculateDirectSubclasses();
1✔
1242
        subclasses = new TransitiveClosure<>(directSubclasses);
1✔
1243
    }
1✔
1244

1245

1246
    private void calculateDirectSubclasses() {
1247
        if (directSubclasses != null) {
1✔
1248
            return;
×
1249
        }
1250
        directSubclasses = HashMultimap.create();
1✔
1251
        for (ClassDef c : classes()) {
1✔
1252
            WurstTypeClass extendedClass = c.attrTypC().extendedClass();
1✔
1253
            if (extendedClass != null) {
1✔
1254
                directSubclasses.put(extendedClass.getDef(), c);
1✔
1255
            }
1256
        }
1✔
1257
    }
1✔
1258

1259
    /**
1260
     * calculates list of all classes
1261
     * ignoring the ones in modules, only module instantiations
1262
     */
1263
    private List<ClassDef> classes() {
1264
        List<ClassDef> result = new ArrayList<>();
1✔
1265
        for (CompilationUnit cu : wurstProg) {
1✔
1266
            for (WPackage p : cu.getPackages()) {
1✔
1267
                for (WEntity e : p.getElements()) {
1✔
1268
                    if (e instanceof ClassDef) {
1✔
1269
                        ClassDef c = (ClassDef) e;
1✔
1270
                        classesAdd(result, c);
1✔
1271
                    }
1272
                }
1✔
1273
            }
1✔
1274
        }
1✔
1275
        return result;
1✔
1276

1277
    }
1278

1279
    private void classesAdd(List<ClassDef> result, ClassOrModuleInstanciation c) {
1280
        if (c instanceof ClassDef) {
1✔
1281
            result.add(((ClassDef) c));
1✔
1282
        }
1283
        for (ClassDef ic : c.getInnerClasses()) {
1✔
1284
            classesAdd(result, ic);
1✔
1285
        }
1✔
1286
        for (ModuleInstanciation mi : c.getModuleInstanciations()) {
1✔
1287
            classesAdd(result, mi);
1✔
1288
        }
1✔
1289
    }
1✔
1290

1291
    public int getEnumMemberId(EnumMember enumMember) {
1292
        return ((EnumMembers) enumMember.getParent()).indexOf(enumMember);
1✔
1293
    }
1294

1295
    private ImFunction getDebugPrintFunction() {
1296
        return debugPrintFunction;
1✔
1297
    }
1298

1299
    public boolean isEclipseMode() {
1300
        return isEclipseMode;
1✔
1301
    }
1302

1303
    public void setEclipseMode(boolean enabled) {
1304
        isEclipseMode = enabled;
×
1305
    }
×
1306

1307
    public TypeParamDef getTypeParamDef(ImTypeVar tv) {
1308
        return typeVariableReverse.get(tv);
1✔
1309
    }
1310

1311
    public ImTypeVar getTypeVar(TypeParamDef tv) {
1312
        return typeVariable.getFor(tv);
1✔
1313
    }
1314

1315
    public boolean isLuaTarget() {
1316
        return runArgs.isLua();
1✔
1317
    }
1318

1319
    interface VarsForTupleResult {
1320

1321
        default Iterable<ImVar> allValues() {
1322
            return allValuesStream()::iterator;
1✔
1323
        }
1324

1325
        Stream<ImVar> allValuesStream();
1326

1327
        <T> T map(Function<Stream<T>, T> nodeBuilder, Function<ImVar, T> leafBuilder);
1328
    }
1329

1330
    static class SingleVarResult implements VarsForTupleResult {
1331
        private final ImVar var;
1332

1333
        public SingleVarResult(ImVar var) {
1✔
1334
            this.var = var;
1✔
1335
        }
1✔
1336

1337
        public ImVar getVar() {
1338
            return var;
×
1339
        }
1340

1341
        @Override
1342
        public Stream<ImVar> allValuesStream() {
1343
            return Stream.of(var);
1✔
1344
        }
1345

1346
        @Override
1347
        public <T> T map(Function<Stream<T>, T> nodeBuilder, Function<ImVar, T> leafBuilder) {
1348
            return leafBuilder.apply(var);
1✔
1349
        }
1350

1351
        @Override
1352
        public String toString() {
1353
            return var.toString();
×
1354
        }
1355
    }
1356

1357
    static class TupleResult implements VarsForTupleResult {
1358
        private final List<VarsForTupleResult> items;
1359

1360
        public TupleResult(List<VarsForTupleResult> items) {
1✔
1361
            this.items = items;
1✔
1362
        }
1✔
1363

1364
        public List<VarsForTupleResult> getItems() {
1365
            return items;
×
1366
        }
1367

1368
        @Override
1369
        public Stream<ImVar> allValuesStream() {
1370
            return items.stream().flatMap(VarsForTupleResult::allValuesStream);
1✔
1371
        }
1372

1373
        @Override
1374
        public <T> T map(Function<Stream<T>, T> nodeBuilder, Function<ImVar, T> leafBuilder) {
1375
            return nodeBuilder.apply(items.stream().map(e -> e.map(nodeBuilder, leafBuilder)));
1✔
1376
        }
1377

1378
        @Override
1379
        public String toString() {
1380
            return "<" + Utils.printSep(", ", items) + ">";
×
1381
        }
1382
    }
1383

1384
    public VarsForTupleResult getVarsForTuple(ImVar v) {
1385
        // TODO use list instead of tree
1386
        VarsForTupleResult result = varsForTupleVar.get(v);
1✔
1387
        if (result == null) {
1✔
1388
            if (TypesHelper.typeContainsTuples(v.getType())) {
1✔
1389
                result = createVarsForType(v.getName(), v.getType(), Function.identity(), v.getTrace());
1✔
1390
            } else {
1391
                result = new SingleVarResult(v);
×
1392
            }
1393
            varsForTupleVar.put(v, result);
1✔
1394
        }
1395
        return result;
1✔
1396
    }
1397

1398

1399
    /**
1400
     * Creates variables for the given type, eliminating tuple types
1401
     *
1402
     * @param name            base name for the variables
1403
     * @param type            the type for which to create variables
1404
     * @param typeConstructor how the types are constructed (creating an array or multi-array, just returning the type)
1405
     * @param tr              trace for the variables
1406
     * @return
1407
     */
1408
    private VarsForTupleResult createVarsForType(String name, final ImType type, Function<ImType, ImType> typeConstructor, de.peeeq.wurstscript.ast.Element tr) {
1409
        return type.match(new ImType.Matcher<VarsForTupleResult>() {
1✔
1410
            @Override
1411
            public VarsForTupleResult case_ImArrayType(ImArrayType at) {
1412
                if (at.getEntryType() instanceof ImTupleType) {
1✔
1413
                    // if it is an array of tuples, create multiple array variables:
1414
                    ImTupleType tt = (ImTupleType) at.getEntryType();
1✔
1415
                    Builder<VarsForTupleResult> ts = ImmutableList.builder();
1✔
1416
                    int i = 0;
1✔
1417
                    for (ImType t : tt.getTypes()) {
1✔
1418
                        ts.add(createVarsForType(name + "_" + tt.getNames().get(i), t, JassIm::ImArrayType, tr));
1✔
1419
                        i++;
1✔
1420
                    }
1✔
1421
                    return new TupleResult(ts.build());
1✔
1422
                }
1423
                // otherwise just create the array variable
1424
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1425
            }
1426

1427
            @Override
1428
            public VarsForTupleResult case_ImTypeVarRef(ImTypeVarRef imTypeVarRef) {
1429
                throw new RuntimeException("Should be called after eliminating generics.");
×
1430
            }
1431

1432
            @Override
1433
            public VarsForTupleResult case_ImArrayTypeMulti(ImArrayTypeMulti at) {
1434
                if (at.getEntryType() instanceof ImTupleType) {
1✔
1435
                    // if it is an array of tuples, create multiple array variables:
1436
                    ImTupleType tt = (ImTupleType) at.getEntryType();
1✔
1437
                    Builder<VarsForTupleResult> ts = ImmutableList.builder();
1✔
1438
                    int i = 0;
1✔
1439
                    for (ImType t : tt.getTypes()) {
1✔
1440
                        ts.add(createVarsForType(name + "_" + tt.getNames().get(i), t, et -> JassIm.ImArrayTypeMulti(et, new ArrayList<>(at.getArraySize())), tr));
1✔
1441
                        i++;
1✔
1442
                    }
1✔
1443
                    return new TupleResult(ts.build());
1✔
1444
                }
1445
                // otherwise just create the array variable
1446
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1447
            }
1448

1449
            @Override
1450
            public VarsForTupleResult case_ImVoid(ImVoid imVoid) {
1451
                return new TupleResult(Collections.emptyList());
×
1452
            }
1453

1454
            @Override
1455
            public VarsForTupleResult case_ImClassType(ImClassType st) {
1456
                ImType type = typeConstructor.apply(st);
×
1457
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1458
            }
1459

1460
            @Override
1461
            public VarsForTupleResult case_ImSimpleType(ImSimpleType st) {
1462
                ImType type = typeConstructor.apply(st);
1✔
1463
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
1✔
1464
            }
1465

1466
            @Override
1467
            public VarsForTupleResult case_ImAnyType(ImAnyType at) {
1468
                ImType type = typeConstructor.apply(at);
×
1469
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1470
            }
1471

1472
            @Override
1473
            public VarsForTupleResult case_ImTupleType(ImTupleType tt) {
1474
                int i = 0;
1✔
1475
                Builder<VarsForTupleResult> ts = ImmutableList.builder();
1✔
1476
                for (ImType t : tt.getTypes()) {
1✔
1477
                    ts.add(createVarsForType(name + "_" + tt.getNames().get(i), t, typeConstructor, tr));
1✔
1478
                    i++;
1✔
1479
                }
1✔
1480
                return new TupleResult(ts.build());
1✔
1481
            }
1482
        });
1483
    }
1484

1485

1486
    private void addVarsForType(List<ImVar> result, String name, ImType type, de.peeeq.wurstscript.ast.Element tr) {
1487
        Preconditions.checkNotNull(type);
×
1488
        Preconditions.checkNotNull(result);
×
1489
        // TODO handle names
1490
        if (type instanceof ImTupleType) {
×
1491
            ImTupleType tt = (ImTupleType) type;
×
1492
            int i = 0;
×
1493
            for (ImType t : tt.getTypes()) {
×
1494
                addVarsForType(result, name + "_" + tt.getNames().get(i), t, tr);
×
1495
                i++;
×
1496
            }
×
1497
        } else if (type instanceof ImVoid) {
×
1498
            // nothing to add
1499
        } else {
1500
            result.add(JassIm.ImVar(tr, type, name, false));
×
1501
        }
1502

1503
    }
×
1504

1505
    private final Map<ImFunction, VarsForTupleResult> tempReturnVars = Maps.newLinkedHashMap();
1✔
1506

1507
    public VarsForTupleResult getTupleTempReturnVarsFor(ImFunction f) {
1508
        VarsForTupleResult result = tempReturnVars.get(f);
1✔
1509
        if (result == null) {
1✔
1510
            result = createVarsForType(f.getName() + "_return", getOriginalReturnValue(f), Function.identity(), f.getTrace());
1✔
1511
            for (ImVar value : result.allValues()) {
1✔
1512
                imProg.getGlobals().add(value);
1✔
1513
            }
1✔
1514
            tempReturnVars.put(f, result);
1✔
1515
        }
1516
        return result;
1✔
1517
    }
1518

1519
    private final Map<ImFunction, ImType> originalReturnValues = Maps.newLinkedHashMap();
1✔
1520

1521

1522
    public void setOriginalReturnValue(ImFunction f, ImType t) {
1523
        originalReturnValues.put(f, t);
1✔
1524
    }
1✔
1525

1526
    public ImType getOriginalReturnValue(ImFunction f) {
1527
        return originalReturnValues.computeIfAbsent(f, ImFunction::getReturnType);
1✔
1528
    }
1529

1530
    public void assertProperties(AssertProperty... properties1) {
1531
        if (!debug) {
1532
            return;
1✔
1533
        }
1534
        final Set<AssertProperty> properties = Sets.newHashSet(properties1);
1535
        assertProperties(properties, imProg);
1536
    }
1537

1538
    public void assertProperties(Set<AssertProperty> properties, Element e) {
1539
        if (e instanceof ElementWithVar) {
1✔
1540
            checkVar(((ElementWithVar) e).getVar(), properties);
1✔
1541
        }
1542
        properties.parallelStream().forEach(p -> p.check(e));
1✔
1543
        if (properties.contains(AssertProperty.NOTUPLES)) {
1✔
1544
            // TODO ?
1545
        }
1546
        if (properties.contains(AssertProperty.FLAT)) {
1✔
1547
            // TODO ?
1548
        }
1549
        for (int i = 0; i < e.size(); i++) {
1✔
1550
            Element child = e.get(i);
1✔
1551
            if (child.getParent() == null) {
1✔
1552
                throw new Error("Child " + i + " (" + child + ") of " + e + " not attached to tree");
×
1553
            } else if (child.getParent() != e) {
1✔
1554
                throw new Error("Child " + i + " (" + child + ") of " + e + " attached to wrong tree");
×
1555
            }
1556
            assertProperties(properties, child);
1✔
1557
        }
1558
    }
1✔
1559

1560
    private void checkVar(ImVar left, Set<AssertProperty> properties) {
1561
        if (left.getParent() == null) {
1✔
1562
            throw new Error("var not attached: " + left);
×
1563
        }
1564
        if (properties.contains(AssertProperty.NOTUPLES)) {
1✔
1565
            if (TypesHelper.typeContainsTuples(left.getType())) {
×
1566
                throw new Error("program contains tuple var " + left);
×
1567
            }
1568
        }
1569
    }
1✔
1570

1571
    public Set<ImVar> getUsedVariables() {
1572
        if (usedVariables == null) {
×
1573
            calculateCallRelationsAndUsedVariables();
×
1574
        }
1575
        return usedVariables;
×
1576
    }
1577

1578
    public Set<ImVar> getReadVariables() {
1579
        if (readVariables == null) {
1✔
1580
            calculateCallRelationsAndUsedVariables();
×
1581
        }
1582
        return readVariables;
1✔
1583
    }
1584

1585
    public Set<ImFunction> getUsedFunctions() {
1586
        if (usedFunctions == null) {
1✔
1587
            calculateCallRelationsAndUsedVariables();
×
1588
        }
1589
        return usedFunctions;
1✔
1590
    }
1591

1592
    public boolean isUnitTestMode() {
1593
        return isUnitTestMode;
1✔
1594
    }
1595

1596
    private final Map<ExprClosure, ImClass> classForClosure = Maps.newLinkedHashMap();
1✔
1597

1598
    public ImClass getClassForClosure(ExprClosure s) {
1599
        Preconditions.checkNotNull(s);
1✔
1600
        return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()));
1✔
1601
    }
1602

1603

1604
    private final Map<ClassOrInterface, @Nullable ImClass> classForStructureDef = Maps.newLinkedHashMap();
1✔
1605

1606
    public ImClass getClassFor(ClassOrInterface s) {
1607
        Preconditions.checkNotNull(s);
1✔
1608
        return classForStructureDef.computeIfAbsent(s, s1 -> {
1✔
1609
            ImTypeVars typeVariables = JassIm.ImTypeVars();
1✔
1610
            if (s instanceof AstElementWithTypeParameters) {
1✔
1611
                for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) {
1✔
1612
                    if (tp.getTypeParamConstraints() instanceof TypeExprList) {
1✔
1613
                        ImTypeVar tv = getTypeVar(tp);
1✔
1614
                        typeVariables.add(tv);
1✔
1615
                    }
1616
                }
1✔
1617
            }
1618

1619
            return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList());
1✔
1620
        });
1621
    }
1622

1623

1624
    Map<FuncDef, ImMethod> methodForFuncDef = Maps.newLinkedHashMap();
1✔
1625

1626
    public ImMethod getMethodFor(FuncDef f) {
1627
        ImMethod m = methodForFuncDef.get(f);
1✔
1628
        if (m == null) {
1✔
1629
            ImFunction imFunc = getFuncFor(f);
1✔
1630
            m = JassIm.ImMethod(f, selfType(f), elementNameWithPath(f), imFunc, Lists.newArrayList(), false);
1✔
1631
            methodForFuncDef.put(f, m);
1✔
1632
        }
1633
        return m;
1✔
1634
    }
1635

1636
    public ClassManagementVars getClassManagementVarsFor(ImClass c) {
1637
        return getClassManagementVars().get(c);
1✔
1638
    }
1639

1640

1641
    private Map<ImClass, ClassManagementVars> classManagementVars = null;
1✔
1642

1643
    private @Nullable ImFunction errorFunc;
1644

1645
    public Map<ImClass, ClassManagementVars> getClassManagementVars() {
1646
        if (classManagementVars != null) {
1✔
1647
            return classManagementVars;
1✔
1648
        }
1649
        // create partitions, such that each sub-class and super-class are in
1650
        // the same partition
1651
        Partitions<ImClass> p = new Partitions<>();
1✔
1652
        for (ImClass c : imProg.getClasses()) {
1✔
1653
            p.add(c);
1✔
1654
            for (ImClassType sc : c.getSuperClasses()) {
1✔
1655
                p.union(c, sc.getClassDef());
1✔
1656
            }
1✔
1657
        }
1✔
1658
        // generate typeId variables
1659
        classManagementVars = Maps.newLinkedHashMap();
1✔
1660
        for (ImClass c : imProg.getClasses()) {
1✔
1661
            ImClass rep = p.getRep(c);
1✔
1662
            ClassManagementVars v = classManagementVars.computeIfAbsent(rep, r -> new ClassManagementVars(r, this));
1✔
1663
            classManagementVars.put(c, v);
1✔
1664
        }
1✔
1665
        return classManagementVars;
1✔
1666
    }
1667

1668

1669
    public ImFunction getGlobalInitFunc() {
1670
        return globalInitFunc;
1✔
1671
    }
1672

1673

1674
    public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr message) {
1675
        ImFunction ef = errorFunc;
1✔
1676
        if (ef == null) {
1✔
1677
            Optional<ImFunction> f = findErrorFunc().map(this::getFuncFor);
1✔
1678
            ef = errorFunc = f.orElseGet(this::makeDefaultErrorFunc);
1✔
1679
        }
1680
        ImExprs arguments = JassIm.ImExprs(message);
1✔
1681
        return ImFunctionCall(trace, ef, ImTypeArguments(), arguments, false, CallType.NORMAL);
1✔
1682
    }
1683

1684
    private ImFunction makeDefaultErrorFunc() {
1685
        ImVar msgVar = JassIm.ImVar(emptyTrace, TypesHelper.imString(), "msg", false);
1✔
1686
        ImVars parameters = JassIm.ImVars(msgVar);
1✔
1687
        ImType returnType = JassIm.ImVoid();
1✔
1688
        ImVars locals = JassIm.ImVars();
1✔
1689
        ImStmts body = JassIm.ImStmts();
1✔
1690

1691
        // print message:
1692
        // msg = msg + stacktrace
1693
        ImExpr msg = JassIm.ImOperatorCall(WurstOperator.PLUS, ImExprs(JassIm.ImVarAccess(msgVar),
1✔
1694
            JassIm.ImOperatorCall(WurstOperator.PLUS,
1✔
1695
                ImExprs(
1✔
1696
                    JassIm.ImStringVal("\n"),
1✔
1697
                    JassIm.ImGetStackTrace()))));
1✔
1698

1699
        body.add(ImFunctionCall(emptyTrace, getDebugPrintFunction(), ImTypeArguments(), JassIm.ImExprs(msg), false, CallType.NORMAL));
1✔
1700
        // TODO divide by zero to crash thread:
1701

1702

1703
//                stmts.add(JassAst.JassStmtCall("BJDebugMsg",
1704
//                                JassAst.JassExprlist(JassAst.JassExprBinary(
1705
//                                                JassAst.JassExprStringVal("|cffFF3A29Wurst Error:|r" + nl),
1706
//                                                JassAst.JassOpPlus(),
1707
//                                                s.getMessage().translate(translator)))));
1708
//                // crash thread (divide by zero)
1709
//                stmts.add(JassAst.JassStmtCall("I2S", JassAst.JassExprlist(JassAst.JassExprBinary(JassAst.JassExprIntVal("1"), JassAst.JassOpDiv(), Jas
1710
//
1711

1712
        List<FunctionFlag> flags = Lists.newArrayList();
1✔
1713

1714
        ImFunction errorFunc = ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags);
1✔
1715
        imProg.getFunctions().add(errorFunc);
1✔
1716
        return errorFunc;
1✔
1717
    }
1718

1719

1720
    private Optional<FuncDef> findErrorFunc() throws CompileError {
1721
        PackageLink p = wurstProg.lookupPackage("ErrorHandling");
1✔
1722
        if (p == null) {
1✔
1723
            return Optional.empty();
1✔
1724
        }
1725
        ImmutableCollection<FuncLink> funcs = p.getDef().getElements().lookupFuncs("error");
1✔
1726
        if (funcs.isEmpty()) {
1✔
1727
            return Optional.empty();
×
1728
        } else if (funcs.size() > 1) {
1✔
1729
            return Optional.empty();
×
1730
        }
1731
        FuncDef f = (FuncDef) funcs.stream().findAny().get().getDef();
1✔
1732
        return Optional.of(f);
1✔
1733
    }
1734

1735
    int getCompiletimeExpressionsOrder(FunctionCall fc) {
1736
        return compiletimeExpressionsOrder.getOrDefault(fc, 0);
1✔
1737
    }
1738

1739
    public RunArgs getRunArgs() {
1740
        return runArgs;
1✔
1741
    }
1742
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc