Skip to content

Commit 1881f29

Browse files
committed
some more good ones
1 parent cdced5d commit 1881f29

12 files changed

Lines changed: 601 additions & 188 deletions

File tree

de.peeeq.wurstscript/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ dependencies {
115115
implementation 'com.github.albfernandez:juniversalchardet:2.4.0'
116116

117117
// Crigges' jmpq
118-
implementation group: 'com.github.inwc3', name: 'jmpq3', version: 'dac0f9e21a'
118+
implementation group: 'com.github.inwc3', name: 'jmpq3', version: '3183dd7680'
119119

120120
// Water's wc3 libs
121-
implementation 'com.github.inwc3:wc3libs:412316b332'
121+
implementation 'com.github.inwc3:wc3libs:c3f131a0e5'
122122

123123
// The setup tool for wurst.build handling
124124
implementation 'com.github.wurstscript:wurstsetup:475cc7fae8'

de.peeeq.wurstscript/src/main/java/de/peeeq/datastructures/GraphInterpreter.java

Lines changed: 61 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public TopsortResult<T> topSort(List<T> nodes) {
2929
return new TopsortResult<>(false, result);
3030
}
3131

32-
3332
private @Nullable TopsortResult<T> topSort_add(List<T> result, Set<T> seen, List<T> seenStack, T n) {
3433
for (int i = seenStack.size() - 1; i >= 0; i--) {
3534
if (seenStack.get(i) == n) {
@@ -52,7 +51,6 @@ public TopsortResult<T> topSort(List<T> nodes) {
5251
return null;
5352
}
5453

55-
5654
public static class TopsortResult<T> {
5755
private final boolean isCycle;
5856
private final List<T> result;
@@ -69,80 +67,86 @@ public boolean isCycle() {
6967
public List<T> getResult() {
7068
return result;
7169
}
72-
73-
7470
}
7571

76-
7772
/**
78-
* Like topsort, but will find bigger cycles
73+
* Like topsort, but will find bigger cycles.
74+
* This is an iterative implementation of the path-based strong component algorithm
75+
* to prevent StackOverflowErrors on deep graphs.
7976
* <p>
8077
* See https://en.wikipedia.org/wiki/Path-based_strong_component_algorithm
8178
*/
8279
public Set<Set<T>> findStronglyConnectedComponents(List<T> nodes) {
83-
// Stack S contains all the vertices that have not yet been assigned to a strongly connected component, in the order in which the depth-first search reaches the vertices.
8480
Deque<T> s = new ArrayDeque<>();
85-
// Stack P contains vertices that have not yet been determined to belong to different strongly connected components from each other
8681
Deque<T> p = new ArrayDeque<>();
87-
// It also uses a counter C of the number of vertices reached so far, which it uses to compute the preorder numbers of the vertices.
8882
AtomicInteger c = new AtomicInteger();
8983
AtomicInteger componentCount = new AtomicInteger();
90-
Map<T, Integer> preorderNumber = new LinkedHashMap<>();
91-
Map<T, Integer> component = new LinkedHashMap<>();
92-
93-
for (T v : nodes) {
94-
if (!preorderNumber.containsKey(v)) {
95-
findStronglyConnectedComponentsRec(v, s, p, c, preorderNumber, component, componentCount);
96-
}
97-
}
98-
return ImmutableSet.copyOf(Utils.inverseMapToSet(component).values());
99-
}
100-
101-
102-
private void findStronglyConnectedComponentsRec(T v, Deque<T> s, Deque<T> p, AtomicInteger c, Map<T, Integer> preorderNumber, Map<T, Integer> component, AtomicInteger componentCount) {
103-
84+
Map<T, Integer> preorderNumber = new HashMap<>();
85+
Map<T, Integer> component = new HashMap<>();
86+
87+
// This stack simulates the recursive calls
88+
Deque<T> traversalStack = new ArrayDeque<>();
89+
// This map holds iterators for the children of each node
90+
Map<T, Iterator<T>> iterators = new HashMap<>();
91+
92+
for (T startNode : nodes) {
93+
if (!preorderNumber.containsKey(startNode)) {
94+
traversalStack.push(startNode);
95+
96+
while (!traversalStack.isEmpty()) {
97+
T v = traversalStack.peek();
98+
99+
// Pre-order processing (first time visiting node v)
100+
if (!preorderNumber.containsKey(v)) {
101+
preorderNumber.put(v, c.getAndIncrement());
102+
s.push(v);
103+
p.push(v);
104+
iterators.put(v, getIncidentNodes(v).iterator());
105+
}
104106

105-
// When the depth-first search reaches a vertex v, the algorithm performs the following steps:
106-
// 1. Set the preorder number of v to C, and increment C.
107-
preorderNumber.put(v, c.getAndIncrement());
107+
boolean foundNewChild = false;
108+
Iterator<T> children = iterators.get(v);
109+
110+
// Iterate over children to find the next one to visit
111+
while (children.hasNext()) {
112+
T w = children.next();
113+
if (!preorderNumber.containsKey(w)) {
114+
// Found an unvisited child, push to stack to simulate recursive call
115+
traversalStack.push(w);
116+
foundNewChild = true;
117+
break;
118+
} else if (!component.containsKey(w)) {
119+
// Child w has been visited but is not yet in a component
120+
while (!p.isEmpty() && preorderNumber.getOrDefault(p.peek(), -1) > preorderNumber.get(w)) {
121+
p.pop();
122+
}
123+
}
124+
}
108125

109-
// 2. Push v onto S and also onto P.
110-
s.push(v);
111-
p.push(v);
126+
if (foundNewChild) {
127+
// Continue the loop to process the new child on top of the stack
128+
continue;
129+
}
112130

113-
// 3. For each edge from v to a neighboring vertex w:
114-
for (T w : getIncidentNodes(v)) {
115-
if (!preorderNumber.containsKey(w)) {
116-
// If the preorder number of w has not yet been assigned, recursively search w;
117-
findStronglyConnectedComponentsRec(w, s, p, c, preorderNumber, component, componentCount);
118-
} else {
119-
// Otherwise, if w has not yet been assigned to a strongly connected component:
120-
if (!component.containsKey(w)) {
121-
// Repeatedly pop vertices from P until the top element of P has a preorder number less than or equal to the preorder number of w.
122-
while (!p.isEmpty()
123-
&& preorderNumber.getOrDefault(p.peek(), -1) > preorderNumber.get(w)) {
131+
// Post-order processing (all children of v have been visited)
132+
traversalStack.pop(); // Finished with v, pop it
133+
iterators.remove(v); // Clean up iterator
134+
135+
if (!p.isEmpty() && p.peek() == v) {
136+
Integer newComponent = componentCount.incrementAndGet();
137+
while (true) {
138+
T popped = s.pop();
139+
component.put(popped, newComponent);
140+
if (popped == v) {
141+
break;
142+
}
143+
}
124144
p.pop();
125145
}
126146
}
127147
}
128148
}
129-
// 4. If v is the top element of P:
130-
if (!p.isEmpty() && p.peek() == v) {
131-
// Pop vertices from S until v has been popped, and assign the popped vertices to a new component.
132-
Integer newComponent = componentCount.incrementAndGet();
133-
while (true) {
134-
T popped = s.pop();
135-
component.put(popped, newComponent);
136-
if (popped == v) {
137-
break;
138-
}
139-
}
140-
// Pop v from P.
141-
T popped = p.pop();
142-
assert popped == v;
143-
}
144-
145-
149+
return ImmutableSet.copyOf(Utils.inverseMapToSet(component).values());
146150
}
147151

148152
public String generateDotFile(List<T> nodes) {
@@ -166,5 +170,4 @@ public String generateDotFile(List<T> nodes) {
166170
sb.append("}\n");
167171
return sb.toString();
168172
}
169-
170173
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/WurstCompilerJassImpl.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,8 +472,13 @@ public JassProg transformProgToJass() {
472472

473473
// eliminate tuples
474474
beginPhase(6, "eliminate tuples");
475-
timeTaker.measure("flatten", () -> getImProg().flatten(imTranslator2));
476-
timeTaker.measure("kill tuples", () -> EliminateTuples.eliminateTuplesProg(getImProg(), imTranslator2));
475+
timeTaker.beginPhase("flatten");
476+
getImProg().flatten(imTranslator2);
477+
timeTaker.endPhase();
478+
timeTaker.beginPhase("kill tuples");
479+
EliminateTuples.eliminateTuplesProg(getImProg(), imTranslator2);
480+
timeTaker.endPhase();
481+
477482
getImTranslator().assertProperties(AssertProperty.NOTUPLES);
478483

479484
printDebugImProg("./test-output/im " + stage++ + "_withouttuples.im");

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ private W3InstallationData getBestW3InstallationData() throws RequestFailedExcep
493493

494494
protected void injectMapData(WurstGui gui, Optional<File> testMap, CompilationResult result) throws Exception {
495495
gui.sendProgress("Injecting map data");
496+
timeTaker.beginPhase("Injecting map data");
496497
try (MpqEditor mpqEditor = MpqEditorFactory.getEditor(testMap)) {
497498
String mapScriptName;
498499
if (runArgs.isLua()) {
@@ -510,6 +511,7 @@ protected void injectMapData(WurstGui gui, Optional<File> testMap, CompilationRe
510511
}
511512
mpqEditor.insertFile(mapScriptName, result.script);
512513
}
514+
timeTaker.endPhase();
513515
}
514516

515517
private void injectExternalLuaFiles(File script) {

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,11 @@ private String compileMap(ModelManager modelManager, WurstGui gui, WurstProjectC
125125
private void startGame(WurstGui gui, Optional<File> testMap, CompilationResult result) throws Exception {
126126
injectMapData(gui, testMap, result);
127127

128+
timeTaker.beginPhase("Starting Warcraft 3");
129+
gui.sendProgress("Starting Warcraft 3...");
130+
128131
File mapCopy = copyToWarcraftMapDir(testMap.get());
129132

130-
gui.sendProgress("Starting Warcraft 3...");
131133
WLogger.info("Starting wc3 ... ");
132134
String path = "";
133135
if (customTarget != null) {
@@ -165,6 +167,7 @@ private void startGame(WurstGui gui, Optional<File> testMap, CompilationResult r
165167
// run with wine
166168
cmd.add(0, "wine");
167169
}
170+
timeTaker.endPhase();
168171

169172
gui.sendProgress("running " + cmd);
170173
Runtime.getRuntime().exec(cmd.toArray(new String[0]));

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,14 @@ public static List<WurstType> argumentTypesPre(StmtCall node) {
171171
if (arg instanceof ExprClosure) {
172172
// for closures, we only calculate the type, if all argument types are specified:
173173
ExprClosure closure = (ExprClosure) arg;
174-
if (closure.getShortParameters().stream().allMatch(p -> p.getTypOpt() instanceof TypeExpr)) {
174+
boolean b = true;
175+
for (WShortParameter wShortParameter : closure.getShortParameters()) {
176+
if (!(wShortParameter.getTypOpt() instanceof TypeExpr)) {
177+
b = false;
178+
break;
179+
}
180+
}
181+
if (b) {
175182
argType = arg.attrTyp();
176183
} else {
177184
List<WurstType> paramTypes = new ArrayList<>();

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -46,40 +46,80 @@ public static ImmutableCollection<FunctionSignature> calculate(FunctionCall fc)
4646
return findBestSignature(fc, resultBuilder.build());
4747
}
4848

49-
private static ImmutableCollection<FunctionSignature> findBestSignature(StmtCall fc, ImmutableCollection<FunctionSignature> res) {
50-
ImmutableCollection.Builder<FunctionSignature> resultBuilder2 = ImmutableList.builder();
51-
List<WurstType> argTypes = AttrFuncDef.argumentTypesPre(fc);
52-
for (FunctionSignature sig : res) {
53-
FunctionSignature sig2 = sig.matchAgainstArgs(argTypes, fc);
54-
if (sig2 != null) {
55-
resultBuilder2.add(sig2);
49+
private static ImmutableCollection<FunctionSignature> findBestSignature(StmtCall fc,
50+
ImmutableCollection<FunctionSignature> res) {
51+
// Fast path: nothing to consider
52+
if (res.isEmpty()) {
53+
return ImmutableList.of();
54+
}
55+
56+
// Materialize once to a random-access list (cheap for Immutable*)
57+
final ImmutableList<FunctionSignature> sigs =
58+
(res instanceof ImmutableList)
59+
? (ImmutableList<FunctionSignature>) res
60+
: ImmutableList.copyOf(res);
61+
62+
// Compute arg types once
63+
final List<WurstType> argTypes = AttrFuncDef.argumentTypesPre(fc);
64+
65+
// --- Pass 1: exact matches only (cheap) ---------------------------------
66+
// Use a single ArrayList and only copy if we actually have matches.
67+
List<FunctionSignature> exact = new java.util.ArrayList<>(sigs.size());
68+
for (int i = 0, n = sigs.size(); i < n; i++) {
69+
FunctionSignature matched = sigs.get(i).matchAgainstArgs(argTypes, fc);
70+
if (matched != null) {
71+
exact.add(matched);
5672
}
5773
}
58-
ImmutableCollection<FunctionSignature> res2 = resultBuilder2.build();
59-
if (res2.isEmpty()) {
60-
// no signature matches precisely --> try to match as good as possible
61-
ImmutableList<ArgsMatchResult> match3 = res.stream()
62-
.map(sig -> sig.tryMatchAgainstArgs(argTypes, fc.getArgs(), fc))
63-
.collect(ImmutableList.toImmutableList());
64-
65-
if (match3.isEmpty()) {
66-
return ImmutableList.of();
67-
} else {
68-
// add errors from best match (minimal badness)
69-
ArgsMatchResult min = Collections.min(match3, Comparator.comparing(ArgsMatchResult::getBadness));
70-
for (CompileError c : min.getErrors()) {
71-
fc.getErrorHandler().sendError(c);
72-
}
74+
if (!exact.isEmpty()) {
75+
return ImmutableList.copyOf(exact);
76+
}
7377

74-
return match3.stream()
75-
.map(ArgsMatchResult::getSig)
76-
.collect(ImmutableList.toImmutableList());
78+
// --- Pass 2: best-effort matches (no exact match) ------------------------
79+
// We must:
80+
// * find the min-badness result (to emit its errors)
81+
// * return ALL resulting signatures (to preserve current semantics)
82+
final int n = sigs.size();
83+
FunctionSignature[] inferredSigs = new FunctionSignature[n];
84+
int bestIdx = -1;
85+
int bestBadness = Integer.MAX_VALUE;
86+
ArgsMatchResult bestResult = null;
87+
88+
// Cache args node once
89+
final Arguments argsNode = fc.getArgs();
90+
91+
for (int i = 0; i < n; i++) {
92+
// tryMatchAgainstArgs may also perform type-arg inference; we must keep its result sig
93+
ArgsMatchResult r = sigs.get(i).tryMatchAgainstArgs(argTypes, argsNode, fc);
94+
inferredSigs[i] = r.getSig();
95+
int b = r.getBadness();
96+
if (b < bestBadness) {
97+
bestBadness = b;
98+
bestIdx = i;
99+
bestResult = r;
77100
}
78-
} else {
79-
return res2;
80101
}
102+
103+
if (bestIdx == -1 || bestResult == null) {
104+
// Shouldn’t happen, but be safe
105+
return ImmutableList.of();
106+
}
107+
108+
// Emit errors from the best match (same as before)
109+
for (CompileError c : bestResult.getErrors()) {
110+
fc.getErrorHandler().sendError(c);
111+
}
112+
113+
// Return ALL candidate signatures (same as previous behavior)
114+
// Avoid another stream/collect
115+
ImmutableList.Builder<FunctionSignature> out = ImmutableList.builderWithExpectedSize(n);
116+
for (int i = 0; i < n; i++) {
117+
out.add(inferredSigs[i]);
118+
}
119+
return out.build();
81120
}
82121

122+
83123
public static ImmutableCollection<FunctionSignature> calculate(ExprNewObject fc) {
84124
TypeDef typeDef = fc.attrTypeDef();
85125
if (!(typeDef instanceof ClassDef)) {

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.eclipse.jdt.annotation.Nullable;
1414

1515
import java.util.ArrayList;
16+
import java.util.Collection;
1617
import java.util.List;
1718
import java.util.Optional;
1819

@@ -47,13 +48,31 @@ public static ImmutableCollection<FuncLink> lookupFuncsNoConfig(Element node, St
4748
}
4849

4950
public static ImmutableCollection<FuncLink> lookupFuncs(Element e, String name, boolean showErrors) {
50-
ArrayList<FuncLink> result = Lists.newArrayList(e.lookupFuncsNoConfig(name, showErrors));
51-
for (int i = 0; i < result.size(); i++) {
52-
result.set(i, result.get(i).withConfigDef());
51+
// Pull once
52+
final ImmutableCollection<FuncLink> raw = e.lookupFuncsNoConfig(name, showErrors);
53+
54+
// If we know the size, we can fast-path 0/1 and pre-size the builder.
55+
if (raw != null) {
56+
final java.util.Collection<FuncLink> c = raw;
57+
final int n = c.size();
58+
if (n == 0) return ImmutableList.of();
59+
if (n == 1) {
60+
// avoid builder/array allocs
61+
final FuncLink only = c.iterator().next();
62+
return ImmutableList.of(only.withConfigDef());
63+
}
64+
final ImmutableList.Builder<FuncLink> b = ImmutableList.builderWithExpectedSize(n);
65+
for (FuncLink f : c) b.add(f.withConfigDef());
66+
return b.build();
5367
}
54-
return ImmutableList.copyOf(result);
68+
69+
// Fallback if not a Collection (unknown size)
70+
ImmutableList.Builder<FuncLink> b = ImmutableList.builder();
71+
for (FuncLink f : raw) b.add(f.withConfigDef());
72+
return b.build();
5573
}
5674

75+
5776
private static <T extends NameLink> ImmutableCollection<T> removeDuplicates(List<T> nameLinks) {
5877
List<T> result = Lists.newArrayList();
5978
nextLink:

0 commit comments

Comments
 (0)