Skip to content
Open
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c299297
Implement closure support for captured variables
BlobMaster41 Dec 10, 2025
6b93f9a
Fix closure environment slot calculation and test coverage
BlobMaster41 Dec 10, 2025
407d6c8
Improve closure environment handling and alignment
BlobMaster41 Dec 10, 2025
e4d88ae
Handle function expressions in 'new' constructor args
BlobMaster41 Dec 11, 2025
eeecdd3
Add closure class compiler tests
BlobMaster41 Dec 11, 2025
82b3e4b
Add closures feature flag and enablement checks
BlobMaster41 Dec 11, 2025
3534c62
Simplify indirect call handling without closures
BlobMaster41 Dec 11, 2025
29608dd
Improve closure environment handling and parent pointer support
BlobMaster41 Dec 11, 2025
efbb38b
Update closure environment layout in WAT tests
BlobMaster41 Dec 11, 2025
4d39b34
Add Anakun to NOTICE contributors list
BlobMaster41 Dec 11, 2025
7e97c46
Handle 'this' capture in closures and methods
BlobMaster41 Dec 11, 2025
218d31b
Refactor closures to capture 'this' directly
BlobMaster41 Dec 11, 2025
80a05b5
Recompiled tests
BlobMaster41 Dec 11, 2025
be88ec4
Add closure capture support for more statement types
BlobMaster41 Dec 12, 2025
a953769
Nitpick: extra $
BlobMaster41 Dec 12, 2025
63c7ab6
Fix closure capture for default parameter values
BlobMaster41 Dec 12, 2025
222c207
Refactor closure capture analysis and remove collectCapturedNames
BlobMaster41 Dec 12, 2025
841411c
Refactor closure capture logic and add closures test config
BlobMaster41 Dec 12, 2025
606bf4d
Refactor environment slot allocation logic
BlobMaster41 Dec 12, 2025
e7b5fd9
Refactor closure capture analysis and minor cleanups
BlobMaster41 Dec 12, 2025
64e11d9
Refactor closure capture analysis and recompilation logic
BlobMaster41 Dec 12, 2025
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
Prev Previous commit
Next Next commit
Refactor environment slot allocation logic
Simplifies and streamlines the allocation of environment slots for captured locals and 'this' references by removing unnecessary variable assignments and directly assigning results. This improves code readability and reduces redundancy in the closure environment setup.
  • Loading branch information
BlobMaster41 committed Jan 27, 2026
commit 606bf4d0c8f7d7577dd003117a372a134e63dd51
27 changes: 9 additions & 18 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1782,8 +1782,7 @@ export class Compiler extends DiagnosticEmitter {
let preCapturedNames = instance.preCapturedNames;
if (preCapturedNames && preCapturedNames.size > 0) {
// Check if any parameters are captured
let parameterTypes = instance.signature.parameterTypes;
for (let i = 0, k = parameterTypes.length; i < k; i++) {
for (let i = 0, k = instance.signature.parameterTypes.length; i < k; i++) {
let paramName = instance.getParameterName(i);
if (preCapturedNames.has(paramName)) {
let local = flow.lookupLocal(paramName);
Expand All @@ -1798,24 +1797,21 @@ export class Compiler extends DiagnosticEmitter {
if (!capturedLocals.has(local)) {
// Calculate proper byte offset with alignment
// Reserve slot 0 for parent environment pointer (4 or 8 bytes depending on wasm32/64)
let ptrSize = this.options.usizeType.byteSize;
let currentOffset = ptrSize; // Start after parent pointer slot
let currentOffset = this.options.usizeType.byteSize; // Start after parent pointer slot
for (let _keys = Map_keys(capturedLocals), j = 0, m = _keys.length; j < m; ++j) {
let existingLocal = _keys[j];
let endOfSlot = existingLocal.envSlotIndex + existingLocal.type.byteSize;
if (endOfSlot > currentOffset) currentOffset = endOfSlot;
}
// Align to the type's natural alignment
let typeSize = local.type.byteSize;
let align = typeSize;
let align = local.type.byteSize;
currentOffset = (currentOffset + align - 1) & ~(align - 1);
local.envSlotIndex = currentOffset;
local.envOwner = instance; // Track which function owns this capture
capturedLocals.set(local, local.envSlotIndex);
}
if (!instance.envLocal) {
let envLocal = flow.addScopedLocal("$env", this.options.usizeType);
instance.envLocal = envLocal;
instance.envLocal = flow.addScopedLocal("$env", this.options.usizeType);
}
}
}
Expand All @@ -1832,23 +1828,20 @@ export class Compiler extends DiagnosticEmitter {
instance.capturedLocals = capturedLocals;
}
if (!capturedLocals.has(thisLocal)) {
let ptrSize = this.options.usizeType.byteSize;
let currentOffset = ptrSize;
let currentOffset = this.options.usizeType.byteSize;
for (let _keys = Map_keys(capturedLocals), j = 0, m = _keys.length; j < m; ++j) {
let existingLocal = _keys[j];
let endOfSlot = existingLocal.envSlotIndex + existingLocal.type.byteSize;
if (endOfSlot > currentOffset) currentOffset = endOfSlot;
}
let typeSize = thisLocal.type.byteSize;
let align = typeSize;
let align = thisLocal.type.byteSize;
currentOffset = (currentOffset + align - 1) & ~(align - 1);
thisLocal.envSlotIndex = currentOffset;
thisLocal.envOwner = instance;
capturedLocals.set(thisLocal, thisLocal.envSlotIndex);
}
if (!instance.envLocal) {
let envLocal = flow.addScopedLocal("$env", this.options.usizeType);
instance.envLocal = envLocal;
instance.envLocal = flow.addScopedLocal("$env", this.options.usizeType);
}
}
}
Expand All @@ -1858,8 +1851,7 @@ export class Compiler extends DiagnosticEmitter {
// the environment pointer. This is needed because indirect calls to other closures
// can overwrite the global $~lib/__closure_env.
if (instance.outerFunction && !instance.closureEnvLocal) {
let closureEnvLocal = flow.addScopedLocal("$closureEnv", this.options.usizeType);
instance.closureEnvLocal = closureEnvLocal;
instance.closureEnvLocal = flow.addScopedLocal("$closureEnv", this.options.usizeType);
}

// compile statements
Expand Down Expand Up @@ -2005,8 +1997,7 @@ export class Compiler extends DiagnosticEmitter {
valueTypeRef, property.memoryOffset
);
let flowBefore = this.currentFlow;
let flow = getterInstance.flow;
this.currentFlow = flow;
this.currentFlow = getterInstance.flow;
if (property.is(CommonFlags.DefinitelyAssigned) && valueType.isReference && !valueType.isNullableReference) {
body = this.makeRuntimeNonNullCheck(body, valueType, getterInstance.identifierNode);
}
Expand Down