Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions de.peeeq.wurstscript/settings.gradle
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
pluginManagement {
repositories {
maven { url = uri("https://cache-redirector.jetbrains.com/plugins.gradle.org/m2") }
gradlePluginPortal()
maven { url = uri("https://plugins.gradle.org/m2") }
maven { url = uri("https://cache-redirector.jetbrains.com/maven-central") }
mavenCentral()
}
}

dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
maven { url = uri("https://cache-redirector.jetbrains.com/maven-central") }
mavenCentral()
google()
maven { url 'https://jitpack.io' }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,49 @@ private void eliminateGenericUses() {
}
}

private void fixCalleesInSpecializedFunction(ImFunction newF, GenericTypes generics) {
newF.accept(new Element.DefaultVisitor() {

@Override
public void visit(ImFunctionCall fc) {
super.visit(fc);

ImFunction callee = fc.getFunc();
if (callee == null) return;

// Only interesting if callee itself is generic
if (callee.getTypeVariables().isEmpty()) {
return;
}

// Determine which generics to use for the callee
GenericTypes calleeGenerics;

if (!fc.getTypeArguments().isEmpty()) {
// Call carries explicit type args → honor them
calleeGenerics = new GenericTypes(specializeTypeArgs(fc.getTypeArguments()));
} else {
// No explicit type args → use the same generics context as the enclosing function.
// This matches the pattern: destroyArrayList<T>(this: ArrayList<T>) calls ArrayList_onDestroy<T>(this)
calleeGenerics = generics;
}

if (calleeGenerics.containsTypeVariable()) {
// Still not concrete → let the normal pipeline handle it later or fail explicitly if needed
return;
}

ImFunction specializedCallee = specializedFunctions.get(callee, calleeGenerics);
if (specializedCallee == null) {
specializedCallee = specializeFunction(callee, calleeGenerics);
}

fc.setFunc(specializedCallee);
fc.getTypeArguments().removeAll();
}
});
}

/**
* creates a specialized version of this function
*/
Expand All @@ -246,7 +289,13 @@ private ImFunction specializeFunction(ImFunction f, GenericTypes generics) {

newF.setName(f.getName() + "⟪" + generics.makeName() + "⟫");
rewriteGenerics(newF, generics, typeVars);

// fix calls inside this specialized function so they also point to specialized callees
fixCalleesInSpecializedFunction(newF, generics);

// Then collect further generic uses inside the now-specialized body
collectGenericUsages(newF);

return newF;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1614,5 +1614,234 @@ public void inheritedField_lazyClosure_uses_enclosing_receiver() {
);
}

