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

wurstscript / WurstScript / 265

29 Sep 2025 09:00AM UTC coverage: 62.244% (+0.02%) from 62.222%
265

Pull #1096

circleci

Frotty
restore determinism, fix tests
Pull Request #1096: Perf improvements

17480 of 28083 relevant lines covered (62.24%)

0.62 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.Element;
17
import de.peeeq.wurstscript.jassIm.ImAnyType;
18
import de.peeeq.wurstscript.jassIm.ImArrayType;
19
import de.peeeq.wurstscript.jassIm.ImArrayTypeMulti;
20
import de.peeeq.wurstscript.jassIm.ImClass;
21
import de.peeeq.wurstscript.jassIm.ImClassType;
22
import de.peeeq.wurstscript.jassIm.ImExprs;
23
import de.peeeq.wurstscript.jassIm.ImFuncRef;
24
import de.peeeq.wurstscript.jassIm.ImFunction;
25
import de.peeeq.wurstscript.jassIm.ImFunctionCall;
26
import de.peeeq.wurstscript.jassIm.ImMethod;
27
import de.peeeq.wurstscript.jassIm.ImProg;
28
import de.peeeq.wurstscript.jassIm.ImReturn;
29
import de.peeeq.wurstscript.jassIm.ImSet;
30
import de.peeeq.wurstscript.jassIm.ImSimpleType;
31
import de.peeeq.wurstscript.jassIm.ImStmts;
32
import de.peeeq.wurstscript.jassIm.ImTupleType;
33
import de.peeeq.wurstscript.jassIm.ImTypeArguments;
34
import de.peeeq.wurstscript.jassIm.ImTypeVar;
35
import de.peeeq.wurstscript.jassIm.ImTypeVarRef;
36
import de.peeeq.wurstscript.jassIm.ImTypeVars;
37
import de.peeeq.wurstscript.jassIm.ImVar;
38
import de.peeeq.wurstscript.jassIm.ImVars;
39
import de.peeeq.wurstscript.jassIm.ImVoid;
40
import de.peeeq.wurstscript.jassIm.*;
41
import de.peeeq.wurstscript.parser.WPos;
42
import de.peeeq.wurstscript.types.*;
43
import de.peeeq.wurstscript.utils.Pair;
44
import de.peeeq.wurstscript.utils.Utils;
45
import de.peeeq.wurstscript.validation.TRVEHelper;
46
import de.peeeq.wurstscript.validation.WurstValidator;
47
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
48
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet;
49
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
50
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
51
import org.eclipse.jdt.annotation.Nullable;
52
import org.jetbrains.annotations.NotNull;
53

54
import java.util.*;
55
import java.util.function.Function;
56
import java.util.stream.Collectors;
57
import java.util.stream.Stream;
58

59
import static de.peeeq.wurstscript.jassIm.JassIm.*;
60
import static de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum.*;
61
import static de.peeeq.wurstscript.utils.Utils.elementNameWithPath;
62

63
public class ImTranslator {
64

65

66
    public static final String $DEBUG_PRINT = "$debugPrint";
67

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

70
    // existing fields (keep callRelations as Guava Multimap to avoid ripple effects)
71
    private Multimap<ImFunction, ImFunction> callRelations;
72

73
    // swap these Sets to fastutil; keep accessors returning Set if you have them
74
    private ReferenceOpenHashSet<ImFunction> usedFunctions;
75
    private ObjectOpenHashSet<ImVar> usedVariables;  // or ReferenceOpenHashSet<ImVar> if identity semantics are intended
76
    private ObjectOpenHashSet<ImVar> readVariables;  // ditto
77

78
    private @Nullable ImFunction debugPrintFunction;
79

80
    private final Map<TranslatedToImFunction, ImFunction> functionMap = new Object2ObjectLinkedOpenHashMap<>();
1✔
81
    private @Nullable ImFunction globalInitFunc;
82

83
    private final ImProg imProg;
84

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

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

89
    private final Set<WPackage> translatedPackages = new ObjectLinkedOpenHashSet<>();
1✔
90
    private final Set<ClassDef> translatedClasses = new ObjectLinkedOpenHashSet<>();
1✔
91

92

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

95
    private final WurstModel wurstProg;
96

97
    private @Nullable ImFunction mainFunc = null;
1✔
98

99
    private @Nullable ImFunction configFunc = null;
1✔
100

101
    @Nullable public ImFunction ensureIntFunc = null;
1✔
102
    @Nullable public ImFunction ensureBoolFunc = null;
1✔
103
    @Nullable public ImFunction ensureRealFunc = null;
1✔
104
    @Nullable public ImFunction ensureStrFunc = null;
1✔
105
    @Nullable public ImFunction stringConcatFunc = null;
1✔
106

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

109
    private final boolean isUnitTestMode;
110

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

113
    private int compiletimeOrderCounter = 1;
1✔
114
    private final Map<TranslatedToImFunction, FunctionFlagCompiletime> compiletimeFlags = new HashMap<>();
1✔
115
    private final Map<ExprFunctionCall, Integer> compiletimeExpressionsOrder = new HashMap<>();
1✔
116

117
    de.peeeq.wurstscript.ast.Element lasttranslatedThing;
118
    private final boolean debug = false;
1✔
119
    private final RunArgs runArgs;
120

121
    public ImTranslator(WurstModel wurstProg, boolean isUnitTestMode, RunArgs runArgs) {
1✔
122
        this.wurstProg = wurstProg;
1✔
123
        this.lasttranslatedThing = wurstProg;
1✔
124
        this.isUnitTestMode = isUnitTestMode;
1✔
125
        imProg = ImProg(wurstProg, ImVars(), ImFunctions(), ImMethods(), JassIm.ImClasses(), JassIm.ImTypeClassFuncs(), new Object2ObjectLinkedOpenHashMap<>());
1✔
126
        this.runArgs = runArgs;
1✔
127
    }
1✔
128

129

130
    /**
131
     * translates a program
132
     */
133
    public ImProg translateProg() {
134
        try {
135
            globalInitFunc = ImFunction(emptyTrace, "initGlobals", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags());
1✔
136
            addFunction(getGlobalInitFunc());
1✔
137
            debugPrintFunction = ImFunction(emptyTrace, $DEBUG_PRINT, ImTypeVars(), ImVars(JassIm.ImVar(wurstProg, WurstTypeString.instance().imTranslateType(this), "msg",
1✔
138
                    false)), ImVoid(), ImVars(), ImStmts(), flags(IS_NATIVE, IS_BJ));
1✔
139

140
            if(isLuaTarget()) {
1✔
141
                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✔
142
                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✔
143
                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✔
144
                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✔
145
                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✔
146
                addFunction(ensureIntFunc);
1✔
147
                addFunction(ensureBoolFunc);
1✔
148
                addFunction(ensureRealFunc);
1✔
149
                addFunction(ensureStrFunc);
1✔
150
                addFunction(stringConcatFunc);
1✔
151
            }
152

153
            calculateCompiletimeOrder();
1✔
154

155
            for (CompilationUnit cu : wurstProg) {
1✔
156
                translateCompilationUnit(cu);
1✔
157
            }
1✔
158

159
            if (mainFunc == null) {
1✔
160
                mainFunc = ImFunction(emptyTrace, "main", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags());
1✔
161
                addFunction(mainFunc);
1✔
162
            }
163
            if (configFunc == null) {
1✔
164
                configFunc = ImFunction(emptyTrace, "config", ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags());
1✔
165
                addFunction(configFunc);
1✔
166
            }
167

168
            finishInitFunctions();
1✔
169
            EliminateCallFunctionsWithAnnotation.process(imProg);
1✔
170
            removeDuplicateNatives(imProg);
1✔
171
            sortEverything();
1✔
172
            return imProg;
1✔
173
        } catch (CompileError t) {
1✔
174
            throw t;
1✔
175
        } catch (Throwable t) {
×
176
            WLogger.severe(t);
×
177
            throw new RuntimeException("There was a Wurst bug in the translation of "
×
178
                    + Utils.printElementWithSource(Optional.of(lasttranslatedThing))
×
179
                    + ": "
180
                    + t.getMessage()
×
181
                    + "\nPlease open a ticket with source code and the error log.", t);
182
        }
183
    }
184

185
    /**
186
     * Number all the compiletime functions and expressions,
187
     * so that the one with the lowest number can be executed first.
188
     * <p>
189
     * Dependendend packages are executed first and inside a package
190
     * it goes from top to bottom.
191
     */
192
    private void calculateCompiletimeOrder() {
193
        Set<WPackage> visited = new HashSet<>();
1✔
194
        ImmutableCollection<WPackage> packages = wurstProg.attrPackages().values();
1✔
195

196
        for (WPackage p : packages) {
1✔
197
            calculateCompiletimeOrder_walk(p, visited);
1✔
198
        }
1✔
199
    }
1✔
200

201
    private void calculateCompiletimeOrder_walk(WPackage p, Set<WPackage> visited) {
202
        if (!visited.add(p)) {
1✔
203
            return;
1✔
204
        }
205
        for (WPackage dep : p.attrInitDependencies()) {
1✔
206
            calculateCompiletimeOrder_walk(dep, visited);
1✔
207
        }
1✔
208
        p.accept(new de.peeeq.wurstscript.ast.Element.DefaultVisitor() {
1✔
209
            @Override
210
            public void visit(FuncDef funcDef) {
211
                super.visit(funcDef);
1✔
212
                if (funcDef.attrIsCompiletime()) {
1✔
213
                    compiletimeFlags.put(funcDef, new FunctionFlagCompiletime(compiletimeOrderCounter++));
1✔
214
                }
215
            }
1✔
216

217
            @Override
218
            public void visit(ExprFunctionCall fc) {
219
                super.visit(fc);
1✔
220
                if (fc.getFuncName().equals("compiletime")) {
1✔
221
                    compiletimeExpressionsOrder.put(fc, compiletimeOrderCounter++);
1✔
222
                }
223
            }
1✔
224
        });
225
    }
1✔
226

227

228
    /**
229
     * sorting everything is supposed to make the translation deterministic
230
     */
231
    private void sortEverything() {
232
        sortList(imProg.getClasses());
1✔
233
        sortList(imProg.getGlobals());
1✔
234
        sortList(imProg.getFunctions());
1✔
235
        for (ImClass c : imProg.getClasses()) {
1✔
236
            sortList(c.getFields());
1✔
237
            sortList(c.getMethods());
1✔
238
        }
1✔
239
    }
1✔
240

241

242
    private <T extends Element> void sortList(List<T> list) {
243
        List<T> classes = removeAll(list);
1✔
244
        Comparator<T> comparator = Comparator.comparing(this::getQualifiedClassName);
1✔
245
        classes.sort(comparator);
1✔
246
        list.addAll(classes);
1✔
247
    }
1✔
248

249
    public <T> List<T> removeAll(List<T> list) {
250
        List<T> result = new ArrayList<>();
1✔
251
        while (!list.isEmpty()) {
1✔
252
            result.add(0, list.remove(list.size() - 1));
1✔
253
        }
254
        return result;
1✔
255
    }
256

257
    private String getQualifiedClassName(Element c) {
258
        return getQualifiedClassName(c.attrTrace());
1✔
259
    }
260

261

262
    private String getQualifiedClassName(de.peeeq.wurstscript.ast.Element e) {
263
        String result = "";
1✔
264
        if (e instanceof NamedScope) {
1✔
265
            NamedScope ns = (NamedScope) e;
1✔
266
            result = ns.getName();
1✔
267
        }
268
        de.peeeq.wurstscript.ast.Element parent = e.getParent();
1✔
269
        if (parent == null) {
1✔
270
            return result;
1✔
271
        }
272
        parent = parent.attrNearestNamedScope();
1✔
273
        if (parent == null) {
1✔
274
            return result;
1✔
275
        }
276
        return getQualifiedClassName(parent) + "_" + result;
1✔
277
    }
278

279

280
    /***
281
     * this phase removes duplicate native declarations
282
     */
283
    private void removeDuplicateNatives(ImProg imProg) {
284
        Map<String, ImFunction> natives = new HashMap<>();
1✔
285
        Map<ImFunction, ImFunction> removed = new HashMap<>();
1✔
286
        ListIterator<ImFunction> it = imProg.getFunctions().listIterator();
1✔
287
        while (it.hasNext()) {
1✔
288
            ImFunction f = it.next();
1✔
289
            if (f.isNative() && natives.containsKey(f.getName())) {
1✔
290
                ImFunction existing = natives.get(f.getName());
1✔
291
                if (!compatibleTypes(f, existing)) {
1✔
292
                    throw new CompileError(f, "Native function definition conflicts with other native function defined in " +
×
293
                            existing.attrTrace().attrErrorPos());
×
294
                }
295
                // remove duplicate
296
                it.remove();
1✔
297
                removed.put(f, existing);
1✔
298
            } else {
1✔
299
                natives.put(f.getName(), f);
1✔
300
            }
301
        }
1✔
302
        // rewrite removed links
303
        imProg.accept(new ImProg.DefaultVisitor() {
1✔
304
            public void visit(ImFunctionCall e) {
305
                super.visit(e);
1✔
306
                if (removed.containsKey(e.getFunc())) {
1✔
307
                    e.setFunc(removed.get(e.getFunc()));
1✔
308
                }
309
            }
1✔
310

311
            public void visit(ImFuncRef e) {
312
                super.visit(e);
1✔
313
                if (removed.containsKey(e.getFunc())) {
1✔
314
                    e.setFunc(removed.get(e.getFunc()));
×
315
                }
316
            }
1✔
317
        });
318
    }
1✔
319

320

321
    /**
322
     * checks if two functions f and g have compatible types
323
     */
324
    private boolean compatibleTypes(ImFunction f, ImFunction g) {
325
        if (!f.getReturnType().equalsType(g.getReturnType())) {
1✔
326
            return false;
×
327
        }
328
        if (f.getParameters().size() != g.getParameters().size()) {
1✔
329
            return false;
×
330
        }
331
        for (int i = 0; i < f.getParameters().size(); i++) {
1✔
332
            if (!f.getParameters().get(i).getType().equalsType(g.getParameters().get(i).getType())) {
×
333
                return false;
×
334
            }
335
        }
336
        return true;
1✔
337
    }
338

339

340
    private ArrayList<FunctionFlag> flags(FunctionFlag... flags) {
341
        return Lists.newArrayList(flags);
1✔
342
    }
343

344

345
    private void translateCompilationUnit(CompilationUnit cu) {
346
        lasttranslatedThing = cu;
1✔
347
        // TODO can we make this smarter? Only translate functions which are actually called...
348
        for (WPackage p : cu.getPackages()) {
1✔
349
            lasttranslatedThing = p;
1✔
350
            p.imTranslateTLD(this);
1✔
351
        }
1✔
352
        for (JassToplevelDeclaration tld : cu.getJassDecls()) {
1✔
353
            lasttranslatedThing = tld;
1✔
354
            tld.imTranslateTLD(this);
1✔
355
        }
1✔
356
    }
1✔
357

358

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

363

364
        for (ImFunction initFunc : initFuncMap.values()) {
1✔
365
            addFunction(initFunc);
1✔
366
        }
1✔
367
        Set<WPackage> calledInitializers = Sets.newLinkedHashSet();
1✔
368

369
        ImVar initTrigVar = prepareTrigger();
1✔
370

371
        for (WPackage p : Utils.sortByName(initFuncMap.keySet())) {
1✔
372
            callInitFunc(calledInitializers, p, initTrigVar);
1✔
373
        }
1✔
374

375
        ImFunction native_DestroyTrigger = getNativeFunc("DestroyTrigger");
1✔
376
        if (native_DestroyTrigger != null) {
1✔
377
            getMainFunc().getBody().add(JassIm.ImFunctionCall(emptyTrace, native_DestroyTrigger, ImTypeArguments(),
1✔
378
                    JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL));
1✔
379
        }
380
    }
1✔
381

382
    @NotNull
383
    private ImVar prepareTrigger() {
384
        ImVar initTrigVar = JassIm.ImVar(emptyTrace, JassIm.ImSimpleType("trigger"), "initTrig", false);
1✔
385
        getMainFunc().getLocals().add(initTrigVar);
1✔
386

387
        // initTrigVar = CreateTrigger()
388
        ImFunction createTrigger = getNativeFunc("CreateTrigger");
1✔
389
        if (createTrigger != null) {
1✔
390
            getMainFunc().getBody().add(ImSet(getMainFunc().getTrace(), ImVarAccess(initTrigVar), JassIm.ImFunctionCall(getMainFunc().getTrace(), getNativeFunc("CreateTrigger"), ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL)));
1✔
391
        }
392
        return initTrigVar;
1✔
393
    }
394

395

396
    private ImFunction getNativeFunc(String funcName) {
397
        ImmutableCollection<FuncLink> wurstFunc = wurstProg.lookupFuncs(funcName);
1✔
398
        if (wurstFunc.isEmpty()) {
1✔
399
            return null;
1✔
400
        }
401
        return getFuncFor(Utils.getFirst(wurstFunc).getDef());
1✔
402
    }
403

404
    private void callInitFunc(Set<WPackage> calledInitializers, WPackage p, ImVar initTrigVar) {
405
        Preconditions.checkNotNull(p);
1✔
406
        if (calledInitializers.contains(p)) {
1✔
407
            return;
1✔
408
        }
409
        calledInitializers.add(p);
1✔
410
        // first initialize all packages imported by this package:
411
        for (WPackage dep : p.attrInitDependencies()) {
1✔
412
            callInitFunc(calledInitializers, dep, initTrigVar);
1✔
413
        }
1✔
414
        ImFunction initFunc = initFuncMap.get(p);
1✔
415
        if (initFunc == null) {
1✔
416
            return;
1✔
417
        }
418
        if (initFunc.getBody().size() == 0) {
1✔
419
            return;
×
420
        }
421
        boolean successful = createInitFuncCall(p, initTrigVar, initFunc);
1✔
422

423
        if (!successful) {
1✔
424
            getMainFunc().getBody().add(ImFunctionCall(initFunc.getTrace(), initFunc, ImTypeArguments(), ImExprs(), false, CallType.NORMAL));
1✔
425
        }
426
    }
1✔
427

428

