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
1 change: 1 addition & 0 deletions NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ under the licensing terms detailed in LICENSE:
* Kam Chehresa <kaz.che@gmail.com>
* Mopsgamer <79159094+Mopsgamer@users.noreply.github.com>
* EDM115 <github@edm115.dev>
* Anakun <anakun@opnet.org>

Portions of this software are derived from third-party works licensed under
the following terms:
Expand Down
1 change: 1 addition & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export namespace CommonNames {
export const ASC_FEATURE_RELAXED_SIMD = "ASC_FEATURE_RELAXED_SIMD";
export const ASC_FEATURE_EXTENDED_CONST = "ASC_FEATURE_EXTENDED_CONST";
export const ASC_FEATURE_STRINGREF = "ASC_FEATURE_STRINGREF";
export const ASC_FEATURE_CLOSURES = "ASC_FEATURE_CLOSURES";
export const ASC_VERSION_MAJOR = "ASC_VERSION_MAJOR";
export const ASC_VERSION_MINOR = "ASC_VERSION_MINOR";
export const ASC_VERSION_PATCH = "ASC_VERSION_PATCH";
Expand Down
1,711 changes: 1,548 additions & 163 deletions src/compiler.ts

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,17 @@ export class Flow {
return null;
}

/** Looks up a local in outer function scopes (for closures). */
lookupLocalInOuter(name: string): Local | null {
let outerFlow: Flow | null = this.outer;
while (outerFlow) {
let local = outerFlow.lookupLocal(name);
if (local) return local;
outerFlow = outerFlow.outer;
}
return null;
}

/** Looks up the element with the specified name relative to the scope of this flow. */
lookup(name: string): Element | null {
let element = this.lookupLocal(name);
Expand Down
2 changes: 2 additions & 0 deletions src/index-wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ export const FEATURE_RELAXED_SIMD = Feature.RelaxedSimd;
export const FEATURE_EXTENDED_CONST = Feature.ExtendedConst;
/** String references. */
export const FEATURE_STRINGREF = Feature.Stringref;
/** Closures. */
export const FEATURE_CLOSURES = Feature.Closures;
/** All features. */
export const FEATURES_ALL = Feature.All;
/** Default features. */
Expand Down
37 changes: 37 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,8 @@ export class Program extends DiagnosticEmitter {
i64_new(options.hasFeature(Feature.ExtendedConst) ? 1 : 0, 0));
this.registerConstantInteger(CommonNames.ASC_FEATURE_STRINGREF, Type.bool,
i64_new(options.hasFeature(Feature.Stringref) ? 1 : 0, 0));
this.registerConstantInteger(CommonNames.ASC_FEATURE_CLOSURES, Type.bool,
i64_new(options.hasFeature(Feature.Closures) ? 1 : 0, 0));

// remember deferred elements
let queuedImports = new Array<QueuedImport>();
Expand Down Expand Up @@ -3628,6 +3630,15 @@ export class Local extends VariableLikeElement {
/** Original name of the (temporary) local. */
private originalName: string;

/** Whether this local is captured by a closure. */
isCaptured: bool = false;

/** Environment slot index if captured, -1 otherwise. */
envSlotIndex: i32 = -1;

/** The function whose environment this local is stored in. Set when captured. */
envOwner: Function | null = null;

/** Constructs a new local variable. */
constructor(
/** Simple name. */
Expand Down Expand Up @@ -3785,6 +3796,32 @@ export class Function extends TypedElement {
/** Counting id of anonymous inner functions. */
nextAnonymousId: i32 = 0;

// Closure support

/** Set of locals from outer scopes that this function captures. Maps Local to slot index. */
capturedLocals: Map<Local, i32> | null = null;

/** The environment class for this function's captured locals, if any. */
envClass: Class | null = null;

/** The local variable holding the environment pointer in outer function. */
envLocal: Local | null = null;

/** The outer function whose environment this closure accesses. */
outerFunction: Function | null = null;

/** Local variable in a closure function that caches the environment pointer from the global.
* This is needed because indirect calls can overwrite the global. */
closureEnvLocal: Local | null = null;

/** Pre-scanned names of captured variables (set before compilation, used to mark locals). */
preCapturedNames: Set<string> | null = null;

/** Whether this function requires an environment (is a closure). */
get needsEnvironment(): bool {
return this.capturedLocals != null && this.capturedLocals.size > 0;
}

/** Constructs a new concrete function. */
constructor(
/** Name incl. type parameters, i.e. `foo<i32>`. */
Expand Down
10 changes: 10 additions & 0 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2175,6 +2175,16 @@ export class Resolver extends DiagnosticEmitter {
return thisLocal;
}
}
// Check for captured 'this' in closures - look up in outer flow chain
let thisLocal = ctxFlow.lookupLocal(CommonNames.this_);
if (!thisLocal) {
thisLocal = ctxFlow.lookupLocalInOuter(CommonNames.this_);
}
if (thisLocal) {
this.currentThisExpression = null;
this.currentElementExpression = null;
return thisLocal;
}
let parent = ctxFlow.sourceFunction.parent;
if (parent) {
this.currentThisExpression = null;
Expand Down
5 changes: 4 additions & 1 deletion std/assembly/shared/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ export const enum Feature {
ExtendedConst = 1 << 13, // see: https://github.com/WebAssembly/extended-const
/** Reference typed strings. */
Stringref = 1 << 14, // see: https://github.com/WebAssembly/stringref
/** Closures. */
Closures = 1 << 15,
/** All features. */
All = (1 << 15) - 1
All = (1 << 16) - 1
}

/** Gets the name of the specified feature one would specify on the command line. */
Expand All @@ -56,6 +58,7 @@ export function featureToString(feature: Feature): string {
case Feature.RelaxedSimd: return "relaxed-simd";
case Feature.ExtendedConst: return "extended-const";
case Feature.Stringref: return "stringref";
case Feature.Closures: return "closures";
}
assert(false);
return "";
Expand Down
Loading