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

wurstscript / WurstScript / 271

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

Pull #1096

circleci

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

18202 of 28155 relevant lines covered (64.65%)

0.65 hits per line

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

74.23
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/SimpleRewrites.java
1
package de.peeeq.wurstscript.intermediatelang.optimizer;
2

3
import de.peeeq.wurstscript.WLogger;
4
import de.peeeq.wurstscript.WurstOperator;
5
import de.peeeq.wurstscript.jassIm.*;
6
import de.peeeq.wurstscript.translation.imoptimizer.OptimizerPass;
7
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
8
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
9

10
import java.text.DecimalFormat;
11
import java.text.DecimalFormatSymbols;
12
import java.util.Iterator;
13
import java.util.List;
14
import java.util.Locale;
15

16
public class SimpleRewrites implements OptimizerPass {
1✔
17
    private SideEffectAnalyzer sideEffectAnalysis;
18
    private int totalRewrites = 0;
1✔
19
    private final boolean showRewrites = false;
1✔
20

21
    private static boolean isNumberLiteral(ImExpr e) {
22
        return e instanceof ImIntVal || e instanceof ImRealVal;
1✔
23
    }
24

25
    private static float asFloat(ImExpr e) {
26
        if (e instanceof ImRealVal) {
1✔
27
            return Float.parseFloat(((ImRealVal) e).getValR());
1✔
28
        } else {
29
            return ((ImIntVal) e).getValI();
1✔
30
        }
31
    }
32

33
    @Override
34
    public int optimize(ImTranslator trans) {
35
        ImProg prog = trans.getImProg();
1✔
36
        this.sideEffectAnalysis = new SideEffectAnalyzer(prog);
1✔
37
        totalRewrites = 0;
1✔
38
        optimizeElement(prog);
1✔
39
        // we need to flatten the program, because we introduced new
40
        // StatementExprs
41
        prog.flatten(trans);
1✔
42
        removeUnreachableCode(prog);
1✔
43
        return totalRewrites;
1✔
44
    }
45

46
    @Override
47
    public String getName() {
48
        return "Simple Rewrites";
1✔
49
    }
50

51
    private void removeUnreachableCode(ImProg prog) {
52
        prog.accept(new ImProg.DefaultVisitor() {
1✔
53
            @Override
54
            public void visit(ImStmts stmts) {
55
                super.visit(stmts);
1✔
56
                removeUnreachableCode(stmts);
1✔
57
            }
1✔
58
        });
59
    }
1✔
60

61
    private void removeUnreachableCode(ImStmts stmts) {
62
        Iterator<ImStmt> it = stmts.iterator();
1✔
63
        boolean reachable = true;
1✔
64
        while (it.hasNext()) {
1✔
65
            ImStmt s = it.next();
1✔
66
            if (reachable) {
1✔
67
                if (s instanceof ImReturn) {
1✔
68
                    reachable = false;
1✔
69
                } else if (s instanceof ImExitwhen) {
1✔
70
                    ImExitwhen imExitwhen = (ImExitwhen) s;
1✔
71
                    ImExpr expr = imExitwhen.getCondition();
1✔
72
                    if (expr instanceof ImBoolVal) {
1✔
73
                        boolean b = ((ImBoolVal) expr).getValB();
1✔
74
                        if (b) {
1✔
75
                            // found "exitwhen true"
76
                            reachable = false;
1✔
77
                        }
78
                    }
79
                }
1✔
80
            } else {
81
                totalRewrites++;
1✔
82
                it.remove();
1✔
83
            }
84
        }
1✔
85
    }
1✔
86

87
    /**
88
     * Recursively optimizes the element
89
     */
90
    private void optimizeElement(Element elem) {
91
        // optimize children:
92
        for (int i = 0; i < elem.size(); i++) {
1✔
93
            optimizeElement(elem.get(i));
1✔
94
            if (i > 0) {
1✔
95
                Element lookback = elem.get(i - 1);
1✔
96
                if (elem.get(i) instanceof ImExitwhen && lookback instanceof ImExitwhen) {
1✔
97
                    optimizeConsecutiveExitWhen((ImExitwhen) lookback, (ImExitwhen) elem.get(i));
1✔
98
                }
99

100
                if (elem.get(i) instanceof ImSet && lookback instanceof ImSet) {
1✔
101
                    optimizeConsecutiveSet((ImSet) lookback, (ImSet) elem.get(i));
1✔
102
                }
103
            }
104
        }
105
        if (elem instanceof ImOperatorCall) {
1✔
106
            ImOperatorCall opc = (ImOperatorCall) elem;
1✔
107
            optimizeOpCall(opc);
1✔
108
        } else if (elem instanceof ImIf) {
1✔
109
            ImIf imIf = (ImIf) elem;
1✔
110
            optimizeIf(imIf);
1✔
111
        } else if (elem instanceof ImExitwhen) {
1✔
112
            ImExitwhen imExitwhen = (ImExitwhen) elem;
1✔
113
            optimizeExitwhen(imExitwhen);
1✔
114
        }
115

116
    }
1✔
117

118
    private void optimizeConsecutiveExitWhen(ImExitwhen lookback, ImExitwhen element) {
119
        element.getCondition().setParent(null);
1✔
120
        lookback.setCondition(JassIm.ImOperatorCall(WurstOperator.OR, JassIm.ImExprs(lookback.getCondition().copy(), element.getCondition())));
1✔
121
        element.replaceBy(ImHelper.nullExpr());
1✔
122
        totalRewrites++;
1✔
123
    }
1✔
124

125
    private void optimizeExitwhen(ImExitwhen imExitwhen) {
126
        ImExpr expr = imExitwhen.getCondition();
1✔
127
        if (expr instanceof ImBoolVal) {
1✔
128
            boolean b = ((ImBoolVal) expr).getValB();
1✔
129
            if (!b) {
1✔
130
                imExitwhen.replaceBy(ImHelper.nullExpr());
1✔
131
                totalRewrites++;
1✔
132
            }
133
        }
134

135
    }
1✔
136

137
    /**
138
     * Rewrites if statements that only contain an exitwhen statement
139
     * so that the if's condition is combined with the exitwhen
140
     * <p>
141
     * if expr1
142
     * exitwhen expr2
143
     * <p>
144
     * to:
145
     * <p>
146
     * exitwhen expr1 and expr2
147
     */
148
    private void optimizeIfExitwhen(ImIf imIf) {
149
        ImExitwhen imStmt = (ImExitwhen) imIf.getThenBlock().get(0);
1✔
150
        imStmt.getCondition().setParent(null);
1✔
151
        imIf.getCondition().setParent(null);
1✔
152

153
        imStmt.setCondition((JassIm.ImOperatorCall(WurstOperator.AND, JassIm.ImExprs(imIf.getCondition(), imStmt.getCondition()))));
1✔
154
        imStmt.setParent(null);
1✔
155
        imIf.replaceBy(imStmt);
1✔
156
        totalRewrites++;
1✔
157
    }
1✔
158

159
    private void optimizeOpCall(ImOperatorCall opc) {
160
        // Binary
161
        boolean wasViable = true;
1✔
162
        if (opc.getArguments().size() > 1) {
1✔
163
            ImExpr left = opc.getArguments().get(0);
1✔
164
            ImExpr right = opc.getArguments().get(1);
1✔
165
            if (left instanceof ImBoolVal && right instanceof ImBoolVal) {
1✔
166
                boolean b1 = ((ImBoolVal) left).getValB();
1✔
167
                boolean b2 = ((ImBoolVal) right).getValB();
1✔
168
                boolean result;
169
                switch (opc.getOp()) {
1✔
170
                    case OR:
171
                        result = b1 || b2;
1✔
172
                        break;
1✔
173
                    case AND:
174
                        result = b1 && b2;
1✔
175
                        break;
1✔
176
                    case EQ:
177
                        result = b1 == b2;
×
178
                        break;
×
179
                    case NOTEQ:
180
                        result = b1 != b2;
×
181
                        break;
×
182
                    default:
183
                        result = false;
×
184
                        break;
185
                }
186
                opc.replaceBy(JassIm.ImBoolVal(result));
1✔
187
            } else if (left instanceof ImBoolVal) {
1✔
188
                boolean b1 = ((ImBoolVal) left).getValB();
1✔
189
                wasViable = replaceBoolTerm(opc, right, b1);
1✔
190
            } else if (right instanceof ImBoolVal) {
1✔
191
                boolean b2 = ((ImBoolVal) right).getValB();
1✔
192
                wasViable = replaceBoolTerm(opc, left, b2);
1✔
193
            } else if (isNumberLiteral(left) && isNumberLiteral(right)) {
1✔
194
                // If any side is real (or the op is a real op), fold as real; otherwise fold as int.
195
                boolean foldAsReal =
1✔
196
                    (left instanceof ImRealVal) ||
197
                        (right instanceof ImRealVal) ||
198
                        opc.getOp() == WurstOperator.DIV_REAL ||
1✔
199
                        opc.getOp() == WurstOperator.MOD_REAL;
1✔
200

201
                if (foldAsReal) {
1✔
202
                    wasViable = optimizeRealRealMixed(opc, wasViable, left, right);
1✔
203
                } else if (left instanceof ImIntVal && right instanceof ImIntVal) {
1✔
204
                    wasViable = optimizeIntInt(opc, wasViable, (ImIntVal) left, (ImIntVal) right);
1✔
205
                } else {
206
                    wasViable = false; // unknown numeric combo
×
207
                }
208
            } else if (left instanceof ImStringVal) {
1✔
209
                // Fold "" + expr  =>  expr
210
                if (opc.getOp() == WurstOperator.PLUS
1✔
211
                    && ((ImStringVal) left).getValS().isEmpty()) {
1✔
212
                    right.setParent(null);
1✔
213
                    opc.replaceBy(right);
1✔
214
                    wasViable = true;
1✔
215
                } else {
216
                    wasViable = false;
1✔
217
                }
218
            } else if (right instanceof ImStringVal) {
1✔
219
                if (left instanceof ImStringVal) {
1✔
220
                    wasViable = optimizeStringString(opc, (ImStringVal) left, (ImStringVal) right);
×
221
                } else if (((ImStringVal) right).getValS().equalsIgnoreCase("") && opc.getOp() == WurstOperator.PLUS) {
1✔
222
                    left.setParent(null);
1✔
223
                    opc.replaceBy(left);
1✔
224
                    wasViable = true;
1✔
225
                } else {
226
                    wasViable = false;
1✔
227
                }
228
            } else {
229
                wasViable = false;
1✔
230
            }
231
        }
1✔
232

233
        // Unary
234
        else {
235
            ImExpr expr = opc.getArguments().get(0);
1✔
236
            if (opc.getOp() == WurstOperator.UNARY_MINUS && expr instanceof ImIntVal) {
1✔
237
                int v = ((ImIntVal) expr).getValI();
1✔
238
                if (v != Integer.MIN_VALUE && v <= 0) {
1✔
239
                    opc.replaceBy(JassIm.ImIntVal(-v));
×
240
                } else {
241
                    wasViable = false;
1✔
242
                }
243
            } else if (expr instanceof ImBoolVal) {
1✔
244
                boolean b1 = ((ImBoolVal) expr).getValB();
1✔
245
                boolean result;
246
                switch (opc.getOp()) {
1✔
247
                    case NOT:
248
                        result = !b1;
1✔
249
                        break;
1✔
250
                    default:
251
                        result = false;
×
252
                        break;
253
                }
254
                opc.replaceBy(JassIm.ImBoolVal(result));
1✔
255
            } else if (opc.getOp() == WurstOperator.NOT && expr instanceof ImOperatorCall) {
1✔
256
                // optimize negation of some operators
257
                ImOperatorCall inner = (ImOperatorCall) expr;
1✔
258
                switch (inner.getOp()) {
1✔
259
                    case NOT:
260
                        opc.replaceBy(inner.getArguments().remove(0));
1✔
261
                        break;
1✔
262
                    case EQ:
263
                    case NOTEQ:
264
                    case LESS:
265
                    case LESS_EQ:
266
                    case GREATER:
267
                    case GREATER_EQ:
268
                        opc.replaceBy(JassIm.ImOperatorCall(oppositeOperator(inner.getOp()), JassIm.ImExprs(inner.getArguments().removeAll())));
1✔
269
                        break;
1✔
270
                    case OR:
271
                    case AND:
272
                        // DeMorgan not(a and b) => not a or not b; not(a or b) => not a and not b
273
                        List<ImExpr> args = inner.getArguments().removeAll();
1✔
274
                        ImExprs imExprs = JassIm.ImExprs();
1✔
275
                        args.forEach((e) ->
1✔
276
                            imExprs.add(JassIm.ImOperatorCall(WurstOperator.NOT, JassIm.ImExprs(e.copy()))));
1✔
277

278
                        ImOperatorCall opCall = JassIm.ImOperatorCall(oppositeOperator(inner.getOp()), imExprs);
1✔
279
                        opc.replaceBy(opCall);
1✔
280
                        break;
1✔
281
                    default:
282
                        wasViable = false;
×
283
                        break;
284
                }
285
            } else {
1✔
286
                wasViable = false;
1✔
287
            }
288
        }
289
        if (wasViable) {
1✔
290
            totalRewrites++;
1✔
291
            if (showRewrites) {
292
                WLogger.info("opcall rewrite: " + opc);
293
            }
294
        }
295

296
    }
1✔
297

298
    private boolean optimizeRealRealMixed(ImOperatorCall opc, boolean wasViable, ImExpr left, ImExpr right) {
299
        float f1 = asFloat(left);
1✔
300
        float f2 = asFloat(right);
1✔
301
        boolean isConditional = false;
1✔
302
        boolean isArithmetic = false;
1✔
303
        boolean result = false;
1✔
304
        float resultVal = 0f;
1✔
305

306
        switch (opc.getOp()) {
1✔
307
            case GREATER:
308
                result = f1 > f2;
1✔
309
                isConditional = true;
1✔
310
                break;
1✔
311
            case GREATER_EQ:
312
                result = f1 >= f2;
1✔
313
                isConditional = true;
1✔
314
                break;
1✔
315
            case LESS:
316
                result = f1 < f2;
1✔
317
                isConditional = true;
1✔
318
                break;
1✔
319
            case LESS_EQ:
320
                result = f1 <= f2;
1✔
321
                isConditional = true;
1✔
322
                break;
1✔
323
            case EQ:
324
                result = f1 == f2;
1✔
325
                isConditional = true;
1✔
326
                break;
1✔
327
            case NOTEQ:
328
                result = f1 != f2;
1✔
329
                isConditional = true;
1✔
330
                break;
1✔
331

332
            case PLUS:
333
                resultVal = f1 + f2;
1✔
334
                isArithmetic = true;
1✔
335
                break;
1✔
336
            case MINUS:
337
                resultVal = f1 - f2;
1✔
338
                isArithmetic = true;
1✔
339
                break;
1✔
340
            case MULT:
341
                resultVal = f1 * f2;
1✔
342
                isArithmetic = true;
1✔
343
                break;
1✔
344
            case MOD_REAL:
345
                if (f2 != 0f) {
1✔
346
                    resultVal = f1 % f2;
1✔
347
                    isArithmetic = true;
1✔
348
                }
349
                break;
350
            case DIV_INT:
351
            case DIV_REAL:
352
                if (f2 != 0f) {
1✔
353
                    resultVal = f1 / f2;
1✔
354
                    isArithmetic = true;
1✔
355
                }
356
                break;
357

358
            default:
359
                return false;
×
360
        }
361

362
        if (isConditional) {
1✔
363
            opc.replaceBy(JassIm.ImBoolVal(result));
1✔
364
            return true;
1✔
365
        } else if (isArithmetic) {
1✔
366
            String s = floatToStringWithDecimalDigits(resultVal, 4);
1✔
367
            if (Float.parseFloat(s) != resultVal) {
1✔
368
                s = floatToStringWithDecimalDigits(resultVal, 9);
1✔
369
                if (Float.parseFloat(s) != resultVal) {
1✔
370
                    return false;
×
371
                }
372
            }
373
            opc.replaceBy(JassIm.ImRealVal(s));
1✔
374
            return true;
1✔
375
        } else {
376
            return false;
1✔
377
        }
378
    }
379

380

381
    private boolean optimizeStringString(ImOperatorCall opc, ImStringVal left, ImStringVal right) {
382
        String f1 = left.getValS();
×
383
        String f2 = right.getValS();
×
384
        switch (opc.getOp()) {
×
385
            case PLUS:
386
                opc.replaceBy(JassIm.ImStringVal(f1 + f2));
×
387
                return true;
×
388
            default:
389
                break;
390
        }
391
        return false;
×
392
    }
393

394
    private boolean optimizeRealReal(ImOperatorCall opc, boolean wasViable, ImRealVal left, ImRealVal right) {
395
        float f1 = Float.parseFloat(left.getValR());
×
396
        float f2 = Float.parseFloat(right.getValR());
×
397
        boolean isConditional = false;
×
398
        boolean isArithmetic = false;
×
399
        boolean result = false;
×
400
        float resultVal = 0;
×
401
        switch (opc.getOp()) {
×
402
            case GREATER:
403
                result = f1 > f2;
×
404
                isConditional = true;
×
405
                break;
×
406
            case GREATER_EQ:
407
                result = f1 >= f2;
×
408
                isConditional = true;
×
409
                break;
×
410
            case LESS:
411
                result = f1 < f2;
×
412
                isConditional = true;
×
413
                break;
×
414
            case LESS_EQ:
415
                result = f1 <= f2;
×
416
                isConditional = true;
×
417
                break;
×
418
            case EQ:
419
                result = f1 == f2;
×
420
                isConditional = true;
×
421
                break;
×
422
            case NOTEQ:
423
                result = f1 != f2;
×
424
                isConditional = true;
×
425
                break;
×
426
            case PLUS:
427
                resultVal = f1 + f2;
×
428
                isArithmetic = true;
×
429
                break;
×
430
            case MINUS:
431
                resultVal = f1 - f2;
×
432
                isArithmetic = true;
×
433
                break;
×
434
            case MULT:
435
                resultVal = f1 * f2;
×
436
                isArithmetic = true;
×
437
                break;
×
438
            case MOD_REAL:
439
                if (f2 != 0) {
×
440
                    resultVal = f1 % f2;
×
441
                    isArithmetic = true;
×
442
                }
443
                break;
444
            case DIV_INT:
445
                if (f2 != 0) {
×
446
                    resultVal = f1 / f2;
×
447
                    isArithmetic = true;
×
448
                }
449
                break;
450
            case DIV_REAL:
451
                if (f2 != 0) {
×
452
                    resultVal = f1 / f2;
×
453
                    isArithmetic = true;
×
454
                }
455
                break;
456
            default:
457
                result = false;
×
458
                isConditional = false;
×
459
                isArithmetic = false;
×
460
                break;
461
        }
462
        if (isConditional) {
×
463
            opc.replaceBy(JassIm.ImBoolVal(result));
×
464
        } else if (isArithmetic) {
×
465
            // convert result to string, using 4 decimal digits
466
            String s = floatToStringWithDecimalDigits(resultVal, 4);
×
467
            // String s = new BigDecimal(resultVal).toPlainString();
468
            // check if the string representation is exact
469
            if (Float.parseFloat(s) == resultVal) {
×
470
                opc.replaceBy(JassIm.ImRealVal(s));
×
471
            } else {
472
                s = floatToStringWithDecimalDigits(resultVal, 9);
×
473
                if (Float.parseFloat(s) == resultVal) {
×
474
                    opc.replaceBy(JassIm.ImRealVal(s));
×
475
                } else {
476
                    wasViable = false;
×
477
                }
478
            }
479
        } else {
×
480
            wasViable = false;
×
481
        }
482
        return wasViable;
×
483
    }
484

485
    private boolean optimizeIntInt(ImOperatorCall opc, boolean wasViable, ImIntVal left, ImIntVal right) {
486
        int i1 = left.getValI();
1✔
487
        int i2 = right.getValI();
1✔
488
        boolean isConditional = false;
1✔
489
        boolean isArithmetic = false;
1✔
490
        boolean result = false;
1✔
491
        int resultVal = 0;
1✔
492
        switch (opc.getOp()) {
1✔
493
            case GREATER:
494
                result = i1 > i2;
1✔
495
                isConditional = true;
1✔
496
                break;
1✔
497
            case GREATER_EQ:
498
                result = i1 >= i2;
1✔
499
                isConditional = true;
1✔
500
                break;
1✔
501
            case LESS:
502
                result = i1 < i2;
1✔
503
                isConditional = true;
1✔
504
                break;
1✔
505
            case LESS_EQ:
506
                result = i1 <= i2;
1✔
507
                isConditional = true;
1✔
508
                break;
1✔
509
            case EQ:
510
                result = i1 == i2;
1✔
511
                isConditional = true;
1✔
512
                break;
1✔
513
            case NOTEQ:
514
                result = i1 != i2;
1✔
515
                isConditional = true;
1✔
516
                break;
1✔
517
            case PLUS:
518
                resultVal = i1 + i2;
1✔
519
                isArithmetic = true;
1✔
520
                break;
1✔
521
            case MINUS:
522
                resultVal = i1 - i2;
1✔
523
                isArithmetic = true;
1✔
524
                break;
1✔
525
            case MULT:
526
                resultVal = i1 * i2;
1✔
527
                isArithmetic = true;
1✔
528
                break;
1✔
529
            case MOD_INT:
530
                if (i2 != 0) {
1✔
531
                    resultVal = i1 % i2;
1✔
532
                    isArithmetic = true;
1✔
533
                }
534
                break;
535
            case MOD_REAL: {
536
                float f1 = i1;
×
537
                float f2 = i2;
×
538
                if (f2 != 0f) {
×
539
                    float resultF = f1 % f2;
×
540
                    String s = floatToStringWithDecimalDigits(resultF, 4);
×
541
                    if (Float.parseFloat(s) != resultF) {
×
542
                        s = floatToStringWithDecimalDigits(resultF, 9);
×
543
                        if (Float.parseFloat(s) != resultF) {
×
544
                            wasViable = false;
×
545
                            break;
×
546
                        }
547
                    }
548
                    opc.replaceBy(JassIm.ImRealVal(s));
×
549
                    // keep wasViable as-is (true) so the caller counts this rewrite
550
                } else {
×
551
                    wasViable = false; // don’t fold div-by-zero
×
552
                }
553
                break;
×
554
            }
555
            case DIV_REAL: {
556
                float f1 = i1;
×
557
                float f2 = i2;
×
558
                if (f2 != 0f) {
×
559
                    float resultF = f1 / f2;
×
560
                    String s = floatToStringWithDecimalDigits(resultF, 4);
×
561
                    if (Float.parseFloat(s) != resultF) {
×
562
                        s = floatToStringWithDecimalDigits(resultF, 9);
×
563
                        if (Float.parseFloat(s) != resultF) {
×
564
                            wasViable = false;
×
565
                            break;
×
566
                        }
567
                    }
568
                    opc.replaceBy(JassIm.ImRealVal(s));
×
569
                    // keep wasViable as-is (true) so the caller counts this rewrite
570
                } else {
×
571
                    wasViable = false; // don’t fold div-by-zero
×
572
                }
573
                break;
×
574
            }
575
            case DIV_INT:
576
                if (i2 != 0) {
1✔
577
                    resultVal = i1 / i2;
1✔
578
                    isArithmetic = true;
1✔
579
                }
580
                break;
581
            default:
582
                result = false;
×
583
                isConditional = false;
×
584
                isArithmetic = false;
×
585
                break;
586
        }
587
        if (isConditional) {
1✔
588
            opc.replaceBy(JassIm.ImBoolVal(result));
1✔
589
        } else if (isArithmetic) {
1✔
590
            opc.replaceBy(JassIm.ImIntVal(resultVal));
1✔
591
        } else {
592
            wasViable = false;
1✔
593
        }
594
        return wasViable;
1✔
595
    }
596

597
    private boolean replaceBoolTerm(ImOperatorCall opc, ImExpr expr, boolean b2) {
598
        switch (opc.getOp()) {
1✔
599
            case OR:
600
                if (b2) {
1✔
601
                    opc.replaceBy(JassIm.ImBoolVal(true));
1✔
602
                } else {
603
                    expr.setParent(null);
1✔
604
                    opc.replaceBy(expr);
1✔
605
                }
606
                break;
1✔
607
            case AND:
608
                if (b2) {
1✔
609
                    expr.setParent(null);
1✔
610
                    opc.replaceBy(expr);
1✔
611
                } else {
612
                    opc.replaceBy(JassIm.ImBoolVal(false));
1✔
613
                }
614
                break;
1✔
615
            default:
616
                return false;
1✔
617
        }
618
        return true;
1✔
619
    }
620

621
    /**
622
     * returns the opposite of an operator
623
     */
624
    private WurstOperator oppositeOperator(WurstOperator op) {
625
        switch (op) {
1✔
626
            case EQ:
627
                return WurstOperator.NOTEQ;
1✔
628
            case GREATER:
629
                return WurstOperator.LESS_EQ;
1✔
630
            case GREATER_EQ:
631
                return WurstOperator.LESS;
1✔
632
            case LESS:
633
                return WurstOperator.GREATER_EQ;
1✔
634
            case LESS_EQ:
635
                return WurstOperator.GREATER;
1✔
636
            case NOTEQ:
637
                return WurstOperator.EQ;
1✔
638
            case AND:
639
                return WurstOperator.OR;
1✔
640
            case OR:
641
                return WurstOperator.AND;
1✔
642
            default:
643
                throw new Error("operator " + op + " does not have an opposite.");
×
644
        }
645
    }
646

647
    private static String floatToStringWithDecimalDigits(float resultVal, int digits) {
648
        DecimalFormat format = new DecimalFormat();
1✔
649
        // use a fixed locale, so that it does not randomly replace a dot by
650
        // comma on German PCs
651
        // hope this works
652
        format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
1✔
653
        format.setMinimumIntegerDigits(1);
1✔
654
        format.setMaximumFractionDigits(digits);
1✔
655
        format.setMinimumFractionDigits(1);
1✔
656
        format.setGroupingUsed(false);
1✔
657
        String s = format.format(resultVal);
1✔
658
        return s;
1✔
659
    }
660

661
    private void optimizeIf(ImIf imIf) {
662
        if (imIf.getThenBlock().isEmpty() && imIf.getElseBlock().isEmpty()) {
1✔
663
            totalRewrites++;
1✔
664
            imIf.replaceBy(imIf.getCondition().copy());
1✔
665
        } else if (imIf.getCondition() instanceof ImBoolVal) {
1✔
666
            ImBoolVal boolVal = (ImBoolVal) imIf.getCondition();
1✔
667
            if (boolVal.getValB()) {
1✔
668
                // we have something like 'if true ...'
669
                // replace the if statement with the then-block
670
                // we have to use ImStatementExpr to get multiple statements
671
                // into one statement as needed
672
                // for the replaceBy function
673
                // we need to copy the thenBlock because otherwise it would have
674
                // two parents (we have not removed it from the old if-block)
675
                imIf.replaceBy(ImHelper.statementExprVoid(imIf.getThenBlock().copy()));
1✔
676
                totalRewrites++;
1✔
677
            } else {
678
                if (!imIf.getElseBlock().isEmpty()) {
1✔
679
                    imIf.replaceBy(ImHelper.statementExprVoid(imIf.getElseBlock().copy()));
1✔
680
                    totalRewrites++;
1✔
681
                } else {
682
                    imIf.replaceBy(ImHelper.nullExpr());
1✔
683
                    totalRewrites++;
1✔
684
                }
685
            }
686
        } else if (imIf.getElseBlock().isEmpty() && imIf.getThenBlock().size() == 1 && imIf.getThenBlock().get(0) instanceof ImExitwhen) {
1✔
687
            optimizeIfExitwhen(imIf);
1✔
688
        }
689
    }
1✔
690

691
    /**
692
     * Optimizes
693
     * <p>
694
     * set x = expr1
695
     * set x = x ⊕ expr2
696
     * <p>
697
     * into:
698
     * <p>
699
     * set x  = expr1 ⊕ expr2
700
     * <p>
701
     * like code that is created by the branch merger
702
     */
703
    private void optimizeConsecutiveSet(ImSet imSet1, ImSet imSet2) {
704
        ImVar leftVar1;
705
        if (imSet1.getLeft() instanceof ImVarAccess) {
1✔
706
            leftVar1 = ((ImVarAccess) imSet1.getLeft()).getVar();
1✔
707
        } else {
708
            return;
1✔
709
        }
710
        ImVar leftVar2;
711
        if (imSet2.getLeft() instanceof ImVarAccess) {
1✔
712
            leftVar2 = ((ImVarAccess) imSet2.getLeft()).getVar();
1✔
713
        } else {
714
            return;
1✔
715
        }
716

717
        ImExpr rightExpr1 = imSet1.getRight();
1✔
718
        ImExpr rightExpr2 = imSet2.getRight();
1✔
719

720
        if (leftVar1 == leftVar2) {
1✔
721
            if (rightExpr2 instanceof ImOperatorCall) {
1✔
722
                ImOperatorCall rightOpCall2 = (ImOperatorCall) rightExpr2;
1✔
723
                if (rightOpCall2.getArguments().size() == 2) {
1✔
724
                    if (rightOpCall2.getArguments().get(0) instanceof ImVarAccess) {
1✔
725
                        ImVarAccess imVarAccess2 = (ImVarAccess) rightOpCall2.getArguments().get(0);
1✔
726
                        if (imVarAccess2.getVar() == leftVar2) {
1✔
727
                            if (sideEffectAnalysis.cannotUseVar(rightOpCall2.getArguments().get(1), leftVar1)) {
1✔
728
                                rightExpr1.setParent(null);
1✔
729
                                imVarAccess2.replaceBy(rightExpr1);
1✔
730
                                imSet1.replaceBy(ImHelper.nullExpr());
1✔
731
                                totalRewrites++;
1✔
732
                            }
733
                        }
734
                    }
735
                }
736
            }
737
        }
738
    }
1✔
739

740
}
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