429
    private boolean createInitFuncCall(WPackage p, ImVar initTrigVar, ImFunction initFunc) {
430
        ImStmts mainBody = getMainFunc().getBody();
1✔
431

432
        ImFunction native_ClearTrigger = getNativeFunc("TriggerClearConditions");
1✔
433
        ImFunction native_TriggerAddCondition = getNativeFunc("TriggerAddCondition");
1✔
434
        ImFunction native_Condition = getNativeFunc("Condition");
1✔
435
        ImFunction native_TriggerEvaluate = getNativeFunc("TriggerEvaluate");
1✔
436
        ImFunction native_DisplayTimedTextToPlayer = getNativeFunc("DisplayTimedTextToPlayer");
1✔
437
        ImFunction native_GetLocalPlayer = getNativeFunc("GetLocalPlayer");
1✔
438

439
        if (native_ClearTrigger == null
1✔
440
                || native_TriggerAddCondition == null
441
                || native_Condition == null
442
                || native_TriggerEvaluate == null
443
                || native_DisplayTimedTextToPlayer == null
444
                || native_GetLocalPlayer == null
445
        ) {
446
            return false;
1✔
447
        }
448

449

450
        // rewrite init func to return boolean true:
451
        initFunc.setReturnType(WurstTypeBool.instance().imTranslateType(this));
1✔
452
        initFunc.accept(new ImFunction.DefaultVisitor() {
1✔
453
            @Override
454
            public void visit(ImReturn imReturn) {
455
                super.visit(imReturn);
×
456
                imReturn.setReturnValue(JassIm.ImBoolVal(true));
×
457
            }
×
458
        });
459
        de.peeeq.wurstscript.ast.Element trace = initFunc.getTrace();
1✔
460
        initFunc.getBody().add(JassIm.ImReturn(trace, JassIm.ImBoolVal(true)));
1✔
461

462

463
        // TriggerAddCondition(initTrigVar, Condition(function myInit))
464
        mainBody.add(ImFunctionCall(trace, native_TriggerAddCondition, ImTypeArguments(), JassIm.ImExprs(
1✔
465
                JassIm.ImVarAccess(initTrigVar),
1✔
466
                ImFunctionCall(trace, native_Condition, ImTypeArguments(), JassIm.ImExprs(
1✔
467
                        JassIm.ImFuncRef(trace, initFunc)), false, CallType.NORMAL)
1✔
468
        ), true, CallType.NORMAL));
469
        // if not TriggerEvaluate(initTrigVar) ...
470
        mainBody.add(JassIm.ImIf(trace,
1✔
471
                JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(
1✔
472
                        ImFunctionCall(trace, native_TriggerEvaluate, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL)
1✔
473
                )),
474
                // then: DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 45., "Could not initialize package")
475
                JassIm.ImStmts(
1✔
476
                    imError(trace, JassIm.ImStringVal("Could not initialize package " + p.getName() + "."))
1✔
477
                ),
478
                // else:
479
                JassIm.ImStmts()));
1✔
480
        mainBody.add(ImFunctionCall(trace, native_ClearTrigger, ImTypeArguments(), JassIm.ImExprs(JassIm.ImVarAccess(initTrigVar)), false, CallType.NORMAL));
1✔
481
        return true;
1✔
482
    }
483

484
    private void addFunction(ImFunction f, StructureDef s) {
485
        ImClass c = getClassFor(s.attrNearestClassOrInterface());
1✔
486
        c.getFunctions().add(f);
1✔
487
    }
1✔
488

489
    private void addFunction(ImFunction f, TranslatedToImFunction funcDef) {
490
        ImClass classForFunc = getClassForFunc(funcDef);
1✔
491
        if (classForFunc != null) {
1✔
492
            classForFunc.getFunctions().add(f);
1✔
493
        } else {
494
            addFunction(f);
1✔
495
        }
496
    }
1✔
497

498
    private void addFunction(ImFunction f) {
499
        imProg.getFunctions().add(f);
1✔
500
    }
1✔
501

502
    public void addGlobal(ImVar v) {
503
        imProg.getGlobals().add(v);
1✔
504
    }
1✔
505

506

507
    public void addGlobalInitalizer(ImVar v, PackageOrGlobal packageOrGlobal, VarInitialization initialExpr) {
508
        if (initialExpr instanceof NoExpr) {
1✔
509
            // nothing to initialize
510
            return;
1✔
511
        }
512

513

514
        ImFunction f;
515
        if (packageOrGlobal instanceof WPackage) {
1✔
516
            WPackage p = (WPackage) packageOrGlobal;
1✔
517
            f = getInitFuncFor(p);
1✔
518
        } else {
1✔
519
            f = globalInitFunc;
1✔
520
        }
521
        de.peeeq.wurstscript.ast.Element trace = packageOrGlobal == null ? emptyTrace : packageOrGlobal;
1✔
522
        if (initialExpr instanceof Expr) {
1✔
523
            Expr expr = (Expr) initialExpr;
1✔
524
            ImExpr translated = expr.imTranslateExpr(this, f);
1✔
525
            ImSet imSet = ImSet(trace, ImVarAccess(v), translated);
1✔
526
            if (!v.getIsBJ()) {
1✔
527
                // add init statement for non-bj vars
528
                // bj-vars are already initalized by blizzard
529
                f.getBody().add(imSet);
1✔
530
            }
531
            imProg.getGlobalInits().put(v, Collections.singletonList(imSet));
1✔
532
        } else if (initialExpr instanceof ArrayInitializer) {
1✔
533
            ArrayInitializer arInit = (ArrayInitializer) initialExpr;
1✔
534
            List<ImExpr> translatedExprs = arInit.getValues().stream()
1✔
535
                    .map(expr -> expr.imTranslateExpr(this, f))
1✔
536
                    .collect(Collectors.toList());
1✔
537
            List<ImSet> imSets = new ArrayList<>();
1✔
538
            for (int i = 0; i < arInit.getValues().size(); i++) {
1✔
539
                ImExpr translated = translatedExprs.get(i);
1✔
540
                ImSet imSet = ImSet(trace, ImVarArrayAccess(trace, v, ImExprs(JassIm.ImIntVal(i))), translated);
1✔
541
                imSets.add(imSet);
1✔
542
            }
543
            f.getBody().addAll(imSets);
1✔
544
            // add list of init-values to translatedExprs
545
            imProg.getGlobalInits().put(v, imSets);
1✔
546
        }
547
    }
1✔
548

549
    public void addGlobalWithInitalizer(ImVar g, ImExpr initial) {
550
        imProg.getGlobals().add(g);
1✔
551
        ImSet imSet = ImSet(g.getTrace(), ImVarAccess(g), initial);
1✔
552
        getGlobalInitFunc().getBody().add(imSet);
1✔
553
        imProg.getGlobalInits().put(g, Collections.singletonList(imSet));
1✔
554
    }
1✔
555

556

557
    public ImExpr getDefaultValueForJassType(ImType type) {
558
        if (type instanceof ImSimpleType) {
1✔
559
            ImSimpleType imSimpleType = (ImSimpleType) type;
1✔
560
            return ImHelper.defaultValueForType(imSimpleType);
1✔
561
        } else if (type instanceof ImAnyType) {
1✔
562
            return JassIm.ImIntVal(0);
1✔
563
        } else if (type instanceof ImTupleType) {
×
564
            ImTupleType imTupleType = (ImTupleType) type;
×
565
            return getDefaultValueForJassType(imTupleType.getTypes().get(0));
×
566
        } else {
567
            throw new IllegalArgumentException("could not get default value for type " + type);
×
568
        }
569
    }
570

571
    public GetAForB<StructureDef, ImFunction> destroyFunc = new GetAForB<StructureDef, ImFunction>() {
1✔
572

573
        @Override
574
        public ImFunction initFor(StructureDef classDef) {
575
            ImVars params = ImVars(JassIm.ImVar(classDef, selfType(classDef), "this", false));
1✔
576

577
            ImFunction f = ImFunction(classDef.getOnDestroy(), "destroy" + classDef.getName(), ImTypeVars(), params, TypesHelper.imVoid(), ImVars(), ImStmts(), flags());
1✔
578
            addFunction(f, classDef);
1✔
579
            return f;
1✔
580
        }
581
    };
582

583
    public GetAForB<StructureDef, ImMethod> destroyMethod = new GetAForB<StructureDef, ImMethod>() {
1✔
584

585
        @Override
586
        public ImMethod initFor(StructureDef classDef) {
587
            ImFunction impl = destroyFunc.getFor(classDef);
1✔
588
            ImMethod m = JassIm.ImMethod(classDef, selfType(classDef), "destroy" + classDef.getName(),
1✔
589
                    impl, Lists.newArrayList(), false);
1✔
590
            return m;
1✔
591
        }
592
    };
593

594
    private ImType selfType(TranslatedToImFunction f) {
595
        return f.match(new TranslatedToImFunction.Matcher<ImType>() {
1✔
596
            @Override
597
            public ImType case_FuncDef(FuncDef f) {
598
                return selfType(f);
1✔
599
            }
600

601
            @Override
602
            public ImType case_ConstructorDef(ConstructorDef f) {
603
                return selfType(f.attrNearestClassOrInterface());
1✔
604
            }
605

606
            @Override
607
            public ImType case_NativeFunc(NativeFunc f) {
608
                throw new CompileError(f, "Cannot use 'this' here.");
×
609
            }
610

611
            @Override
612
            public ImType case_OnDestroyDef(OnDestroyDef f) {
613
                return selfType(f.attrNearestClassOrInterface());
1✔
614
            }
615

616
            @Override
617
            public ImType case_TupleDef(TupleDef f) {
618
                throw new CompileError(f, "Cannot use 'this' here.");
×
619
            }
620

621
            @Override
622
            public ImType case_ExprClosure(ExprClosure f) {
623
                return selfType(getClassForClosure(f));
1✔
624
            }
625

626
            @Override
627
            public ImType case_InitBlock(InitBlock f) {
628
                throw new CompileError(f, "Cannot use 'this' here.");
×
629
            }
630

631
            @Override
632
            public ImType case_ExtensionFuncDef(ExtensionFuncDef f) {
633
                return f.getExtendedType().attrTyp().imTranslateType(ImTranslator.this);
1✔
634
            }
635
        });
636
    }
637

