Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

wurstscript / WurstScript / 1005

21 Mar 2019 - 22:31 coverage decreased (-0.09%) to 61.245%
1005

Pull #822

travis-ci

9181eb84f9c35729a3bad740fb7f9d93?size=18&default=identiconweb-flow
more new bug fixes
Pull Request #822: WIP: support for persisting objects in compiletime expressions

14931 of 24379 relevant lines covered (61.25%)

0.61 hits per line

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

97.6
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateClasses.java
1
package de.peeeq.wurstscript.translation.imtranslation;
2

3
import com.google.common.collect.Lists;
4
import com.google.common.collect.Maps;
5
import de.peeeq.wurstscript.WurstOperator;
6
import de.peeeq.wurstscript.ast.AstElementWithNameId;
7
import de.peeeq.wurstscript.ast.Element;
8
import de.peeeq.wurstscript.attributes.CompileError;
9
import de.peeeq.wurstscript.jassIm.*;
10
import de.peeeq.wurstscript.translation.imtojass.TypeRewriteMatcher;
11
import de.peeeq.wurstscript.translation.imtojass.TypeRewriter;
12
import de.peeeq.wurstscript.utils.Pair;
13

14
import java.util.ArrayList;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.stream.Collectors;
18

19
import static de.peeeq.wurstscript.types.TypesHelper.imInt;
20

21
/**
22
 * eliminate classes and dynamic method invocations
23
 */