@Test
public void arrayListInClosure() {
testAssertOkLines(true,
"package Hello",
"import NoWurst",
"",
"native testSuccess()",
"",
"public class ArrayList<T:>",
" private static T array store",
" private static int nextFreeIndex = 0",
"",
" private static constant int MAX_FREE_SECTIONS = 256",
" private static int array freeSectionStart",
" private static int array freeSectionCapacity",
" private static int freeSectionCount = 0",
"",
" private int startIndex",
" private int capacity",
" private int size = 0",
" private static constant int INITIAL_CAPACITY = 16",
"",
" static function getNextFreeIndex() returns int",
" return nextFreeIndex",
"",
" construct()",
" allocateStorage(INITIAL_CAPACITY)",
"",
" construct(int initialCapacity)",
" allocateStorage(initialCapacity)",
"",
" private function allocateStorage(int cap)",
" for i = 0 to freeSectionCount - 1",
" if freeSectionCapacity[i] >= cap",
" startIndex = freeSectionStart[i]",
" capacity = freeSectionCapacity[i]",
"",
" for j = i to freeSectionCount - 2",
" freeSectionStart[j] = freeSectionStart[j + 1]",
" freeSectionCapacity[j] = freeSectionCapacity[j + 1]",
" freeSectionCount--",
" return",
"",
" if nextFreeIndex + cap > 10000",
" compactFreeList()",
"",
" if nextFreeIndex + cap > 10000",
" nextFreeIndex = 0",
"",
" startIndex = nextFreeIndex",
" capacity = cap",
" nextFreeIndex += cap",
"",
" private static function compactFreeList()",
" if freeSectionCount <= 1",
" return",
"",
" for i = 1 to freeSectionCount - 1",
" let keyStart = freeSectionStart[i]",
" let keyCap = freeSectionCapacity[i]",
" var j = i - 1",
"",
" while j >= 0 and freeSectionStart[j] > keyStart",
" freeSectionStart[j + 1] = freeSectionStart[j]",
" freeSectionCapacity[j + 1] = freeSectionCapacity[j]",
" j--",
"",
" freeSectionStart[j + 1] = keyStart",
" freeSectionCapacity[j + 1] = keyCap",
"",
" var writeIdx = 0",
" for readIdx = 0 to freeSectionCount - 1",
" if writeIdx > 0 and freeSectionStart[writeIdx - 1] + freeSectionCapacity[writeIdx - 1] == freeSectionStart[readIdx]",
" freeSectionCapacity[writeIdx - 1] += freeSectionCapacity[readIdx]",
" else",
" if writeIdx != readIdx",
" freeSectionStart[writeIdx] = freeSectionStart[readIdx]",
" freeSectionCapacity[writeIdx] = freeSectionCapacity[readIdx]",
" writeIdx++",
"",
" freeSectionCount = writeIdx",
"",
" if freeSectionCount > 0",
" let lastIdx = freeSectionCount - 1",
" if freeSectionStart[lastIdx] + freeSectionCapacity[lastIdx] == nextFreeIndex",
" nextFreeIndex = freeSectionStart[lastIdx]",
" freeSectionCount--",
"",
" private function freeStorage()",
" if capacity <= 0",
" return",
"",
" if freeSectionCount < MAX_FREE_SECTIONS",
" freeSectionStart[freeSectionCount] = startIndex",
" freeSectionCapacity[freeSectionCount] = capacity",
" freeSectionCount++",
"",
" if startIndex + capacity == nextFreeIndex",
" nextFreeIndex = startIndex",
" freeSectionCount--",
" else",
" compactFreeList()",
"",
" if freeSectionCount < MAX_FREE_SECTIONS",
" freeSectionStart[freeSectionCount] = startIndex",
" freeSectionCapacity[freeSectionCount] = capacity",
" freeSectionCount++",
"",
" private function grow()",
" let newCapacity = capacity * 2",
" let oldStart = startIndex",
" let oldCapacity = capacity",
"",
" allocateStorage(newCapacity)",
"",
" for i = 0 to size - 1",
" store[startIndex + i] = store[oldStart + i]",
"",
" let tempStart = startIndex",
" let tempCap = capacity",
" startIndex = oldStart",
" capacity = oldCapacity",
" freeStorage()",
" startIndex = tempStart",
" capacity = tempCap",
"",
" ondestroy",
" for i = 0 to size - 1",
" store[startIndex + i] = null",
"",
" // Return storage to free pool",
" freeStorage()",
"",
" /** Adds one or more elements to the end of the list (amortized O(1)) */",
" function add(vararg T elems)",
" for elem in elems",
" if size >= capacity",
" grow()",
" store[startIndex + size] = elem",
" size++",
"",
" /** Returns the element at the specified index (O(1)) */",
" function get(int index) returns T",
" if index < 0 or index >= size",
" return store[startIndex + index]",
"",
" /** Removes the element at the given index and returns it (O(n) - shifts elements) */",
" function removeAt(int index) returns T",
" if index < 0 or index >= size",
"",
" let elem = store[startIndex + index]",
"",
" // Shift elements left",
" for i = index to size - 2",
" store[startIndex + i] = store[startIndex + i + 1]",
"",
" size--",
" return elem",
"",
" /** Returns the size of the list (O(1)) */",
" function size() returns int",
" return size",
"",
" /** Checks whether this list is empty (O(1)) */",
" function isEmpty() returns boolean",
" return size == 0",
"",
"",
"public function lazy<T:>(Lazy<T> l) returns Lazy<T>",
" return l",
"",
"public abstract class Lazy<T:>",
" T val = null",
" var wasRetrieved = false",
"",
" abstract function retrieve() returns T",
"",
" function get() returns T",
" if not wasRetrieved",
" val = retrieve()",
" wasRetrieved = true",
" return val",
"",
"public class CFBuilding",
" CFBuilding precursor = null",
" ArrayList<CFBuilding> upgrades = null",
"",
" Lazy<boolean> hasAAUpgrade = lazy<boolean>(() -> begin",
" var result = false",
" if upgrades != null",
" result = true",
" // perform iterative search through \"tree\"",
" var toCheck = new ArrayList<CFBuilding>()",
" // toCheck.addAll(upgrades)",
" while not toCheck.isEmpty()",
" let b = toCheck.removeAt(0)",
" if b.isAntiAir",
" result = true",
" break",
" if b.upgrades != null",
" // toCheck.addAll(b.upgrades)",
"",
" destroy toCheck",
" return result",
" end)",
"",
" var netWorthDiv100 = lazy<real>(() -> begin",
" var worth = 0",
" var cur = this",
" while cur != null",
" worth += cur.goldCost",
" cur = cur.precursor",
" return worth / 200.",
" end)",
"",
" var goldCost = 0",
" var isAntiAir = false // Is the building an anti air unit",
"",
"init",
" let b = new CFBuilding()",
" b.upgrades = new ArrayList<CFBuilding>()",
" let aa = b",
" .hasAAUpgrade.get()",
" if aa == true",
" testSuccess()",
""
);
}


}
Loading