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

wurstscript / WurstScript / 203

18 Oct 2023 02:20PM UTC coverage: 63.758% (+0.3%) from 63.447%
203

push

circleci

web-flow
Update deps, improve performance, JHCR fixes (#1080)

- update dependencies
- update stdlib verison for unit tests
- only apply nullsetting when `-opt` is enabled to save some build time for testing
- minor performance improvements
- make build deterministic
- apply build map config before compilation
- hot code reload now more reliable

17246 of 27049 relevant lines covered (63.76%)

0.64 hits per line

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

97.69
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/CyclicFunctionRemover.java
1
package de.peeeq.wurstscript.translation.imtranslation;
2

3
import com.google.common.collect.ImmutableList;
4
import com.google.common.collect.Lists;
5
import com.google.common.collect.Maps;
6
import de.peeeq.datastructures.GraphInterpreter;
7
import de.peeeq.wurstio.TimeTaker;
8
import de.peeeq.wurstscript.WurstOperator;
9
import de.peeeq.wurstscript.jassIm.*;
10
import de.peeeq.wurstscript.types.WurstTypeInt;
11

12
import java.util.*;
13
import java.util.concurrent.atomic.AtomicReference;
14

15
/**
16
 * Removes cyclic functions from a program
17
 * by putting cyclic functions into one big function
18
 */
19
public class CyclicFunctionRemover {
20

21

22
    private ImProg prog;
23
    private TimeTaker timeTaker;
24
    private ImTranslator tr;
25
    private ImFuncGraph graph;
26

27
    public CyclicFunctionRemover(ImTranslator tr, ImProg prog, TimeTaker timeTaker) {
1✔
28
        this.tr = tr;
1✔
29
        this.prog = prog;
1✔
30
        this.timeTaker = timeTaker;
1✔
31
        this.graph = new ImFuncGraph();
1✔
32
    }
1✔
33

34
    public void work() {
35
        tr.calculateCallRelationsAndUsedVariables();
1✔
36
        AtomicReference<Set<Set<ImFunction>>> components = new AtomicReference<>();
1✔
37
        timeTaker.measure("finding cycles", () -> components.set(graph.findStronglyConnectedComponents(prog.getFunctions())));
1✔
38

39
        timeTaker.measure("removing cycles", () -> removeCycles(components));
1✔
40
    }
1✔
41

42
    private void removeCycles(AtomicReference<Set<Set<ImFunction>>> components) {
43
        for (Set<ImFunction> component : components.get()) {
1✔
44
            if (component.size() > 1) {
1✔
45
                removeCycle(ImmutableList.copyOf(component), component);
1✔
46
            }
47
        }
1✔
48
    }
1✔
49

50
    private void removeCycle(List<ImFunction> funcs, Set<ImFunction> funcSet) {
51
        List<ImVar> newParameters = Lists.newArrayList();
1✔
52
        Map<ImVar, ImVar> oldToNewVar = Maps.newLinkedHashMap();
1✔
53

54
        calculateNewParameters(funcs, newParameters, oldToNewVar);
1✔
55

56
        de.peeeq.wurstscript.ast.Element trace = funcs.get(0).getTrace();
1✔
57

58
        ImVar choiceVar = JassIm.ImVar(trace, WurstTypeInt.instance().imTranslateType(tr), "funcChoice", false);
1✔
59

60
        List<FunctionFlag> flags = Lists.newArrayList();
1✔
61

62
        ImFunction newFunc = JassIm.ImFunction(trace, makeName(funcs), JassIm.ImTypeVars(), JassIm.ImVars(), JassIm.ImVoid(), JassIm.ImVars(), JassIm.ImStmts(), flags);
1✔
63
        prog.getFunctions().add(newFunc);
1✔
64
        newFunc.getParameters().add(choiceVar);
1✔
65
        newFunc.getParameters().addAll(newParameters);
1✔
66

67
        ImStmts stmts = newFunc.getBody();
1✔
68

69
        for (int i = 0; i < funcs.size(); i++) {
1✔
70
            ImFunction f = funcs.get(i);
1✔
71
            ImStmts thenBlock = JassIm.ImStmts();
1✔
72

73
            // add body
74
            thenBlock.addAll(f.getBody().removeAll());
1✔
75
            // addLocals
76
            newFunc.getLocals().addAll(f.getLocals().removeAll());
1✔
77

78
            replaceVars(thenBlock, oldToNewVar);
1✔
79

80
            if (!(f.getReturnType() instanceof ImVoid)) {
1✔
81
                replaceReturn(thenBlock, f.getReturnType());
1✔
82
            }
83

84
            ImStmts elseBlock = JassIm.ImStmts();
1✔
85
            stmts.add(JassIm.ImIf(trace,
1✔
86
                    JassIm.ImOperatorCall(WurstOperator.EQ,
1✔
87
                            JassIm.ImExprs(JassIm.ImVarAccess(choiceVar), JassIm.ImIntVal(i))),
1✔
88
                    thenBlock,
89
                    elseBlock));
90
            stmts = elseBlock;
1✔
91
        }
92

93
        replaceCalls(funcs, funcSet, newFunc, oldToNewVar, prog);
1✔
94

95
        for (ImFunction e : Lists.newArrayList(tr.getCalledFunctions().keys())) {
1✔
96
            Collection<ImFunction> called = tr.getCalledFunctions().get(e);
1✔
97
            called.removeAll(funcs);
1✔
98
        }
1✔
99

100
        // remove the old funcs
101
        prog.getFunctions().removeAll(funcs);
1✔
102
//                System.out.println("----------------------------------");
103
//                System.out.println(prog.toString());
104
//                System.out.println("----------------------------------");
105
    }
1✔
106

107
    private void replaceVars(Element e, Map<ImVar, ImVar> oldToNewVar) {
108
        // process children
109
        for (int i = 0; i < e.size(); i++) {
1✔
110
            replaceVars(e.get(i), oldToNewVar);
1✔
111
        }
112

113
        if (e instanceof ImVarAccess) {
1✔
114
            ImVarAccess va = (ImVarAccess) e;
1✔
115
            ImVar newVar = oldToNewVar.get(va.getVar());
1✔
116
            if (newVar != null) {
1✔
117
                va.setVar(newVar);
1✔
118
            }
119
        }
120
    }
1✔
121

122

123
    private void replaceCalls(List<ImFunction> funcs, Set<ImFunction> funcSet, ImFunction newFunc, Map<ImVar, ImVar> oldToNewVar, Element e) {
124
        // process children
125
        for (int i = 0; i < e.size(); i++) {
1✔
126
            replaceCalls(funcs, funcSet, newFunc, oldToNewVar, e.get(i));
1✔
127
        }
128

129

130
        if (e instanceof ImFuncRef) {
1✔
131
            ImFuncRef fr = (ImFuncRef) e;
1✔
132
            ImFunction f = fr.getFunc();
1✔
133
            if (funcSet.contains(f)) {
1✔
134

135
                ImFunction proxyFunc = JassIm.ImFunction(f.attrTrace(), f.getName() + "_proxy", JassIm.ImTypeVars(), f.getParameters().copy(), (ImType) f.getReturnType().copy(), JassIm.ImVars(), JassIm.ImStmts(), Collections.<FunctionFlag>emptyList());
1✔
136
                prog.getFunctions().add(proxyFunc);
1✔
137

138
                ImExprs arguments = JassIm.ImExprs();
1✔
139
                for (ImVar p : proxyFunc.getParameters()) {
1✔
140
                    arguments.add(JassIm.ImVarAccess(p));
×
141
                }
×
142

143
                ImFunctionCall call = JassIm.ImFunctionCall(fr.attrTrace(), f, JassIm.ImTypeArguments(), arguments, true, CallType.NORMAL);
1✔
144

145
                if (f.getReturnType() instanceof ImVoid) {
1✔
146
                    proxyFunc.getBody().add(call);
×
147
                } else {
148
                    proxyFunc.getBody().add(JassIm.ImReturn(proxyFunc.getTrace(), call));
1✔
149
                }
150
                // rewrite the proxy call:
151
                replaceCalls(funcs, funcSet, newFunc, oldToNewVar, call);
1✔
152
                // change the funcref to use the proxy
153
                fr.setFunc(proxyFunc);
1✔
154
            }
155
        } else if (e instanceof ImFunctionCall) {
1✔
156
            ImFunctionCall fc = (ImFunctionCall) e;
1✔
157
            ImFunction oldFunc = fc.getFunc();
1✔
158
            if (funcSet.contains(oldFunc)) {
1✔
159

160
                ImExprs arguments = JassIm.ImExprs();
1✔
161

162
                // first argument is the choice index
163
                arguments.add(JassIm.ImIntVal(funcs.indexOf(oldFunc)));
1✔
164

165
                // now for the actual arguments
166
                List<ImExpr> oldArgs = fc.getArguments().removeAll();
1✔
167
                int pos = 0;
1✔
168
                for (int i = 1; i < newFunc.getParameters().size(); i++) {
1✔
169
                    ImVar p = newFunc.getParameters().get(i);
1✔
170
                    if (pos < oldArgs.size() && oldToNewVar.get(oldFunc.getParameters().get(pos)) == p) {
1✔
171
                        arguments.add(oldArgs.get(pos));
1✔
172
                        pos++;
1✔
173
                    } else {
174
                        // use default value
175
                        arguments.add(tr.getDefaultValueForJassType(p.getType()));
1✔
176
                    }
177
                }
178

179

180
                ImFunctionCall newCall = JassIm.ImFunctionCall(fc.getTrace(), newFunc, JassIm.ImTypeArguments(), arguments, true, CallType.NORMAL);
1✔
181

182
                Element ret;
183
                if (oldFunc.getReturnType() instanceof ImVoid) {
1✔
184
                    ret = newCall;
1✔
185
                } else {
186
                    // if there is a return value, use the temporary return value
187
                    ret = JassIm.ImStatementExpr(JassIm.ImStmts(newCall), JassIm.ImVarAccess(getTempReturnVar(oldFunc.getReturnType())));
1✔
188
                }
189
                fc.replaceBy(ret);
1✔
190

191
            }
192
        }
193

194

195
    }
1✔
196

197
    private void replaceReturn(Element e, ImType returnType) {
198
        // process children
199
        for (int i = 0; i < e.size(); i++) {
1✔
200
            replaceReturn(e.get(i), returnType);
1✔
201
        }
202

203

204
        if (e instanceof ImReturn) {
1✔
205
            ImReturn r = (ImReturn) e;
1✔
206

207
            ImExprOpt returnValue = r.getReturnValue();
1✔
208
            returnValue.setParent(null);
1✔
209
            ImStmts stmts = JassIm.ImStmts(
1✔
210
                    JassIm.ImSet(r.getTrace(), JassIm.ImVarAccess(getTempReturnVar(returnType)), (ImExpr) returnValue),
1✔
211
                    JassIm.ImReturn(r.getTrace(), JassIm.ImNoExpr())
1✔
212
            );
213
            r.replaceBy(ImHelper.statementExprVoid(stmts));
1✔
214
        }
215

216
    }
1✔
217

218
    private Map<String, ImVar> tempReturnVars = Maps.newLinkedHashMap();
1✔
219

220
    private ImVar getTempReturnVar(ImType t) {
221
        String typeName = t.translateType();
1✔
222
        ImVar r = tempReturnVars.get(typeName);
1✔
223
        if (r == null) {
1✔
224
            r = JassIm.ImVar(t.attrTrace(), t, "tempReturn_" + typeName, false);
1✔
225
            prog.getGlobals().add(r);
1✔
226
            tempReturnVars.put(typeName, r);
1✔
227
        }
228
        return r;
1✔
229
    }
230

231
    private String makeName(List<ImFunction> funcs) {
232
        return "cyc_" + funcs.get(0).getName();
1✔
233
    }
234

235
    private void calculateNewParameters(List<ImFunction> funcs,
236
                                        List<ImVar> newParameters, Map<ImVar, ImVar> oldToNewVar) {
237
        for (ImFunction f : funcs) {
1✔
238
            int pos = 0;
1✔
239
            withNextParameter:
240
            for (ImVar v : f.getParameters()) {
1✔
241
                // first check if we can reuse a parameter from the newParameters
242
                for (int i = pos; i < newParameters.size(); i++) {
1✔
243
                    if (newParameters.get(i).getType().translateType().equals(v.getType().translateType())) {
1✔
244
                        // found a var we can reuse
245
                        oldToNewVar.put(v, newParameters.get(i));
1✔
246
                        pos = i + 1;
1✔
247
                        continue withNextParameter;
1✔
248
                    }
249
                }
250
                // otherwise, we have to create a new var:
251
                ImVar newVar = JassIm.ImVar(v.getTrace(), (ImType) v.getType().copy(), v.getName(), false);
1✔
252
                oldToNewVar.put(v, newVar);
1✔
253
                newParameters.add(newVar);
1✔
254
                pos = newParameters.size() + 1;
1✔
255
            }
1✔
256
        }
1✔
257
    }
1✔
258

259
    class ImFuncGraph extends GraphInterpreter<ImFunction> {
1✔
260

261
        @Override
262
        protected Collection<ImFunction> getIncidentNodes(ImFunction f) {
263
            return tr.getCalledFunctions().get(f);
1✔
264
        }
265

266
    }
267

268

269
}
270

271

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

© 2026 Coveralls, Inc