• 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

84.87
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java
1
package de.peeeq.wurstscript.translation.imtojass;
2

3
import com.google.common.collect.*;
4
import de.peeeq.wurstscript.attributes.CompileError;
5
import de.peeeq.wurstscript.jassAst.*;
6
import de.peeeq.wurstscript.jassIm.*;
7
import de.peeeq.wurstscript.jassIm.Element;
8
import de.peeeq.wurstscript.parser.WPos;
9
import de.peeeq.wurstscript.translation.imoptimizer.RestrictedCompressedNames;
10
import de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum;
11
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
12
import de.peeeq.wurstscript.utils.Utils;
13
import org.eclipse.jdt.annotation.Nullable;
14

15
import java.util.*;
16
import java.util.regex.Pattern;
17
import java.util.stream.Collectors;
18

19
import static de.peeeq.wurstscript.jassAst.JassAst.*;
20

21
public class ImToJassTranslator {
22

23
    private final ImProg imProg;
24
    private final Multimap<ImFunction, ImFunction> calledFunctions;
25
    private final ImFunction mainFunc;
26
    private final ImFunction confFunction;
27
    private @Nullable JassProg prog;
28
    private final Stack<ImFunction> translatingFunctions = new Stack<>();
1✔
29
    private final Set<ImFunction> translatedFunctions = Sets.newLinkedHashSet();
1✔
30
    private final Set<String> usedNames = Sets.newLinkedHashSet();
1✔
31
    private final Multimap<ImFunction, String> usedLocalNames = HashMultimap.create();
1✔
32

33
    public ImToJassTranslator(ImProg imProg, Multimap<ImFunction, ImFunction> calledFunctions,
34
                              ImFunction mainFunc, ImFunction confFunction) {
1✔
35
        this.imProg = imProg;
1✔
36
        this.calledFunctions = calledFunctions;
1✔
37
        this.mainFunc = mainFunc;
1✔
38
        this.confFunction = confFunction;
1✔
39
    }
1✔
40

41
    public JassProg translate() {
42
        makeNamesUnique(imProg.getGlobals());
1✔
43
        makeNamesUnique(imProg.getFunctions());
1✔
44

45
        JassVars globals = JassVars();
1✔
46
        JassFunctions functions = JassFunctions();
1✔
47
        prog = JassProg(JassTypeDefs(), globals, JassNatives(), functions);
1✔
48

49
        collectGlobalVars();
1✔
50

51
        translateFunctionTransitive(mainFunc);
1✔
52
        translateFunctionTransitive(confFunction);
1✔
53

54
        return prog;
1✔
55
    }
56

57
    /**
58
     * makes names unique in a consistent way
59
     */
60
    private <T extends JassImElementWithName> void makeNamesUnique(List<T> list) {
61
        List<T> sorted = list.stream()
1✔
62
                .sorted(
1✔
63
                        Comparator.comparing(JassImElementWithName::getName)
1✔
64
                                .thenComparing(v -> v.getTrace().attrSource().getFile())
1✔
65
                                .thenComparing(v -> v.getTrace().attrSource().getLine())
1✔
66
                                .thenComparing(v -> v.getTrace().attrSource().getStartColumn()))
1✔
67
                .collect(Collectors.toList());
1✔
68

69
        for (int i = 0; i < sorted.size(); i++) {
1✔
70
            T vi = sorted.get(i);
1✔
71
            for (int j = i + 1; j < sorted.size(); j++) {
1✔
72
                T vj = sorted.get(j);
1✔
73
                if (vi.getName().equals(vj.getName())) {
1✔
74
                    vj.setName(vi.getName() + "_" + j);
1✔
75
                }
76
            }
77
        }
78
    }
1✔
79

80
    private void collectGlobalVars() {
81
        for (ImVar v : imProg.getGlobals()) {
1✔
82
            globalImVars.add(v);
1✔
83
            getJassVarFor(v);
1✔
84
        }
1✔
85
    }
1✔
86

87
    private void translateFunctionTransitive(ImFunction imFunc) {
88
        if (translatedFunctions.contains(imFunc)) {
1✔
89
            // already translated
90
            return;
1✔
91
        }
92
        if (translatingFunctions.contains(imFunc)) {
1✔
93
            // TODO extract method
94
            if (imFunc != translatingFunctions.peek()) {
×
95
                StringBuilder msg = new StringBuilder("cyclic dependency between functions: ");
×
96
                boolean start = false;
×
97
                for (ImFunction f : translatingFunctions) {
×
98
                    if (imFunc == f) {
×
99
                        start = true;
×
100
                    }
101
                    if (start) {
×
102
                        msg.append("\n - ").append(Utils.printElement(getTrace(f))).append("  ( ").append(f.attrTrace().attrSource().getFile()).append(" line" +
×
103
                                "  ").append(f.attrTrace().attrSource().getLine()).append(")");
×
104
                    }
105
                }
×
106
                WPos src = getTrace(imFunc).attrSource();
×
107
                throw new CompileError(src, msg.toString());
×
108
            }
109
            // already translating, recursive function
110
            return;
×
111
        }
112
        translatingFunctions.push(imFunc);
1✔
113
        for (ImFunction f : sorted(calledFunctions.get(imFunc))) {
1✔
114
            translateFunctionTransitive(f);
1✔
115
        }
1✔
116

117
        translateFunction(imFunc);
1✔
118

119
        // translation finished
120
        if (translatingFunctions.pop() != imFunc) {
1✔
121
            throw new Error("something went wrong...");
×
122
        }
123
        translatedFunctions.add(imFunc);
1✔
124
    }
1✔
125

126
    private List<ImFunction> sorted(Collection<ImFunction> collection) {
127
        List<ImFunction> r = Lists.newArrayList(collection);
1✔
128
        r.sort(Comparator.comparing(ImFunction::getName));
1✔
129
        return r;
1✔
130
    }
131

132
    private static de.peeeq.wurstscript.ast.Element getTrace(@Nullable Element elem) {
133
        while (elem != null) {
×
134
            if (elem instanceof ElementWithTrace) {
×
135
                ElementWithTrace ElementWithTrace = (ElementWithTrace) elem;
×
136
                de.peeeq.wurstscript.ast.Element t = ElementWithTrace.getTrace();
×
137
                if (t != null) {
×
138
                    return t;
×
139
                }
140
            }
141
            elem = elem.getParent();
×
142
        }
143
        throw new Error("Could not get trace to original program.");
×
144
    }
145

146
    private void translateFunction(ImFunction imFunc) {
147
        if (imFunc.isBj() || imFunc.hasFlag(FunctionFlagEnum.IS_VARARG)) {
1✔
148
            return;
1✔
149
        }
150
        // not a native
151
        JassFunctionOrNative f = getJassFuncFor(imFunc);
1✔
152

153
        f.setReturnType(imFunc.getReturnType().translateType());
1✔
154

155
        // translate parameters
156
        for (ImVar v : imFunc.getParameters()) {
1✔
157
            f.getParams().add((JassSimpleVar) getJassVarFor(v));
1✔
158
        }
1✔
159
        if (f instanceof JassFunction) {
1✔
160
            JassFunction jf = (JassFunction) f;
1✔
161
            // translate locals
162
            for (ImVar v : imFunc.getLocals()) {
1✔
163
                jf.getLocals().add(getJassVarFor(v));
1✔
164
            }
1✔
165
            imFunc.getBody().translate(jf.getBody(), jf, this);
1✔
166
        }
167

168
    }
1✔
169

170

171
    private String getUniqueGlobalName(String name) { // TODO find local names
172
        name = jassifyName(name);
1✔
173
        name = Utils.makeUniqueName(name, n -> !usedNames.contains(n));
1✔
174
        usedNames.add(name);
1✔
175
        return name;
1✔
176
    }
177

178
    private String getUniqueLocalName(ImFunction imFunction, String name) {
179
        name = jassifyName(name);
1✔
180
        name = Utils.makeUniqueName(name, n -> !usedNames.contains(n) && !usedLocalNames.containsEntry(imFunction, n));
1✔
181
        usedLocalNames.put(imFunction, name);
1✔
182
        return name;
1✔
183
    }
184

185

186
    private final Map<ImVar, JassVar> jassVars = Maps.newLinkedHashMap();
1✔
187
    private final Set<ImVar> globalImVars = Sets.newLinkedHashSet();
1✔
188

189
    JassVar getJassVarFor(ImVar v) {
190
        JassVar result = jassVars.get(v);
1✔
191
        if (result == null) {
1✔
192
            boolean isArray = v.getType() instanceof ImArrayType;
1✔
193
            String type = v.getType().translateType();
1✔
194
            String name = v.getName();
1✔
195
            if (v.getNearestFunc() != null) {
1✔
196
                name = getUniqueLocalName(v.getNearestFunc(), name);
1✔
197
            } else {
198
                name = getUniqueGlobalName(name);
1✔
199
            }
200
            if (isArray) {
1✔
201
                result = JassAst.JassArrayVar(type, name);
1✔
202
            } else {
203
                if (isGlobal(v) && v.getType() instanceof ImSimpleType) {
1✔
204
                    JassExpr initialVal = ImHelper.defaultValueForType((ImSimpleType) v.getType()).translate(this);
1✔
205
                    result = JassAst.JassInitializedVar(type, name, initialVal, v.getIsBJ());
1✔
206
                } else {
1✔
207
                    result = JassAst.JassSimpleVar(type, name);
1✔
208
                }
209
            }
210
            if (isGlobal(v) && (!v.getIsBJ() || result instanceof JassInitializedVar)) {
1✔
211
                prog.getGlobals().add(result);
1✔
212
            }
213
            jassVars.put(v, result);
1✔
214
        }
215
        return result;
1✔
216
    }
217

218
    private String jassifyName(String name) {
219
        name = filterInvalidSymbols(name);
1✔
220
        if (RestrictedCompressedNames.contains(name) || name.startsWith("_")) {
1✔
221
            name = "w" + name;
1✔
222
        }
223
        if (name.endsWith("_")) {
1✔
224
            name = name + "u";
1✔
225
        }
226
        if (name.isEmpty()) {
1✔
227
            name = "empty";
×
228
        }
229
        return name;
1✔
230
    }
231

232
    private final Pattern jassValidName = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*");
1✔
233

234
    /**
235
     * replaces all invalid characters with underscores
236
     */
237
    private String filterInvalidSymbols(String name) {
238
        if (jassValidName.matcher(name).matches()) {
1✔
239
            return name;
1✔
240
        }
241
        StringBuilder sb = new StringBuilder(name.length());
1✔
242
        for (int i = 0; i < name.length(); i++) {
1✔
243
            char c = name.charAt(i);
1✔
244
            if (!(c >= 'a' && c <= 'z'
1✔
245
                    || c >= 'A' && c <= 'Z'
246
                    || c >= '0' && c <= '9'
247
                    || c == '_')) {
248
                c = '_';
1✔
249
            }
250
            sb.append(c);
1✔
251
        }
252
        return sb.toString();
1✔
253
    }
254

255
    private boolean isGlobal(ImVar v) {
256
        return globalImVars.contains(v);
1✔
257
    }
258

259

260
    private final Map<ImFunction, JassFunctionOrNative> jassFuncs = Maps.newLinkedHashMap();
1✔
261

262
    public JassFunctionOrNative getJassFuncFor(ImFunction func) {
263
        JassFunctionOrNative f = jassFuncs.get(func);
1✔
264
        if (f == null) {
1✔
265
            if (func.isNative()) {
1✔
266
                f = JassAst.JassNative(func.getName(), JassSimpleVars(), "nothing");
1✔
267
                if (!func.isBj() && !func.isExtern()) {
1✔
268
                    prog.getNatives().add((JassNative) f);
1✔
269
                }
270
            } else {
271
                String name = func.getName();
1✔
272
                // find a unique name, but keep special names 'main' and 'config'
273
                if (!name.equals("main") && !name.equals("config") && !func.hasFlag(FunctionFlagEnum.IS_EXTERN)) {
1✔
274
                    name = getUniqueGlobalName(func.getName());
1✔
275
                }
276
                boolean isCompiletimeNative = func.hasFlag(FunctionFlagEnum.IS_COMPILETIME_NATIVE);
1✔
277
                f = JassFunction(name, JassSimpleVars(), "nothing", JassVars(), JassStatements(), isCompiletimeNative);
1✔
278
                if (!func.isBj() && !func.isExtern()) {
1✔
279
                    prog.getFunctions().add((JassFunction) f);
1✔
280
                }
281
            }
282
            jassFuncs.put(func, f);
1✔
283
        }
284
        return f;
1✔
285
    }
286

287

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