Skip to content

Commit 5705cc3

Browse files
committed
try to implement first class function
1 parent f055bff commit 5705cc3

15 files changed

+5238
-48
lines changed

src/builtins.ts

Lines changed: 27 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,9 @@ 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 = (classReference && !classReference.hasDecorator(DecoratorFlags.Unmanaged)) || (signatureReference && signatureReference.hasEnv);
10696+
if (isGcObject && global.is(CommonFlags.Compiled)) {
1067610697
if (global.is(CommonFlags.Inlined)) {
1067710698
let value = global.constantIntegerValue;
1067810699
if (i64_low(value) || i64_high(value)) {

src/common.ts

Lines changed: 1 addition & 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";

src/compiler.ts

Lines changed: 74 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,64 @@ export class Compiler extends DiagnosticEmitter {
70387043
return module.unreachable();
70397044
}
70407045

7046+
compileFirstClassFunction(
7047+
expression: FunctionExpression
7048+
// TODO(support contextualType)
7049+
): ExpressionRef {
7050+
// class AnonymousFunctionClass extends Function<T> {}
7051+
let module = this.module;
7052+
let flow = this.currentFlow;
7053+
let sourceFunction = flow.sourceFunction;
7054+
let declaration = expression.declaration.clone();
7055+
let anonymousId = sourceFunction.nextAnonymousId++;
7056+
let contextualTypeArguments = cloneMap(flow.contextualTypeArguments);
7057+
7058+
let classPrototype = new ClassPrototype(
7059+
`${sourceFunction.internalName}|anonymous|${anonymousId}`,
7060+
sourceFunction,
7061+
Node.createClassDeclaration(
7062+
Node.createIdentifierExpression(
7063+
`${sourceFunction.internalName}|anonymous|${anonymousId}`, declaration.range
7064+
),
7065+
null,
7066+
CommonFlags.None,
7067+
null,
7068+
null,
7069+
null,
7070+
[],
7071+
declaration.range
7072+
),
7073+
DecoratorFlags.None
7074+
);
7075+
let classInstance = this.resolver.resolveClass(classPrototype, null, contextualTypeArguments, ReportMode.Report);
7076+
if (!classInstance) return this.module.unreachable();
7077+
7078+
declaration.flags |= CommonFlags.Instance;
7079+
let functionPrototype = new FunctionPrototype(
7080+
`anonymous|${anonymousId}`,
7081+
classInstance,
7082+
declaration,
7083+
DecoratorFlags.None
7084+
);
7085+
7086+
let instance = this.resolver.resolveFirstClassFunction(functionPrototype, null, contextualTypeArguments);
7087+
if (!instance) return this.module.unreachable();
7088+
instance.flow.outer = flow;
7089+
7090+
let worked = this.compileFunction(instance);
7091+
this.currentType = instance.signature.type;
7092+
if (!worked) return module.unreachable();
7093+
7094+
let currentType = this.currentType;
7095+
if (!instance) return module.unreachable();
7096+
let rtInstance = assert(this.resolver.resolveClass(this.program.functionPrototype, [ instance.type ]));
7097+
const functionIndexInTable = this.registerFunctionInTable(instance);
7098+
let ctor = this.ensureConstructor(rtInstance, expression);
7099+
let expr = this.makeCallDirect(ctor, [module.i32(0), module.i32(functionIndexInTable), module.usize(0)], expression, /* immediatelyDropped */ false);
7100+
this.currentType = currentType;
7101+
return expr;
7102+
}
7103+
70417104
private compileFunctionExpression(
70427105
expression: FunctionExpression,
70437106
contextualType: Type,
@@ -8774,7 +8837,7 @@ export class Compiler extends DiagnosticEmitter {
87748837
classInstance.type,
87758838
classInstance.type,
87768839
baseCtor.signature.requiredParameters,
8777-
baseCtor.signature.hasRest
8840+
baseCtor.signature.hasRest ? SignatureFlags.Rest : SignatureFlags.None
87788841
),
87798842
contextualTypeArguments
87808843
);

src/program.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,6 +1010,12 @@ export class Program extends DiagnosticEmitter {
10101010
this.makeNativeTypeDeclaration(CommonNames.nonnull, CommonFlags.Export | CommonFlags.Generic),
10111011
DecoratorFlags.Builtin
10121012
));
1013+
this.nativeFile.add(CommonNames.experimental_first_class_function, new TypeDefinition(
1014+
CommonNames.experimental_first_class_function,
1015+
this.nativeFile,
1016+
this.makeNativeTypeDeclaration(CommonNames.experimental_first_class_function, CommonFlags.Export | CommonFlags.Generic),
1017+
DecoratorFlags.Builtin
1018+
));
10131019

10141020
// The following types might not be enabled by compiler options, so the
10151021
// compiler needs to check this condition whenever such a value is created

0 commit comments

Comments
 (0)