638
    private ImClassType selfType(FuncDef f) {
639
        return selfType(f.attrNearestClassOrInterface());
1✔
640
    }
641

642
    public ImClassType selfType(StructureDef classDef) {
643
        ImClass imClass = getClassFor(classDef.attrNearestClassOrInterface());
1✔
644
        return selfType(imClass);
1✔
645
    }
646

647
    public ImClassType selfType(ImClass imClass) {
648
        ImTypeArguments typeArgs = JassIm.ImTypeArguments();
1✔
649
        for (ImTypeVar tv : imClass.getTypeVariables()) {
1✔
650
            typeArgs.add(JassIm.ImTypeArgument(JassIm.ImTypeVarRef(tv), Collections.emptyMap()));
1✔
651
        }
1✔
652
        return JassIm.ImClassType(imClass, typeArgs);
1✔
653
    }
654

655
    public GetAForB<ImClass, ImFunction> allocFunc = new GetAForB<ImClass, ImFunction>() {
1✔
656

657
        @Override
658
        public ImFunction initFor(ImClass c) {
659

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

663
    };
664

665
    public GetAForB<ImClass, ImFunction> deallocFunc = new GetAForB<ImClass, ImFunction>() {
1✔
666

667
        @Override
668
        public ImFunction initFor(ImClass c) {
669

670
            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✔
671
        }
672

673
    };
674

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

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

679
        @Override
680
        public ImTypeVar initFor(TypeParamDef a) {
681
            ImTypeVar v = JassIm.ImTypeVar(a.getName());
1✔
682
            typeVariableReverse.put(v, a);
1✔
683
            return v;
1✔
684
        }
685
    };
686

687

688
    public ImFunction getFuncFor(TranslatedToImFunction funcDef) {
689
        if (functionMap.containsKey(funcDef)) {
1✔
690
            return functionMap.get(funcDef);
1✔
691
        }
692
        String name = getNameFor(funcDef);
1✔
693
        List<FunctionFlag> flags = flags();
1✔
694
        if (funcDef instanceof NativeFunc) {
1✔
695
            flags.add(IS_NATIVE);
1✔
696
        }
697
        if (isBJ(funcDef.getSource())) {
1✔
698
            flags.add(IS_BJ);
1✔
699
        }
700
        if (isExtern(funcDef)) {
1✔
701
            flags.add(FunctionFlagEnum.IS_EXTERN);
1✔
702
        }
703
        if (funcDef instanceof FuncDef) {
1✔
704
            FuncDef funcDef2 = (FuncDef) funcDef;
1✔
705
            if (funcDef2.attrIsCompiletime()) {
1✔
706
                FunctionFlagCompiletime flag = compiletimeFlags.get(funcDef);
1✔
707
                if (flag == null) {
1✔
708
                    throw new CompileError(funcDef.getSource(), "Compiletime flag not supported here.");
×
709
                }
710
                flags.add(flag);
1✔
711
            }
712
            if (funcDef2.attrHasAnnotation("compiletimenative")) {
1✔
713
                flags.add(FunctionFlagEnum.IS_COMPILETIME_NATIVE);
1✔
714
            }
715
            if (funcDef2.attrHasAnnotation("test")) {
1✔
716
                flags.add(IS_TEST);
1✔
717
            }
718
        }
719

720
        // Check if last parameter is vararg
721
        if (funcDef instanceof AstElementWithParameters) {
1✔
722
            WParameters params = ((AstElementWithParameters) funcDef).getParameters();
1✔
723
            if (params.size() >= 1 && params.get(params.size() - 1).attrIsVararg()) {
1✔
724
                flags.add(IS_VARARG);
1✔
725
            }
726
        }
727

728

729
        if (funcDef instanceof HasModifier) {
1✔
730
            HasModifier awm = (HasModifier) funcDef;
1✔
731
            for (Modifier m : awm.getModifiers()) {
1✔
732
                if (m instanceof Annotation) {
1✔
733
                    Annotation annotation = (Annotation) m;
1✔
734
                    flags.add(new FunctionFlagAnnotation(annotation.getAnnotationType()));
1✔
735
                }
736
            }
1✔
737
        }
738

739
        ImTypeVars typeVars = collectTypeVarsForFunction(funcDef);
1✔
740
        ImFunction f = ImFunction(funcDef, name, typeVars, ImVars(), ImVoid(), ImVars(), ImStmts(), flags);
1✔
741
        funcDef.imCreateFuncSkeleton(this, f);
1✔
742

743
        addFunction(f, funcDef);
1✔
744
        functionMap.put(funcDef, f);
1✔
745
        return f;
1✔
746
    }
747

748
    private ImClass getClassForFunc(TranslatedToImFunction funcDef) {
749
        if (funcDef == null) {
1✔
750
            return null;
×
751
        }
752
        return funcDef.match(new TranslatedToImFunction.Matcher<ImClass>() {
1✔
753
            @Override
754
            public ImClass case_TupleDef(TupleDef tupleDef) {
755
                return null;
×
756
            }
757

758
            @Override
759
            public ImClass case_FuncDef(FuncDef funcDef) {
760
                if (funcDef.attrIsDynamicClassMember()) {
1✔
761
                    return getClassFor(funcDef.attrNearestClassOrInterface());
1✔
762
                }
763
                return null;
1✔
764
            }
765

766
            @Override
767
            public ImClass case_NativeFunc(NativeFunc nativeFunc) {
768
                return null;
1✔
769
            }
770

771
            @Override
772
            public ImClass case_OnDestroyDef(OnDestroyDef funcDef) {
773
                return getClassFor(funcDef.attrNearestClassOrInterface());
1✔
774
            }
775

776
            @Override
777
            public ImClass case_InitBlock(InitBlock initBlock) {
778
                return null;
×
779
            }
780

781
            @Override
782
            public ImClass case_ExtensionFuncDef(ExtensionFuncDef extensionFuncDef) {
783
                return null;
1✔
784
            }
785

786
            @Override
787
            public ImClass case_ConstructorDef(ConstructorDef funcDef) {
788
                return getClassFor(funcDef.attrNearestClassOrInterface());
1✔
789
            }
790

791
            @Override
792
            public ImClass case_ExprClosure(ExprClosure exprClosure) {
793
                return null;
1✔
794
            }
795
        });
796
    }
797

798
    private ImTypeVars collectTypeVarsForFunction(TranslatedToImFunction funcDef) {
799
        ImTypeVars typeVars = ImTypeVars();
1✔
800
        funcDef.match(new TranslatedToImFunction.MatcherVoid() {
1✔
801
            @Override
802
            public void case_FuncDef(FuncDef funcDef) {
803
                handleTypeParameters(funcDef.getTypeParameters());
1✔
804
            }
1✔
805

806

807
            private void handleTypeParameters(TypeParamDefs tps) {
808
                for (TypeParamDef tp : tps) {
1✔
809
                    handleTypeParameter(tp);
1✔
810
                }
1✔
811
            }
1✔
812

813
            private void handleTypeParameter(TypeParamDef tp) {
814
                if (tp.getTypeParamConstraints() instanceof TypeExprList) {
1✔
815
                    typeVars.add(typeVariable.getFor(tp));
1✔
816
                }
817
            }
1✔
818

819
            @Override
820
            public void case_ConstructorDef(ConstructorDef constructorDef) {
821
            }
×
822

823
            @Override
824
            public void case_NativeFunc(NativeFunc nativeFunc) {
825
            }
1✔
826

827
            @Override
828
            public void case_OnDestroyDef(OnDestroyDef onDestroyDef) {
829
            }
1✔
830

831
            @Override
832
            public void case_TupleDef(TupleDef tupleDef) {
833
            }
×
834

835
            @Override
836
            public void case_ExprClosure(ExprClosure exprClosure) {
837
                // TODO where to set closure parameters?
838
            }
1✔
839

840
            @Override
841
            public void case_InitBlock(InitBlock initBlock) {
842

843
            }
×
844

845
            @Override
846
            public void case_ExtensionFuncDef(ExtensionFuncDef funcDef) {
847
                handleTypeParameters(funcDef.getTypeParameters());
1✔
848
            }
1✔
849
        });
850
        return typeVars;
1✔
851
    }
852

853

854
    private boolean isExtern(TranslatedToImFunction funcDef) {
855
        if (funcDef instanceof HasModifier) {
1✔
856
            HasModifier f = (HasModifier) funcDef;
1✔
857
            for (Modifier m : f.getModifiers()) {
1✔
858
                if (m instanceof Annotation) {
1✔
859
                    Annotation a = (Annotation) m;
1✔
860
                    if (a.getAnnotationType().equals("@extern")) {
1✔
861
                        return true;
1✔
862
                    }
863
                }
864
            }
1✔
865
        }
866
        return false;
1✔
867
    }
868

869

870
    private boolean isBJ(WPos source) {
871
        String f = source.getFile().toLowerCase();
1✔
872
        return f.endsWith("blizzard.j") || f.endsWith("common.j");
1✔
873
    }
874

875
    public ImFunction getInitFuncFor(WPackage p) {
876
        // TODO more precise trace
877
        return initFuncMap.computeIfAbsent(p, p1 -> ImFunction(p1, "init_" + p1.getName(), ImTypeVars(), ImVars(), ImVoid(), ImVars(), ImStmts(), flags()));
1✔
878
    }
879

880
    /**
881
     * returns a suitable name for the given element
882
     * the returned name is a valid jass identifier
883
     */