24
public class EliminateClasses {
25

26
    private final ImTranslator translator;
27
    private final ImProg prog;
28
    private final Map<ImVar, ImVar> fieldToArray = Maps.newLinkedHashMap();
1×
29
    private final Map<ImMethod, ImFunction> dispatchFuncs = Maps.newLinkedHashMap();
1×
30
    private final RecycleCodeGenerator recycleCodeGen = new RecycleCodeGeneratorQueue();
1×
31
    private boolean checkedDispatch;
32

33
    public EliminateClasses(ImTranslator tr, ImProg prog, boolean checkedDispatch) {
1×
34
        translator = tr;
1×
35
        this.prog = prog;
1×
36
        this.checkedDispatch = checkedDispatch;
1×
37
    }
1×
38

39
    public void eliminateClasses() {
40
        moveFunctionsOutOfClasses();
1×
41

42
        for (ImClass c : prog.getClasses()) {
1×
43
            eliminateClass(c);
1×
44
        }
1×
45

46
        // for each method, create a dispatch function
47
        for (ImMethod m : prog.getMethods()) {
1×
48
            ImClass c = m.getMethodClass().getClassDef();
1×
49
            createDispatchFunc(c, m);
1×
50
        }
1×
51

52
        for (ImFunction f : prog.getFunctions()) {
1×
53
            eliminateClassRelatedExprs(f.getBody());
1×
54
        }
1×
55

56
        for (Map.Entry<ImVar, List<ImExpr>> entry : prog.getGlobalInits().entrySet()) {
1×
57
            ImExprs exprs = JassIm.ImExprs();
1×
58
            List<ImExpr> value = entry.getValue();
1×
59
            for (int i = 0, valueSize = value.size(); i < valueSize; i++) {
1×
60
                ImExpr e = value.get(i);
1×
61
                List<ImExpr> newValues = new ArrayList<>();
1×
62
                if (e.getParent() == null) {
1×
63
                    exprs.add(e);
1×
64
                    eliminateClassRelatedExprs(exprs);
1×
65
                    newValues.add(exprs.remove(0));
1×
66
                } else {
67
                    eliminateClassRelatedExprs(exprs);
1×
68
                    newValues.add(e);
1×
69
                }
70
                entry.setValue(newValues);
1×
71
            }
72
        }
1×
73

74
        prog.getClasses().clear();
1×
75
        prog.getMethods().clear();
1×
76

77
        eliminateClassTypes();
1×
78
    }
1×
79

80
    private void eliminateClassTypes() {
81
        TypeRewriter.rewriteTypes(prog, this::eliminateClassTypes);
1×
82
    }
1×
83

84
    private ImType eliminateClassTypes(ImType imType) {
85
        return imType.match(new TypeRewriteMatcher() {
1×
86
            @Override
87
            public ImType case_ImClassType(ImClassType t) {
88
                return imInt();
1×
89
            }
90
        });
91
    }
92

93

94
    /**
95
     * Move all the functions out of classes and into the global program
96
     */
97
    private void moveFunctionsOutOfClasses() {
98
        for (ImClass c : prog.getClasses()) {
1×
99
            prog.getFunctions().addAll(c.getFunctions().removeAll());
1×
100
        }
1×
101
    }
1×
102

103

104
    private void eliminateClass(ImClass c) {
105
        // for each field, create a global array variable
106
        for (ImVar f : c.getFields()) {
1×
107
            ImType type = ImHelper.toArray(f.getType());
1×
108
            ImVar v = JassIm
1×
109
                    .ImVar(f.getTrace(), type, f.getName(), false);
1×
110
            prog.getGlobals().add(v);
1×
111
            fieldToArray.put(f, v);
1×
112
        }
1×
113
        // for each method, create a dispatch function
114
        for (ImMethod m : c.getMethods()) {
1×
UNCOV
115
            createDispatchFunc(c, m);
!
UNCOV
116
        }
!
117

118
        // create management functions
119
        recycleCodeGen.createAllocFunc(translator, prog, c);
1×
120
        recycleCodeGen.createDeallocFunc(translator, prog, c);
1×
121
    }
1×
122

123

124
    public void createDispatchFunc(ImClass c, ImMethod m) {
125
        List<ImMethod> methods = Lists.newArrayList();
1×
126
        addSubMethods(m, methods);
1×
127

128
        List<Pair<IntRange, ImMethod>> ranges = calculateTypeIdRanges(c,
1×
129
                methods);
130

131

132
        List<FunctionFlag> flags = new ArrayList<>();
1×
133
        if (m.getImplementation().hasFlag(FunctionFlagEnum.IS_VARARG)) {
1×
134
            // if implementation has varargs, dispatch also needs varargs
135
            flags.add(FunctionFlagEnum.IS_VARARG);
1×
136
        }
137

138

139
        ImFunction df = JassIm.ImFunction(m.getTrace(), "dispatch_" + c.getName() + "_" + m.getName(), JassIm.ImTypeVars(), m
1×
140
                .getImplementation().getParameters().copy(), m
1×
141
                .getImplementation().getReturnType(), JassIm.ImVars(), JassIm
1×
142
                .ImStmts(), flags);
1×
143
        prog.getFunctions().add(df);
1×
144
        dispatchFuncs.put(m, df);
1×
145

146

147
        ImType returnType = df.getReturnType();
1×
148
        if (ranges.isEmpty()) {
1×
149
            // no implementations for method
150
            if (!(returnType instanceof ImVoid)) {
1×
151
                // return default value if it is not void
152
                ImExpr rv = ImHelper.defaultValueForComplexType(returnType);
1×
153
                df.getBody().add(JassIm.ImReturn(df.getTrace(), rv));
1×
154
            }
155
            return;
1×
156
        }
157

158

159
        ImVar resultVar;
160
        if (returnType instanceof ImVoid) {
1×
161
            resultVar = null;
1×
162
        } else {
163
            resultVar = JassIm.ImVar(df.getTrace(), returnType, m.getName()
1×
164
                    + "_result", false);
165
            df.getLocals().add(resultVar);
1×
166
        }
167

168
        ClassManagementVars mVars = translator.getClassManagementVarsFor(c);
1×
169
        ImVar thisVar = df.getParameters().get(0);
1×
170
        ImExpr typeId = JassIm.ImVarArrayAccess(m.getTrace(), mVars.typeId, JassIm.ImExprs((ImExpr) JassIm.ImVarAccess(thisVar)));
1×
171

172
        // ckeck if destroyed or nullpointer
173
        if (checkedDispatch) {
1×
174
            Element trace = m.attrTrace();
1×
175
            String methodName = getMethodName(m);
1×
176

177
            df.getBody().add(
1×
178
                    // if typeId[this] == 0
179
                    JassIm.ImIf(
1×
180
                            df.getTrace(), JassIm.ImOperatorCall(WurstOperator.EQ, JassIm.ImExprs(
1×
181
                                    (ImExpr) typeId.copy(), JassIm.ImIntVal(0)
1×
182
                            )),
183
                            // then
184
                            // if this == 0
185
                            JassIm.ImStmts(JassIm.ImIf(df.getTrace(), JassIm.ImOperatorCall(WurstOperator.EQ, JassIm.ImExprs(
1×
186
                                    JassIm.ImVarAccess(thisVar), JassIm.ImIntVal(0)
1×
187
                                    )),
188
                                    // then error(NPE)
189
                                    JassIm.ImStmts(translator.imError(trace, JassIm.ImStringVal("Nullpointer exception when calling " + c.getName() + "." + methodName)))
1×
190
                                    ,
191
                                    // else error(unallocated)
192
                                    JassIm.ImStmts(translator.imError(trace, JassIm.ImStringVal("Called " + c.getName() + "." + methodName + " on invalid object.")))
1×
193
                            ))
194

195
                            , JassIm.ImStmts())
1×
196
            );
197
        }
198

199

200
        createDispatch(df, df.getBody(), resultVar, typeId, ranges, 0,
1×
201
                ranges.size() - 1);
1×
202
        if (resultVar != null) {
1×
203
            df.getBody().add(
1×
204
                    JassIm.ImReturn(df.getTrace(),
1×
205
                            JassIm.ImVarAccess(resultVar)));
1×
206
        }
207
    }
1×
208

209
    private String getMethodName(ImMethod m) {
210
        Element trace = m.attrTrace();
1×
211
        String methodName = m.getName();
1×
212
        if (trace instanceof AstElementWithNameId) {
1×
213
            methodName = ((AstElementWithNameId) trace).getNameId().getName();
1×
214
        }
215
        return methodName;
1×
216
    }
217

218
    private void createDispatch(ImFunction df, ImStmts stmts, ImVar resultVar,
219
                                ImExpr typeId, List<Pair<IntRange, ImMethod>> ranges, int start,
220
                                int end) {
221
        if (start == end) {
1×
222
            ImExprs arguments = JassIm.ImExprs();
1×
223
            for (ImVar p : df.getParameters()) {
1×
224
                arguments.add(JassIm.ImVarAccess(p));
1×
225
            }
1×
226
            // only one method, call it
227
            ImFunctionCall call = JassIm.ImFunctionCall(df.getTrace(), ranges
1×
228
                    .get(start).getB().getImplementation(), JassIm.ImTypeArguments(), arguments, false, CallType.NORMAL);
1×
229
            if (resultVar == null) {
1×
230
                stmts.add(call);
1×
231
            } else {
232
                stmts.add(JassIm.ImSet(df.getTrace(), JassIm.ImVarAccess(resultVar), call));
1×
233
            }
234
        } else {
1×
235
            int mid = (start + end) / 2;
1×
236
            ImStmts thenBlock = JassIm.ImStmts();
1×
237
            ImStmts elseBlock = JassIm.ImStmts();
1×
238
            ImExpr condition = JassIm
1×
239
                    .ImOperatorCall(
1×
240
                            WurstOperator.LESS_EQ,
241
                            JassIm.ImExprs((ImExpr) typeId.copy(),
1×
242
                                    JassIm.ImIntVal(ranges.get(mid).getA().end)));
1×
243
            stmts.add(JassIm.ImIf(df.getTrace(), condition, thenBlock,
1×
244
                    elseBlock));
245

246
            createDispatch(df, thenBlock, resultVar, typeId, ranges, start, mid);
1×
247
            createDispatch(df, elseBlock, resultVar, typeId, ranges, mid + 1,
1×
248
                    end);
249
        }
250
    }
1×
251

252
    private void addSubMethods(ImMethod m, List<ImMethod> methods) {
253
        if (!m.getIsAbstract()) {
1×
254
            methods.add(m);
1×
255
        }
256
        for (ImMethod mm : m.getSubMethods()) {
1×
257
            addSubMethods(mm, methods);
1×
258
        }
1×
259
    }
1×
260

261
    /**
262
     * returns a mapping from classdefs to functions into a mapping from typeid
263
     * ranges to functions
264
     * <p>
265
     * the resulting list is sorted by the intrange and the intervals are
266
     * disjunct
267
     */
268
    private List<Pair<IntRange, ImMethod>> calculateTypeIdRanges(ImClass c,
269
                                                                 List<ImMethod> methods) {
270
        Map<Integer, ImMethod> typeIdToMethod = Maps.newLinkedHashMap();
1×
271
        calculateTypeIdToMethodHelper(c, methods, null, typeIdToMethod);
1×
272

273
        int min = Integer.MAX_VALUE;
1×
274
        int max = 0;
1×
275
        for (int i : typeIdToMethod.keySet()) {
1×
276
            min = Math.min(min, i);
1×
277
            max = Math.max(max, i);
1×
278
        }
1×
279

280
        List<Pair<IntRange, ImMethod>> result = Lists.newArrayList();
1×
281

282
        for (int i = min; i <= max; i++) {
1×
283
            ImMethod f = typeIdToMethod.get(i);
1×
284
            if (f == null) {
1×
UNCOV
285
                continue;
!
286
            }
287
            int j = i;
1×
288
            while (typeIdToMethod.get(j) == f) {
1×
289
                j++;
1×
290
            }
291
            result.add(Pair.create(new IntRange(i, j - 1), f));
1×
292
            i = j - 1;
1×
293
        }
294
        return result;
1×
295
    }
296

297
    private void calculateTypeIdToMethodHelper(ImClass c,
298
                                               List<ImMethod> methods, ImMethod current,
299
                                               Map<Integer, ImMethod> typeIdToMethod) {
300
        for (ImMethod m : methods) {
1×
301
            if (m.attrClass() == c) {
1×
302
                current = m;
1×
303
                break;
1×
304
            }
305
        }
1×
306
        if (current != null) {
1×
307
            typeIdToMethod.put(c.attrTypeId(), current);
1×
308
        }
309
        // process subclasses:
310
        for (ImClass sc : c.attrSubclasses()) {
1×
311
            calculateTypeIdToMethodHelper(sc, methods, current, typeIdToMethod);
1×
312
        }
1×
313
    }
1×
314

315
    private void eliminateClassRelatedExprs(de.peeeq.wurstscript.jassIm.Element body) {
316
        final List<ImMemberAccess> mas = Lists.newArrayList();
1×
317
        final List<ImMethodCall> mcs = Lists.newArrayList();
1×
318
        final List<ImAlloc> allocs = Lists.newArrayList();
1×
319
        final List<ImDealloc> deallocs = Lists.newArrayList();
1×
320
        final List<ImInstanceof> instaneofs = Lists.newArrayList();
1×
321
        final List<ImTypeIdOfObj> typeIdObjs = Lists.newArrayList();
1×
322
        final List<ImTypeIdOfClass> typeIdClasses = Lists.newArrayList();
1×
323
        body.accept(new de.peeeq.wurstscript.jassIm.Element.DefaultVisitor() {
1×
324
            @Override
325
            public void visit(ImMemberAccess e) {
326
                super.visit(e);
1×
327
                mas.add(e);
1×
328
            }
1×
329

330
            @Override
331
            public void visit(ImMethodCall e) {
332
                super.visit(e);
1×
333
                mcs.add(e);
1×
334
            }
1×
335

336
            @Override
337
            public void visit(ImAlloc e) {
338
                super.visit(e);
1×
339
                allocs.add(e);
1×
340
            }
1×
341

342
            @Override
343
            public void visit(ImDealloc e) {
344
                super.visit(e);
1×
345
                deallocs.add(e);
1×
346
            }
1×
347

348
            @Override
349
            public void visit(ImInstanceof e) {
350
                super.visit(e);
1×
351
                instaneofs.add(e);
1×
352
            }
1×
353

354
            @Override
355
            public void visit(ImTypeIdOfClass e) {
356
                super.visit(e);
1×
357
                typeIdClasses.add(e);
1×
358
            }
1×
359

360
            @Override
361
            public void visit(ImTypeIdOfObj e) {
362
                super.visit(e);
1×
363
                typeIdObjs.add(e);
1×
364
            }
1×
365
        });
366
        for (ImMemberAccess ma : mas) {
1×
367
            replaceMemberAccess(ma);
1×
368
        }
1×
369
        for (ImMethodCall mc : mcs) {
1×
370
            replaceMethodCall(mc);
1×
371
        }
1×
372
        for (ImAlloc imAlloc : allocs) {
1×
373
            replaceAlloc(imAlloc);
1×
374
        }
1×
375
        for (ImDealloc imDealloc : deallocs) {
1×
376
            replaceDealloc(imDealloc);
1×
377
        }
1×
378
        for (ImInstanceof e : instaneofs) {
1×
379
            replaceInstanceof(e);
1×
380
        }
1×
381
        for (ImTypeIdOfClass e : typeIdClasses) {
1×
382
            replaceTypeIdOfClass(e);
1×
383
        }
1×
384
        for (ImTypeIdOfObj e : typeIdObjs) {
1×
385
            replaceTypeIdOfObj(e);
1×
386
        }
1×
387
    }
1×
388

389
    private void replaceTypeIdOfObj(ImTypeIdOfObj e) {
390
        ImVar typeIdVar = translator.getClassManagementVarsFor(e.getClazz().getClassDef()).typeId;
1×
391
        ImExpr obj = e.getObj();
1×
392
        obj.setParent(null);
1×
393
        e.replaceBy(JassIm.ImVarArrayAccess(e.attrTrace(), typeIdVar, JassIm.ImExprs(obj)));
1×
394
    }
1×
395

396
    private void replaceTypeIdOfClass(ImTypeIdOfClass e) {
397
        e.replaceBy(JassIm.ImIntVal(e.getClazz().getClassDef().attrTypeId()));
1×
398
    }
1×
399

400
    private void replaceInstanceof(ImInstanceof e) {
401
        ImFunction f = e.getNearestFunc();
1×
402
        List<ImClass> allSubClasses = getAllSubclasses(e.getClazz().getClassDef());
1×
403
        List<Integer> subClassIds = allSubClasses.stream()
1×
404
                .map(ImClass::attrTypeId)
1×
405
                .collect(Collectors.toList());
1×
406
        List<IntRange> idRanges = IntRange.createFromIntList(subClassIds);
1×
407
        ImExpr obj = e.getObj();
1×
408
        obj.setParent(null);
1×
409
        ImVar typeIdVar = translator.getClassManagementVarsFor(e.getClazz().getClassDef()).typeId;
1×
410

411
        ImExpr objTypeId = JassIm.ImVarArrayAccess(e.attrTrace(), typeIdVar, JassIm.ImExprs(obj));
1×
412

413
        boolean useTempVar = idRanges.size() >= 2 || idRanges.get(0).start < idRanges.get(0).end;
1×
414
        ImVar tempVar = null;
1×
415
        ImExpr objTypeIdExpr = objTypeId;
1×
416
        if (useTempVar) {
1×
417
            // use temporary variable
418
            tempVar = JassIm.ImVar(e.attrTrace(), imInt(), "instanceOfTemp", false);
1×
419
            f.getLocals().add(tempVar);
1×
420
            objTypeIdExpr = JassIm.ImVarAccess(tempVar);
1×
421
        }
422
        ImExpr newExpr = null;
1×
423
        for (IntRange intRange : idRanges) {
1×
424
            newExpr = or(newExpr, inRange(objTypeIdExpr, intRange));
1×
425
        }
1×
426
        if (useTempVar) {
1×
427
            newExpr = JassIm.ImStatementExpr(JassIm.ImStmts(
1×
428
                    JassIm.ImSet(f.getTrace(), JassIm.ImVarAccess(tempVar), objTypeId)
1×
429
            ), newExpr);
430
        }
431
        e.replaceBy(newExpr);
1×
432
    }
1×
433

434
    private ImExpr or(ImExpr a, ImExpr b) {
435
        if (a == null && b == null) return null;
1×
436
        if (a == null) return b;
1×
UNCOV
437
        if (b == null) return a;
!
UNCOV
438
        return JassIm.ImOperatorCall(WurstOperator.OR, JassIm.ImExprs(a, b));
!
439
    }
440

441
    private ImExpr inRange(ImExpr obj, IntRange range) {
442
        if (range.start == range.end) {
1×
443
            return JassIm.ImOperatorCall(WurstOperator.EQ, JassIm.ImExprs((ImExpr) obj.copy(), JassIm.ImIntVal(range.start)));
1×
444
        } else {
445
            return JassIm.ImOperatorCall(WurstOperator.AND, JassIm.ImExprs(
1×
446
                    JassIm.ImOperatorCall(WurstOperator.GREATER_EQ, JassIm.ImExprs((ImExpr) obj.copy(), JassIm.ImIntVal(range.start))),
1×
447
                    JassIm.ImOperatorCall(WurstOperator.LESS_EQ, JassIm.ImExprs((ImExpr) obj.copy(), JassIm.ImIntVal(range.end)))));
1×
448
        }
449
    }
450

451
    private List<ImClass> getAllSubclasses(ImClass clazz) {
452
        List<ImClass> result = Lists.newArrayList();
1×
453
        getAllSubclassesH(result, clazz);
1×
454
        return result;
1×
455
    }
456

457
    private void getAllSubclassesH(List<ImClass> result, ImClass clazz) {
458
        result.add(clazz);
1×
459
        for (ImClass sc : clazz.attrSubclasses()) {
1×
460
            getAllSubclassesH(result, sc);
1×
461
        }
1×
462
    }
1×
463

464
    private void replaceDealloc(ImDealloc e) {
465
        ImFunction deallocFunc = translator.deallocFunc.getFor(e.getClazz().getClassDef());
1×
466
        ImExpr obj = e.getObj();
1×
467
        obj.setParent(null);
1×
468
        e.replaceBy(JassIm.ImFunctionCall(e.attrTrace(), deallocFunc, JassIm.ImTypeArguments(), JassIm.ImExprs(obj), false, CallType.NORMAL));
1×
469

470
    }
1×
471

472
    private void replaceAlloc(ImAlloc e) {
473
        ImFunction allocFunc = translator.allocFunc.getFor(e.getClazz().getClassDef());
1×
474
        e.replaceBy(JassIm.ImFunctionCall(e.attrTrace(), allocFunc, JassIm.ImTypeArguments(), JassIm.ImExprs(), false, CallType.NORMAL));
1×
475
    }
1×
476

477
    private void replaceMethodCall(ImMethodCall mc) {
478
        ImExpr receiver = mc.getReceiver();
1×
479
        receiver.setParent(null);
1×
480

481
        ImExprs arguments = JassIm.ImExprs(receiver);
1×
482
        arguments.addAll(mc.getArguments().removeAll());
1×
483

484
        ImFunction dispatch = dispatchFuncs.get(mc.getMethod());
1×
485
        if (dispatch == null) {
1×
UNCOV
486
            throw new CompileError(mc.attrTrace().attrSource(), "Could not find dispatch for " + mc.getMethod().getName());
!
487
        }
488
        mc.replaceBy(JassIm.ImFunctionCall(mc.getTrace(), dispatch, JassIm.ImTypeArguments(), arguments, false, CallType.NORMAL));
1×
489

490
    }
1×
491

492
    private void replaceMemberAccess(ImMemberAccess ma) {
493
        ImExpr receiver = ma.getReceiver();
1×
494
        receiver.setParent(null);
1×
495

496
        ImVar fieldArray = fieldToArray.get(ma.getVar());
1×
497
        if (fieldArray == null) {
1×
UNCOV
498
            throw new CompileError(ma, "Could not find field array for " + ma);
!
499
        }
500
        ImExprs indexes = JassIm.ImExprs(receiver);
1×
501
        indexes.addAll(ma.getIndexes().removeAll());
1×
502
        ma.replaceBy(JassIm.ImVarArrayAccess(ma.attrTrace(), fieldArray, indexes));
1×
503

504
    }
1×
505

506
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc