Skip to content

Commit 41cf0f7

Browse files
committed
implement first class function
1 parent f055bff commit 41cf0f7

15 files changed

+4858
-49
lines changed

src/builtins.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ import {
4343
NodeKind,
4444
LiteralExpression,
4545
ArrayLiteralExpression,
46-
IdentifierExpression
46+
IdentifierExpression,
47+
FunctionExpression
4748
} from "./ast";
4849

4950
import {
@@ -194,6 +195,8 @@ export namespace BuiltinNames {
194195
export const instantiate = "~lib/builtins/instantiate";
195196
export const idof = "~lib/builtins/idof";
196197

198+
export const experimental_first_class_function = "~lib/builtins/experimental_first_class_function";
199+
197200
export const i8 = "~lib/builtins/i8";
198201
export const i16 = "~lib/builtins/i16";
199202
export const i32 = "~lib/builtins/i32";
@@ -3609,6 +3612,26 @@ function builtin_unchecked(ctx: BuiltinFunctionContext): ExpressionRef {
36093612
}
36103613
builtinFunctions.set(BuiltinNames.unchecked, builtin_unchecked);
36113614

3615+
// experimental_first_class_function(FunctionExpression: *) -> *
3616+
function builtin_experimental_first_class_function(ctx: BuiltinFunctionContext): ExpressionRef {
3617+
let compiler = ctx.compiler;
3618+
let module = compiler.module;
3619+
if (
3620+
checkTypeAbsent(ctx) |
3621+
checkArgsRequired(ctx, 1)
3622+
) return module.unreachable();
3623+
let operand = ctx.operands[0];
3624+
if (operand.kind != NodeKind.Function) {
3625+
let prototype = ctx.prototype;
3626+
prototype.program.error(DiagnosticCode._0_expected, operand.range, "FunctionExpression");
3627+
return module.unreachable();
3628+
}
3629+
let functionExpression = <FunctionExpression>operand;
3630+
let expr = compiler.compileFirstClassFunction(functionExpression);
3631+
return expr;
3632+
}
3633+
builtinFunctions.set(BuiltinNames.experimental_first_class_function, builtin_experimental_first_class_function);
3634+
36123635
// call_indirect<T?>(index: u32, ...args: *[]) -> T
36133636
function builtin_call_indirect(ctx: BuiltinFunctionContext): ExpressionRef {
36143637
let compiler = ctx.compiler;
@@ -10668,11 +10691,11 @@ export function compileVisitGlobals(compiler: Compiler): void {
1066810691
let global = <Global>element;
1066910692
let globalType = global.type;
1067010693
let classReference = globalType.getClass();
10671-
if (
10672-
classReference &&
10673-
!classReference.hasDecorator(DecoratorFlags.Unmanaged) &&
10674-
global.is(CommonFlags.Compiled)
10675-
) {
10694+
let signatureReference = globalType.getSignature();
10695+
let isGcObject =
10696+
(classReference != null && !classReference.hasDecorator(DecoratorFlags.Unmanaged)) ||
10697+
(signatureReference != null && signatureReference.hasEnv);
10698+
if (isGcObject && global.is(CommonFlags.Compiled)) {
1067610699
if (global.is(CommonFlags.Inlined)) {
1067710700
let value = global.constantIntegerValue;
1067810701
if (i64_low(value) || i64_high(value)) {

src/common.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ export namespace CommonNames {
158158
export const valueof = "valueof";
159159
export const returnof = "returnof";
160160
export const nonnull = "nonnull";
161+
export const experimental_first_class_function = "experimental_first_class_function";
161162
// aliases
162163
export const null_ = "null";
163164
export const true_ = "true";
@@ -225,6 +226,7 @@ export namespace CommonNames {
225226
export const Set = "Set";
226227
export const Map = "Map";
227228
export const Function = "Function";
229+
export const FirstClassFunction = "FirstClassFunctionBase";
228230
export const ArrayBufferView = "ArrayBufferView";
229231
export const ArrayBuffer = "ArrayBuffer";
230232
export const Math = "Math";

src/compiler.ts

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ import {
188188
TypeKind,
189189
TypeFlags,
190190
Signature,
191-
typesToRefs
191+
typesToRefs,
192+
SignatureFlags
192193
} from "./types";
193194

194195
import {
@@ -2054,20 +2055,23 @@ export class Compiler extends DiagnosticEmitter {
20542055

20552056
// === Table ====================================================================================
20562057

2058+
private registerFunctionInTable(instance: Function): u32 {
2059+
// Add to the function table
2060+
let functionTable = this.functionTable;
2061+
let tableBase = this.options.tableBase;
2062+
if (!tableBase) tableBase = 1; // leave first elem blank
2063+
let index = tableBase + functionTable.length;
2064+
functionTable.push(instance);
2065+
return index;
2066+
}
2067+
20572068
/** Ensures that a runtime counterpart of the specified function exists and returns its address. */
20582069
ensureRuntimeFunction(instance: Function): i64 {
20592070
assert(instance.is(CommonFlags.Compiled) && !instance.is(CommonFlags.Stub));
20602071
let program = this.program;
20612072
let memorySegment = instance.memorySegment;
20622073
if (!memorySegment) {
2063-
2064-
// Add to the function table
2065-
let functionTable = this.functionTable;
2066-
let tableBase = this.options.tableBase;
2067-
if (!tableBase) tableBase = 1; // leave first elem blank
2068-
let index = tableBase + functionTable.length;
2069-
functionTable.push(instance);
2070-
2074+
let index = this.registerFunctionInTable(instance);
20712075
// Create runtime function
20722076
let rtInstance = assert(this.resolver.resolveClass(program.functionPrototype, [ instance.type ]));
20732077
let buf = rtInstance.createBuffer();
@@ -6085,12 +6089,13 @@ export class Compiler extends DiagnosticEmitter {
60856089
let functionArg = this.compileExpression(expression.expression, Type.auto);
60866090
let signature = this.currentType.getSignature();
60876091
if (signature) {
6092+
const thisArg = signature.hasEnv ? functionArg : 0;
60886093
return this.compileCallIndirect(
60896094
signature,
60906095
functionArg,
60916096
expression.args,
60926097
expression,
6093-
0,
6098+
thisArg,
60946099
contextualType == Type.void
60956100
);
60966101
}
@@ -7038,6 +7043,38 @@ export class Compiler extends DiagnosticEmitter {
70387043
return module.unreachable();
70397044
}
70407045

7046+
compileFirstClassFunction(
7047+
expression: FunctionExpression
7048+
): ExpressionRef {
7049+
let module = this.module;
7050+
let flow = this.currentFlow;
7051+
let sourceFunction = flow.sourceFunction;
7052+
let declaration = expression.declaration.clone();
7053+
let anonymousId = sourceFunction.nextAnonymousId++;
7054+
let contextualTypeArguments = cloneMap(flow.contextualTypeArguments);
7055+
7056+
let prototype = new FunctionPrototype(
7057+
`${sourceFunction.internalName}|anonymous|${anonymousId}`,
7058+
sourceFunction,
7059+
declaration
7060+
);
7061+
let instance = this.resolver.resolveFirstClassFunction(prototype, contextualTypeArguments, ReportMode.Report);
7062+
if (!instance) return this.module.unreachable();
7063+
instance.flow.outer = flow;
7064+
7065+
let worked = this.compileFunction(instance);
7066+
this.currentType = instance.signature.type;
7067+
if (!worked) return module.unreachable();
7068+
7069+
const currentType = this.currentType;
7070+
if (!instance) return module.unreachable();
7071+
const functionIndexInTable = this.registerFunctionInTable(instance);
7072+
let ctor = this.ensureConstructor(this.program.firstClassFunctionInstance, expression);
7073+
let expr = this.makeCallDirect(ctor, [module.i32(0), module.i32(functionIndexInTable), module.usize(0)], expression, /* immediatelyDropped */ false);
7074+
this.currentType = currentType;
7075+
return expr;
7076+
}
7077+
70417078
private compileFunctionExpression(
70427079
expression: FunctionExpression,
70437080
contextualType: Type,
@@ -8774,7 +8811,7 @@ export class Compiler extends DiagnosticEmitter {
87748811
classInstance.type,
87758812
classInstance.type,
87768813
baseCtor.signature.requiredParameters,
8777-
baseCtor.signature.hasRest
8814+
baseCtor.signature.hasRest ? SignatureFlags.Rest : SignatureFlags.None
87788815
),
87798816
contextualTypeArguments
87808817
);

src/program.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,14 @@ export class Program extends DiagnosticEmitter {
538538
}
539539
private _functionPrototype: ClassPrototype | null = null;
540540

541+
/** Get the first class `Function` instance */
542+
get firstClassFunctionInstance(): Class {
543+
let cached = this._firstClassFunctionInstance;
544+
if (!cached) this._firstClassFunctionInstance = cached = this.requireClass(CommonNames.FirstClassFunction);
545+
return cached;
546+
}
547+
private _firstClassFunctionInstance: Class | null = null;
548+
541549
/** Gets the standard `Int8Array` prototype. */
542550
get int8ArrayPrototype(): ClassPrototype {
543551
let cached = this._int8ArrayPrototype;
@@ -1010,6 +1018,12 @@ export class Program extends DiagnosticEmitter {
10101018
this.makeNativeTypeDeclaration(CommonNames.nonnull, CommonFlags.Export | CommonFlags.Generic),
10111019
DecoratorFlags.Builtin
10121020
));
1021+
this.nativeFile.add(CommonNames.experimental_first_class_function, new TypeDefinition(
1022+
CommonNames.experimental_first_class_function,
1023+
this.nativeFile,
1024+
this.makeNativeTypeDeclaration(CommonNames.experimental_first_class_function, CommonFlags.Export | CommonFlags.Generic),
1025+
DecoratorFlags.Builtin
1026+
));
10131027

10141028
// The following types might not be enabled by compiler options, so the
10151029
// compiler needs to check this condition whenever such a value is created
@@ -3741,7 +3755,6 @@ export class FunctionPrototype extends DeclaredElement {
37413755

37423756
/** A resolved function. */
37433757
export class Function extends TypedElement {
3744-
37453758
/** Function prototype. */
37463759
prototype: FunctionPrototype;
37473760
/** Function signature. */

0 commit comments

Comments
 (0)