884
    public String getNameFor(de.peeeq.wurstscript.ast.Element e) {
885
        if (e instanceof FuncDef) {
1✔
886
            FuncDef f = (FuncDef) e;
1✔
887
            if (f.attrNearestStructureDef() != null) {
1✔
888
                return getNameFor(f.attrNearestStructureDef()) + "_" + f.getName();
1✔
889
            }
890
        } else if (e instanceof ExtensionFuncDef) {
1✔
891
            ExtensionFuncDef f = (ExtensionFuncDef) e;
1✔
892
            return getNameFor(f.getExtendedType()) + "_" + f.getName();
1✔
893
        } else if (e instanceof TypeExprSimple) {
1✔
894
            TypeExprSimple t = (TypeExprSimple) e;
1✔
895
            return t.getTypeName();
1✔
896
        } else if (e instanceof TypeExprThis) {
1✔
897
            return "thistype";
×
898
        } else if (e instanceof TypeExprArray) {
1✔
899
            TypeExprArray t = (TypeExprArray) e;
×
900
            return getNameFor(t.getBase()) + "Array";
×
901
        } else if (e instanceof ModuleInstanciation) {
1✔
902
            ModuleInstanciation mi = (ModuleInstanciation) e;
1✔
903
            return getNameFor(mi.getParent().attrNearestNamedScope()) + "_" + mi.getName();
1✔
904
        }
905

906

907
        if (e instanceof AstElementWithNameId) {
1✔
908
            AstElementWithNameId wn = (AstElementWithNameId) e;
1✔
909
            return wn.getNameId().getName();
1✔
910
        } else if (e instanceof ConstructorDef) {
1✔
911
            return "new_" + e.attrNearestClassDef().getName();
×
912
        } else if (e instanceof OnDestroyDef) {
1✔
913
            return "ondestroy_" + e.attrNearestClassDef().getName();
1✔
914
        } else if (e instanceof ExprClosure) {
1✔
915
            return e.attrNearestNamedScope().getName() + "_closure";
1✔
916
        }
917
        throw new RuntimeException("unhandled case: " + e.getClass().getName());
×
918
    }
919

920
    public ImVar getThisVar(TranslatedToImFunction f) {
921
        if (f instanceof OnDestroyDef) {
1✔
922
            // special case for onDestroy defs
923
            // TODO also special case for constructors needed?
924
            OnDestroyDef od = (OnDestroyDef) f;
1✔
925
            if (od.getParent() instanceof ModuleInstanciation) {
1✔
926
                ModuleInstanciation mi = (ModuleInstanciation) od.getParent();
1✔
927
                ClassDef c = mi.attrNearestClassDef();
1✔
928
                f = c.getOnDestroy();
1✔
929
            }
930
        }
931
        if (thisVarMap.containsKey(f)) {
1✔
932
            return thisVarMap.get(f);
1✔
933
        }
934
        ImVar v = JassIm.ImVar(f, selfType(f), "this", false);
1✔
935
        thisVarMap.put(f, v);
1✔
936
        return v;
1✔
937
    }
938

939
    public ImVar getThisVar(ImFunction f, ExprThis e) {
940
        return getThisVarForNode(f, e);
1✔
941
    }
942

943
    public ImVar getThisVar(ImFunction f, ExprSuper e) {
944
        return getThisVarForNode(f, e);
×
945
    }
946

947
    private ImVar getThisVarForNode(ImFunction f, de.peeeq.wurstscript.ast.Element node1) {
948
        de.peeeq.wurstscript.ast.Element node = node1;
1✔
949
        while (node != null) {
1✔
950
            if (node instanceof TranslatedToImFunction && !(node instanceof ExprClosure)) {
1✔
951
                return getThisVar((TranslatedToImFunction) node);
1✔
952
            }
953
            node = node.getParent();
1✔
954
        }
955
        if (f.getParameters().isEmpty()) {
1✔
956
            throw new CompileError(node1.attrSource(), "Could not get 'this'.");
×
957
        }
958
        return f.getParameters().get(0);
1✔
959
    }
960

961

962
    public int getTupleIndex(TupleDef tupleDef, VarDef parameter) {
963
        int i = 0;
×
964
        for (WParameter p : tupleDef.getParameters()) {
×
965
            if (p == parameter) {
×
966
                return i;
×
967
            }
968
            i++;
×
969
        }
×
970
        throw new Error("");
×
971
    }
972

973

974
    public ImVar getVarFor(VarDef varDef) {
975
        ImVar v = varMap.get(varDef);
1✔
976
        if (v == null) {
1✔
977
            ImType type = varDef.attrTyp().imTranslateType(this);
1✔
978
            String name = varDef.getName();
1✔
979
            if (isNamedScopeVar(varDef)) {
1✔
980
                name = getNameFor(varDef.attrNearestNamedScope()) + "_" + name;
1✔
981
            }
982
            boolean isBj = isBJ(varDef.getSource());
1✔
983
            v = JassIm.ImVar(varDef, type, name, isBj);
1✔
984
            varMap.put(varDef, v);
1✔
985
        }
986
        return v;
1✔
987
    }
988

989
    private boolean isNamedScopeVar(VarDef varDef) {
990
        if (varDef.getParent() == null) {
1✔
991
            return false;
×
992
        }
993
        return varDef.getParent().getParent() instanceof NamedScope;
1✔
994
    }
995

996
    public WurstModel getWurstProg() {
997
        return wurstProg;
×
998
    }
999

1000
    public ImProg imProg() {
1001
        return imProg;
1✔
1002
    }
1003

1004
    public boolean isTranslated(WPackage pack) {
1005
        return translatedPackages.contains(pack);
1✔
1006
    }
1007

1008
    public void setTranslated(WPackage pack) {
1009
        translatedPackages.add(pack);
1✔
1010
    }
1✔
1011

1012
    public boolean isTranslated(ClassDef c) {
1013
        return translatedClasses.contains(c);
1✔
1014
    }
1015

1016
    public void setTranslated(ClassDef c) {
1017
        translatedClasses.add(c);
1✔
1018
    }
1✔
1019

1020
    public List<ImStmt> translateStatements(ImFunction f, List<WStatement> statements) {
1021
        List<ImStmt> result = Lists.newArrayList();
1✔
1022
        for (WStatement s : statements) {
1✔
1023
            lasttranslatedThing = s;
1✔
1024
            ImStmt translated = s.imTranslateStmt(this, f);
1✔
1025
            result.add(translated);
1✔
1026
        }
1✔
1027
        return result;
1✔
1028
    }
1029

1030
    public void setMainFunc(ImFunction f) {
1031
        if (mainFunc != null) {
1✔
1032
            throw new Error("mainFunction already set");
×
1033
        }
1034
        mainFunc = f;
1✔
1035
    }
1✔
1036

1037
    public void setConfigFunc(ImFunction f) {
1038
        if (configFunc != null) {
1✔
1039
            throw new Error("configFunction already set");
×
1040
        }
1041
        configFunc = f;
1✔
1042
    }
1✔
1043

1044
    public Multimap<ImFunction, ImFunction> getCalledFunctions() {
1045
        if (callRelations == null) {
1✔
1046
            calculateCallRelationsAndUsedVariables();
×
1047
        }
1048
        return callRelations;
1✔
1049
    }
1050

1051

1052

1053
    public void calculateCallRelationsAndUsedVariables() {
1054
        // estimate sizes to reduce rehashing
1055
        final int funcEstimate = Math.max(16, imProg.getFunctions().size());
1✔
1056
        final int varEstimate  = Math.max(32, imProg.getGlobals().size());
1✔
1057

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

1060
        usedFunctions = new ReferenceOpenHashSet<>(funcEstimate);
1✔
1061
        usedVariables = new ObjectOpenHashSet<>(varEstimate);
1✔
1062
        readVariables = new ObjectOpenHashSet<>(varEstimate);
1✔
1063

1064
        final ImFunction main = getMainFunc();
1✔
1065
        if (main != null) calculateCallRelations(main);
1✔
1066

1067
        final ImFunction conf = getConfFunc();
1✔
1068
        if (conf != null && conf != main) calculateCallRelations(conf);
1✔
1069

1070
        // mark protected globals as read
1071
        // TRVEHelper.protectedVariables is presumably a HashSet<String> (O(1) contains)
1072
        for (ImVar global : imProg.getGlobals()) {
1✔
1073
            if (TRVEHelper.protectedVariables.contains(global.getName())) {
1✔
1074
                readVariables.add(global);
1✔
1075
            }
1076
        }
1✔
1077
    }
1✔
1078

1079
    private void calculateCallRelations(ImFunction rootFunction) {
1080
        // nothing to do
1081
        if (rootFunction == null) return;
1✔
1082

1083
        // if already processed, skip entirely
1084
        if (usedFunctions.contains(rootFunction)) return;
1✔
1085

1086
        final ArrayDeque<ImFunction> work = new ArrayDeque<>();
1✔
1087
        work.add(rootFunction);
1✔
1088

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

1092
            if (!usedFunctions.add(f)) {
1✔
1093
                // was already processed; skip
1094
                continue;
1✔
1095
            }
1096

1097
            // Only computed once per function thanks to usedFunctions.add() gate
1098
            usedVariables.addAll(f.calcUsedVariables());
1✔
1099
            readVariables.addAll(f.calcReadVariables());
1✔
1100

1101
            final Set<ImFunction> called = f.calcUsedFunctions();
1✔
1102
            // Avoid streams/alloc; avoid pushing functions we've already seen
1103
            for (ImFunction g : called) {
1✔
1104
                if (g == null) continue;
1✔
1105
                if (g != f) { // ignore self-calls in relation
1✔
1106
                    callRelations.put(f, g);
1✔
1107
                }
1108
                if (!usedFunctions.contains(g)) {
1✔
1109
                    work.add(g);
1✔
1110
                }
1111
            }
1✔
1112
        }
1✔
1113
    }
1✔
1114

1115
    private Multimap<ImFunction, ImFunction> getCallRelations() {
1116
        return callRelations;
×
1117
    }
1118

1119
    public ImFunction getMainFunc() { return mainFunc; }
1✔
1120
    public ImFunction getConfFunc() { return configFunc; }
1✔
1121

1122

1123

1124
    /**
1125
     * returns a list of classes and functions implementing funcDef
1126
     */
1127
    public Map<ClassDef, FuncDef> getClassesWithImplementation(Collection<ClassDef> instances, FuncDef func) {
1128
        if (func.attrIsPrivate()) {
1✔
1129
            // private functions cannot be overridden
1130
            return Collections.emptyMap();
1✔
1131
        }
1132
        Map<ClassDef, FuncDef> result = Maps.newLinkedHashMap();
1✔
1133
        for (ClassDef c : instances) {
1✔
1134
            FuncLink funcNameLink = null;
1✔
1135
            WurstTypeClass cType = c.attrTypC();
1✔
1136
            for (FuncLink nameLink : func.lookupMemberFuncs(cType, func.getName())) {
1✔
1137
                if (nameLink.getDef() == func) {
1✔
1138
                    funcNameLink = nameLink;
1✔
1139
                }
1140
            }
1✔
1141
            if (funcNameLink == null) {
1✔
1142
                throw new Error("Could not find "
×
1143
                    + Utils.printElementWithSource(Optional.of(func))
×
1144
                    + " in "
1145
                    + Utils.printElementWithSource(Optional.of(c)));
×
1146
            }
1147
            for (NameLink nameLink : c.attrNameLinks().get(func.getName())) {
1✔
1148
                NameDef nameDef = nameLink.getDef();
1✔
1149
                if (nameLink.getDefinedIn() == c) {
1✔
1150
                    if (nameLink instanceof FuncLink && nameLink.getDef() instanceof FuncDef) {
1✔
1151
                        FuncLink funcLink = (FuncLink) nameLink;
1✔
1152
                        FuncDef f = (FuncDef) funcLink.getDef();
1✔
1153
                        // check if function f overrides func
1154
                        if (WurstValidator.canOverride(funcLink, funcNameLink, false)) {
1✔
1155
                            result.put(c, f);
1✔
1156
                        }
1157
                    }
1158
                }
1159
            }
1✔
1160
        }
1✔
1161
        return result;
1✔
1162
    }
1163

1164

1165
    private final Map<ClassDef, List<Pair<ImVar, VarInitialization>>> classDynamicInitMap = Maps.newLinkedHashMap();
1✔
1166
    private final Map<ClassDef, List<WStatement>> classInitStatements = Maps.newLinkedHashMap();
1✔
1167

1168
    public List<Pair<ImVar, VarInitialization>> getDynamicInits(ClassDef c) {
1169
        return classDynamicInitMap.computeIfAbsent(c, k -> Lists.newArrayList());
1✔
1170
    }
1171

1172

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

1175
    public ImFunction getConstructFunc(ConstructorDef constr) {
1176
        ImFunction f = constructorFuncs.get(constr);
1✔
1177
        if (f == null) {
1✔
1178
            String name = constructorName(constr);
1✔
1179
            ImVars params = ImVars(getThisVar(constr));
1✔
1180
            for (WParameter p : constr.getParameters()) {
1✔
1181
                params.add(getVarFor(p));
1✔
1182
            }
1✔
1183

1184
            f = ImFunction(constr, name, ImTypeVars(), params, ImVoid(), ImVars(), ImStmts(), flags());
1✔
1185
            addFunction(f, constr);
1✔
1186
            constructorFuncs.put(constr, f);
1✔
1187
        }
1188
        return f;
1✔
1189
    }
1190

1191

1192
    private String constructorName(ConstructorDef constr) {
1193
        ArrayDeque<String> names = new ArrayDeque<>();
1✔
1194
        de.peeeq.wurstscript.ast.Element e = constr;
1✔
1195
        while (e != null) {
1✔
1196
            if (e instanceof ClassOrModuleInstanciation) {
1✔
1197
                ClassOrModuleInstanciation mi = (ClassOrModuleInstanciation) e;
1✔
1198
                int index = mi.getConstructors().indexOf(constr);
1✔
1199
                names.addFirst(mi.getName() + (index > 0 ? 1 + index : ""));
1✔
1200
                if (e instanceof ClassDef) {
1✔
1201
                    break;
1✔
1202
                }
1203
            }
1204
            e = e.getParent();
1✔
1205
        }
1206
        return "construct_" + String.join("_", names);
1✔
1207
    }
1208

1209

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

1212
    public ImFunction getConstructNewFunc(ConstructorDef constr) {
1213
        ImFunction f = constrNewFuncs.get(constr);
1✔
1214
        if (f == null) {
1✔
1215
            String name = "new_" + constr.attrNearestClassDef().getName();
1✔
1216

1217
            f = ImFunction(constr, name, ImTypeVars(), ImVars(), selfType(constr.attrNearestClassOrInterface()), ImVars(), ImStmts(), flags());
1✔
1218
            addFunction(f, constr);
1✔
1219
            constrNewFuncs.put(constr, f);
1✔
1220
        }
1221
        return f;
1✔
1222
    }
1223

1224
    public ImProg getImProg() {
1225
        return imProg;
1✔
1226
    }
1227

1228

1229
    private Multimap<InterfaceDef, ClassDef> interfaceInstances = null;
1✔
1230

1231
    public Collection<ClassDef> getInterfaceInstances(InterfaceDef interfaceDef) {
1232
        if (interfaceInstances == null) {
1✔
1233
            calculateInterfaceInstances();
1✔
1234
        }
1235
        return interfaceInstances.get(interfaceDef);
1✔
1236
    }
1237

1238
    private void calculateInterfaceInstances() {
1239
        interfaceInstances = HashMultimap.create();
1✔
1240
        for (CompilationUnit cu : wurstProg) {
1✔
1241
            for (ClassDef c : cu.attrGetByType().classes) {
1✔
1242
                for (WurstTypeInterface i : c.attrTypC().transitiveSuperInterfaces()) {
1✔
1243
                    interfaceInstances.put(i.getDef(), c);
1✔
1244
                }
1✔
1245
            }
1✔
1246
        }
1✔
1247
    }
1✔
1248

1249

1250
    private TransitiveClosure<ClassDef> subclasses = null;
1✔
1251
    private Multimap<ClassDef, ClassDef> directSubclasses = null;
1✔
1252

1253
    private boolean isEclipseMode = false;
1✔
1254

1255
    public List<ClassDef> getSubClasses(ClassDef classDef) {
1256
        calculateSubclasses();
1✔
1257
        return subclasses.getAsList(classDef);
1✔
1258
    }
1259

1260
    private void calculateSubclasses() {
1261
        if (subclasses != null) {
1✔
1262
            return;
1✔
1263
        }
1264
        calculateDirectSubclasses();
1✔
1265
        subclasses = new TransitiveClosure<>(directSubclasses);
1✔
1266
    }
1✔
1267

1268

1269
    private void calculateDirectSubclasses() {
1270
        if (directSubclasses != null) {
1✔
1271
            return;
×
1272
        }
1273
        directSubclasses = HashMultimap.create();
1✔
1274
        for (ClassDef c : classes()) {
1✔
1275
            WurstTypeClass extendedClass = c.attrTypC().extendedClass();
1✔
1276
            if (extendedClass != null) {
1✔
1277
                directSubclasses.put(extendedClass.getDef(), c);
1✔
1278
            }
1279
        }
1✔
1280
    }
1✔
1281

1282
    /**
1283
     * calculates list of all classes
1284
     * ignoring the ones in modules, only module instantiations
1285
     */
1286
    private List<ClassDef> classes() {
1287
        List<ClassDef> result = new ArrayList<>();
1✔
1288
        for (CompilationUnit cu : wurstProg) {
1✔
1289
            for (WPackage p : cu.getPackages()) {
1✔
1290
                for (WEntity e : p.getElements()) {
1✔
1291
                    if (e instanceof ClassDef) {
1✔
1292
                        ClassDef c = (ClassDef) e;
1✔
1293
                        classesAdd(result, c);
1✔
1294
                    }
1295
                }
1✔
1296
            }
1✔
1297
        }
1✔
1298
        return result;
1✔
1299

1300
    }
1301

1302
    private void classesAdd(List<ClassDef> result, ClassOrModuleInstanciation c) {
1303
        if (c instanceof ClassDef) {
1✔
1304
            result.add(((ClassDef) c));
1✔
1305
        }
1306
        for (ClassDef ic : c.getInnerClasses()) {
1✔
1307
            classesAdd(result, ic);
1✔
1308
        }
1✔
1309
        for (ModuleInstanciation mi : c.getModuleInstanciations()) {
1✔
1310
            classesAdd(result, mi);
1✔
1311
        }
1✔
1312
    }
1✔
1313

1314
    public int getEnumMemberId(EnumMember enumMember) {
1315
        return ((EnumMembers) enumMember.getParent()).indexOf(enumMember);
1✔
1316
    }
1317

1318
    private ImFunction getDebugPrintFunction() {
1319
        return debugPrintFunction;
1✔
1320
    }
1321

1322
    public boolean isEclipseMode() {
1323
        return isEclipseMode;
1✔
1324
    }
1325

1326
    public void setEclipseMode(boolean enabled) {
1327
        isEclipseMode = enabled;
×
1328
    }
×
1329

1330
    public TypeParamDef getTypeParamDef(ImTypeVar tv) {
1331
        return typeVariableReverse.get(tv);
1✔
1332
    }
1333

1334
    public ImTypeVar getTypeVar(TypeParamDef tv) {
1335
        return typeVariable.getFor(tv);
1✔
1336
    }
1337

1338
    public boolean isLuaTarget() {
1339
        return runArgs.isLua();
1✔
1340
    }
1341

1342
    interface VarsForTupleResult {
1343

1344
        default Iterable<ImVar> allValues() {
1345
            return allValuesStream()::iterator;
1✔
1346
        }
1347

1348
        Stream<ImVar> allValuesStream();
1349

1350
        <T> T map(Function<Stream<T>, T> nodeBuilder, Function<ImVar, T> leafBuilder);
1351
    }
1352

1353
    static class SingleVarResult implements VarsForTupleResult {
1354
        private final ImVar var;
1355

1356
        public SingleVarResult(ImVar var) {
1✔
1357
            this.var = var;
1✔
1358
        }
1✔
1359

1360
        public ImVar getVar() {
1361
            return var;
×
1362
        }
1363

1364
        @Override
1365
        public Stream<ImVar> allValuesStream() {
1366
            return Stream.of(var);
1✔
1367
        }
1368

1369
        @Override
1370
        public <T> T map(Function<Stream<T>, T> nodeBuilder, Function<ImVar, T> leafBuilder) {
1371
            return leafBuilder.apply(var);
1✔
1372
        }
1373

1374
        @Override
1375
        public String toString() {
1376
            return var.toString();
×
1377
        }
1378
    }
1379

1380
    static class TupleResult implements VarsForTupleResult {
1381
        private final List<VarsForTupleResult> items;
1382

1383
        public TupleResult(List<VarsForTupleResult> items) {
1✔
1384
            this.items = items;
1✔
1385
        }
1✔
1386

1387
        public List<VarsForTupleResult> getItems() {
1388
            return items;
×
1389
        }
1390

1391
        @Override
1392
        public Stream<ImVar> allValuesStream() {
1393
            return items.stream().flatMap(VarsForTupleResult::allValuesStream);
1✔
1394
        }
1395

1396
        @Override
1397
        public <T> T map(Function<Stream<T>, T> nodeBuilder, Function<ImVar, T> leafBuilder) {
1398
            return nodeBuilder.apply(items.stream().map(e -> e.map(nodeBuilder, leafBuilder)));
1✔
1399
        }
1400

1401
        @Override
1402
        public String toString() {
1403
            return "<" + Utils.printSep(", ", items) + ">";
×
1404
        }
1405
    }
1406

1407
    public VarsForTupleResult getVarsForTuple(ImVar v) {
1408
        // TODO use list instead of tree
1409
        VarsForTupleResult result = varsForTupleVar.get(v);
1✔
1410
        if (result == null) {
1✔
1411
            if (TypesHelper.typeContainsTuples(v.getType())) {
1✔
1412
                result = createVarsForType(v.getName(), v.getType(), Function.identity(), v.getTrace());
1✔
1413
            } else {
1414
                result = new SingleVarResult(v);
×
1415
            }
1416
            varsForTupleVar.put(v, result);
1✔
1417
        }
1418
        return result;
1✔
1419
    }
1420

1421

1422
    /**
1423
     * Creates variables for the given type, eliminating tuple types
1424
     *
1425
     * @param name            base name for the variables
1426
     * @param type            the type for which to create variables
1427
     * @param typeConstructor how the types are constructed (creating an array or multi-array, just returning the type)
1428
     * @param tr              trace for the variables
1429
     * @return
1430
     */
1431
    private VarsForTupleResult createVarsForType(String name, final ImType type, Function<ImType, ImType> typeConstructor, de.peeeq.wurstscript.ast.Element tr) {
1432
        return type.match(new ImType.Matcher<VarsForTupleResult>() {
1✔
1433
            @Override
1434
            public VarsForTupleResult case_ImArrayType(ImArrayType at) {
1435
                if (at.getEntryType() instanceof ImTupleType) {
1✔
1436
                    // if it is an array of tuples, create multiple array variables:
1437
                    ImTupleType tt = (ImTupleType) at.getEntryType();
1✔
1438
                    Builder<VarsForTupleResult> ts = ImmutableList.builder();
1✔
1439
                    int i = 0;
1✔
1440
                    for (ImType t : tt.getTypes()) {
1✔
1441
                        ts.add(createVarsForType(name + "_" + tt.getNames().get(i), t, JassIm::ImArrayType, tr));
1✔
1442
                        i++;
1✔
1443
                    }
1✔
1444
                    return new TupleResult(ts.build());
1✔
1445
                }
1446
                // otherwise just create the array variable
1447
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1448
            }
1449

1450
            @Override
1451
            public VarsForTupleResult case_ImTypeVarRef(ImTypeVarRef imTypeVarRef) {
1452
                throw new RuntimeException("Should be called after eliminating generics.");
×
1453
            }
1454

1455
            @Override
1456
            public VarsForTupleResult case_ImArrayTypeMulti(ImArrayTypeMulti at) {
1457
                if (at.getEntryType() instanceof ImTupleType) {
1✔
1458
                    // if it is an array of tuples, create multiple array variables:
1459
                    ImTupleType tt = (ImTupleType) at.getEntryType();
1✔
1460
                    Builder<VarsForTupleResult> ts = ImmutableList.builder();
1✔
1461
                    int i = 0;
1✔
1462
                    for (ImType t : tt.getTypes()) {
1✔
1463
                        ts.add(createVarsForType(name + "_" + tt.getNames().get(i), t, et -> JassIm.ImArrayTypeMulti(et, new ArrayList<>(at.getArraySize())), tr));
1✔
1464
                        i++;
1✔
1465
                    }
1✔
1466
                    return new TupleResult(ts.build());
1✔
1467
                }
1468
                // otherwise just create the array variable
1469
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1470
            }
1471

1472
            @Override
1473
            public VarsForTupleResult case_ImVoid(ImVoid imVoid) {
1474
                return new TupleResult(Collections.emptyList());
×
1475
            }
1476

1477
            @Override
1478
            public VarsForTupleResult case_ImClassType(ImClassType st) {
1479
                ImType type = typeConstructor.apply(st);
×
1480
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1481
            }
1482

1483
            @Override
1484
            public VarsForTupleResult case_ImSimpleType(ImSimpleType st) {
1485
                ImType type = typeConstructor.apply(st);
1✔
1486
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
1✔
1487
            }
1488

1489
            @Override
1490
            public VarsForTupleResult case_ImAnyType(ImAnyType at) {
1491
                ImType type = typeConstructor.apply(at);
×
1492
                return new SingleVarResult(JassIm.ImVar(tr, type, name, false));
×
1493
            }
1494

1495
            @Override
1496
            public VarsForTupleResult case_ImTupleType(ImTupleType tt) {
1497
                int i = 0;
1✔
1498
                Builder<VarsForTupleResult> ts = ImmutableList.builder();
1✔
1499
                for (ImType t : tt.getTypes()) {
1✔
1500
                    ts.add(createVarsForType(name + "_" + tt.getNames().get(i), t, typeConstructor, tr));
1✔
1501
                    i++;
1✔
1502
                }
1✔
1503
                return new TupleResult(ts.build());
1✔
1504
            }
1505
        });
1506
    }
1507

1508

1509
    private void addVarsForType(List<ImVar> result, String name, ImType type, de.peeeq.wurstscript.ast.Element tr) {
1510
        Preconditions.checkNotNull(type);
×
1511
        Preconditions.checkNotNull(result);
×
1512
        // TODO handle names
1513
        if (type instanceof ImTupleType) {
×
1514
            ImTupleType tt = (ImTupleType) type;
×
1515
            int i = 0;
×
1516
            for (ImType t : tt.getTypes()) {
×
1517
                addVarsForType(result, name + "_" + tt.getNames().get(i), t, tr);
×
1518
                i++;
×
1519
            }
×
1520
        } else if (type instanceof ImVoid) {
×
1521
            // nothing to add
1522
        } else {
1523
            result.add(JassIm.ImVar(tr, type, name, false));
×
1524
        }
1525

1526
    }
×
1527

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

1530
    public VarsForTupleResult getTupleTempReturnVarsFor(ImFunction f) {
1531
        VarsForTupleResult result = tempReturnVars.get(f);
1✔
1532
        if (result == null) {
1✔
1533
            result = createVarsForType(f.getName() + "_return", getOriginalReturnValue(f), Function.identity(), f.getTrace());
1✔
1534
            for (ImVar value : result.allValues()) {
1✔
1535
                imProg.getGlobals().add(value);
1✔
1536
            }
1✔
1537
            tempReturnVars.put(f, result);
1✔
1538
        }
1539
        return result;
1✔
1540
    }
1541

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

1544

1545
    public void setOriginalReturnValue(ImFunction f, ImType t) {
1546
        originalReturnValues.put(f, t);
1✔
1547
    }
1✔
1548

1549
    public ImType getOriginalReturnValue(ImFunction f) {
1550
        return originalReturnValues.computeIfAbsent(f, ImFunction::getReturnType);
1✔
1551
    }
1552

1553
    public void assertProperties(AssertProperty... properties1) {
1554
        if (!debug) {
1555
            return;
1✔
1556
        }
1557
        final Set<AssertProperty> properties = Sets.newHashSet(properties1);
1558
        assertProperties(properties, imProg);
1559
    }
1560

1561
    public void assertProperties(Set<AssertProperty> properties, Element e) {
1562
        if (e instanceof ElementWithVar) {
1✔
1563
            checkVar(((ElementWithVar) e).getVar(), properties);
1✔
1564
        }
1565
        properties.parallelStream().forEach(p -> p.check(e));
1✔
1566
        if (properties.contains(AssertProperty.NOTUPLES)) {
1✔
1567
            // TODO ?
1568
        }
1569
        if (properties.contains(AssertProperty.FLAT)) {
1✔
1570
            // TODO ?
1571
        }
1572
        for (int i = 0; i < e.size(); i++) {
1✔
1573
            Element child = e.get(i);
1✔
1574
            if (child.getParent() == null) {
1✔
1575
                throw new Error("Child " + i + " (" + child + ") of " + e + " not attached to tree");
×
1576
            } else if (child.getParent() != e) {
1✔
1577
                throw new Error("Child " + i + " (" + child + ") of " + e + " attached to wrong tree");
×
1578
            }
1579
            assertProperties(properties, child);
1✔
1580
        }
1581
    }
1✔
1582

1583
    private void checkVar(ImVar left, Set<AssertProperty> properties) {
1584
        if (left.getParent() == null) {
1✔
1585
            throw new Error("var not attached: " + left);
×
1586
        }
1587
        if (properties.contains(AssertProperty.NOTUPLES)) {
1✔
1588
            if (TypesHelper.typeContainsTuples(left.getType())) {
×
1589
                throw new Error("program contains tuple var " + left);
×
1590
            }
1591
        }
1592
    }
1✔
1593

1594
    public Set<ImVar> getUsedVariables() {
1595
        if (usedVariables == null) {
×
1596
            calculateCallRelationsAndUsedVariables();
×
1597
        }
1598
        return usedVariables;
×
1599
    }
1600

1601
    public Set<ImVar> getReadVariables() {
1602
        if (readVariables == null) {
1✔
1603
            calculateCallRelationsAndUsedVariables();
×
1604
        }
1605
        return readVariables;
1✔
1606
    }
1607

1608
    public Set<ImFunction> getUsedFunctions() {
1609
        if (usedFunctions == null) {
1✔
1610
            calculateCallRelationsAndUsedVariables();
×
1611
        }
1612
        return usedFunctions;
1✔
1613
    }
1614

1615
    public boolean isUnitTestMode() {
1616
        return isUnitTestMode;
1✔
1617
    }
1618

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

1621
    public ImClass getClassForClosure(ExprClosure s) {
1622
        Preconditions.checkNotNull(s);
1✔
1623
        return classForClosure.computeIfAbsent(s, s1 -> JassIm.ImClass(s1, "Closure", JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList()));
1✔
1624
    }
1625

1626

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

1629
    public ImClass getClassFor(ClassOrInterface s) {
1630
        Preconditions.checkNotNull(s);
1✔
1631
        return classForStructureDef.computeIfAbsent(s, s1 -> {
1✔
1632
            ImTypeVars typeVariables = JassIm.ImTypeVars();
1✔
1633
            if (s instanceof AstElementWithTypeParameters) {
1✔
1634
                for (TypeParamDef tp : ((AstElementWithTypeParameters) s).getTypeParameters()) {
1✔
1635
                    if (tp.getTypeParamConstraints() instanceof TypeExprList) {
1✔
1636
                        ImTypeVar tv = getTypeVar(tp);
1✔
1637
                        typeVariables.add(tv);
1✔
1638
                    }
1639
                }
1✔
1640
            }
1641

1642
            return JassIm.ImClass(s1, s1.getName(), typeVariables, JassIm.ImVars(), JassIm.ImMethods(), JassIm.ImFunctions(), Lists.newArrayList());
1✔
1643
        });
1644
    }
1645

1646

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

1649
    public ImMethod getMethodFor(FuncDef f) {
1650
        ImMethod m = methodForFuncDef.get(f);
1✔
1651
        if (m == null) {
1✔
1652
            ImFunction imFunc = getFuncFor(f);
1✔
1653
            m = JassIm.ImMethod(f, selfType(f), elementNameWithPath(f), imFunc, Lists.newArrayList(), false);
1✔
1654
            methodForFuncDef.put(f, m);
1✔
1655
        }
1656
        return m;
1✔
1657
    }
1658

1659
    public ClassManagementVars getClassManagementVarsFor(ImClass c) {
1660
        return getClassManagementVars().get(c);
1✔
1661
    }
1662

1663

1664
    private Map<ImClass, ClassManagementVars> classManagementVars = null;
1✔
1665

1666
    private @Nullable ImFunction errorFunc;
1667

1668
    public Map<ImClass, ClassManagementVars> getClassManagementVars() {
1669
        if (classManagementVars != null) {
1✔
1670
            return classManagementVars;
1✔
1671
        }
1672
        // create partitions, such that each sub-class and super-class are in
1673
        // the same partition
1674
        Partitions<ImClass> p = new Partitions<>();
1✔
1675
        for (ImClass c : imProg.getClasses()) {
1✔
1676
            p.add(c);
1✔
1677
            for (ImClassType sc : c.getSuperClasses()) {
1✔
1678
                p.union(c, sc.getClassDef());
1✔
1679
            }
1✔
1680
        }
1✔
1681
        // generate typeId variables
1682
        classManagementVars = Maps.newLinkedHashMap();
1✔
1683
        for (ImClass c : imProg.getClasses()) {
1✔
1684
            ImClass rep = p.getRep(c);
1✔
1685
            ClassManagementVars v = classManagementVars.computeIfAbsent(rep, r -> new ClassManagementVars(r, this));
1✔
1686
            classManagementVars.put(c, v);
1✔
1687
        }
1✔
1688
        return classManagementVars;
1✔
1689
    }
1690

1691

1692
    public ImFunction getGlobalInitFunc() {
1693
        return globalInitFunc;
1✔
1694
    }
1695

1696

1697
    public ImFunctionCall imError(de.peeeq.wurstscript.ast.Element trace, ImExpr message) {
1698
        ImFunction ef = errorFunc;
1✔
1699
        if (ef == null) {
1✔
1700
            Optional<ImFunction> f = findErrorFunc().map(this::getFuncFor);
1✔
1701
            ef = errorFunc = f.orElseGet(this::makeDefaultErrorFunc);
1✔
1702
        }
1703
        ImExprs arguments = JassIm.ImExprs(message);
1✔
1704
        return ImFunctionCall(trace, ef, ImTypeArguments(), arguments, false, CallType.NORMAL);
1✔
1705
    }
1706

1707
    private ImFunction makeDefaultErrorFunc() {
1708
        ImVar msgVar = JassIm.ImVar(emptyTrace, TypesHelper.imString(), "msg", false);
1✔
1709
        ImVars parameters = JassIm.ImVars(msgVar);
1✔
1710
        ImType returnType = JassIm.ImVoid();
1✔
1711
        ImVars locals = JassIm.ImVars();
1✔
1712
        ImStmts body = JassIm.ImStmts();
1✔
1713

1714
        // print message:
1715
        // msg = msg + stacktrace
1716
        ImExpr msg = JassIm.ImOperatorCall(WurstOperator.PLUS, ImExprs(JassIm.ImVarAccess(msgVar),
1✔
1717
            JassIm.ImOperatorCall(WurstOperator.PLUS,
1✔
1718
                ImExprs(
1✔
1719
                    JassIm.ImStringVal("\n"),
1✔
1720
                    JassIm.ImGetStackTrace()))));
1✔
1721

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

1725

1726
//                stmts.add(JassAst.JassStmtCall("BJDebugMsg",
1727
//                                JassAst.JassExprlist(JassAst.JassExprBinary(
1728
//                                                JassAst.JassExprStringVal("|cffFF3A29Wurst Error:|r" + nl),
1729
//                                                JassAst.JassOpPlus(),
1730
//                                                s.getMessage().translate(translator)))));
1731
//                // crash thread (divide by zero)
1732
//                stmts.add(JassAst.JassStmtCall("I2S", JassAst.JassExprlist(JassAst.JassExprBinary(JassAst.JassExprIntVal("1"), JassAst.JassOpDiv(), Jas
1733
//
1734

1735
        List<FunctionFlag> flags = Lists.newArrayList();
1✔
1736

1737
        ImFunction errorFunc = ImFunction(emptyTrace, "error", ImTypeVars(), parameters, returnType, locals, body, flags);
1✔
1738
        imProg.getFunctions().add(errorFunc);
1✔
1739
        return errorFunc;
1✔
1740
    }
1741

1742

1743
    private Optional<FuncDef> findErrorFunc() throws CompileError {
1744
        PackageLink p = wurstProg.lookupPackage("ErrorHandling");
1✔
1745
        if (p == null) {
1✔
1746
            return Optional.empty();
1✔
1747
        }
1748
        ImmutableCollection<FuncLink> funcs = p.getDef().getElements().lookupFuncs("error");
1✔
1749
        if (funcs.isEmpty()) {
1✔
1750
            return Optional.empty();
×
1751
        } else if (funcs.size() > 1) {
1✔
1752
            return Optional.empty();
×
1753
        }
1754
        FuncDef f = (FuncDef) funcs.stream().findAny().get().getDef();
1✔
1755
        return Optional.of(f);
1✔
1756
    }
1757

1758
    int getCompiletimeExpressionsOrder(FunctionCall fc) {
1759
        return compiletimeExpressionsOrder.getOrDefault(fc, 0);
1✔
1760
    }
1761

1762
    public RunArgs getRunArgs() {
1763
        return runArgs;
1✔
1764
    }
1765
}
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