Skip to content

Commit 6b35f1e

Browse files
committed
WIP has optimize bug
1 parent e3e4166 commit 6b35f1e

13 files changed

+4456
-40
lines changed

src/builtins.ts

Lines changed: 24 additions & 1 deletion
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;

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: 75 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();
@@ -6082,12 +6086,13 @@ export class Compiler extends DiagnosticEmitter {
60826086
let functionArg = this.compileExpression(expression.expression, Type.auto);
60836087
let signature = this.currentType.getSignature();
60846088
if (signature) {
6089+
const thisArg = signature.hasEnv ? functionArg : 0;
60856090
return this.compileCallIndirect(
60866091
signature,
60876092
functionArg,
60886093
expression.args,
60896094
expression,
6090-
0,
6095+
thisArg,
60916096
contextualType == Type.void
60926097
);
60936098
}
@@ -7035,6 +7040,65 @@ export class Compiler extends DiagnosticEmitter {
70357040
return module.unreachable();
70367041
}
70377042

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

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

src/resolver.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ import {
8484
Signature,
8585
typesToString,
8686
TypeKind,
87-
TypeFlags
87+
TypeFlags,
88+
SignatureFlags,
8889
} from "./types";
8990

9091
import {
@@ -309,6 +310,9 @@ export class Resolver extends DiagnosticEmitter {
309310
if (text == CommonNames.valueof) return this.resolveBuiltinValueofType(node, ctxElement, ctxTypes, reportMode);
310311
if (text == CommonNames.returnof) return this.resolveBuiltinReturnTypeType(node, ctxElement, ctxTypes, reportMode);
311312
if (text == CommonNames.nonnull) return this.resolveBuiltinNotNullableType(node, ctxElement, ctxTypes, reportMode);
313+
if (text == CommonNames.experimental_first_class_function) {
314+
return this.resolveBuiltinFirstClassFunctionType(node, ctxElement, ctxTypes, reportMode);
315+
}
312316
}
313317

314318
// Resolve normally
@@ -383,7 +387,7 @@ export class Resolver extends DiagnosticEmitter {
383387
let numParameters = parameterNodes.length;
384388
let parameterTypes = new Array<Type>(numParameters);
385389
let requiredParameters = 0;
386-
let hasRest = false;
390+
let flags = SignatureFlags.None;
387391
for (let i = 0; i < numParameters; ++i) {
388392
let parameterNode = parameterNodes[i];
389393
switch (parameterNode.parameterKind) {
@@ -393,7 +397,7 @@ export class Resolver extends DiagnosticEmitter {
393397
}
394398
case ParameterKind.Rest: {
395399
assert(i == numParameters - 1);
396-
hasRest = true;
400+
flags |= SignatureFlags.Rest;
397401
break;
398402
}
399403
}
@@ -435,7 +439,7 @@ export class Resolver extends DiagnosticEmitter {
435439
);
436440
if (!returnType) return null;
437441
}
438-
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, hasRest);
442+
let signature = Signature.create(this.program, parameterTypes, returnType, thisType, requiredParameters, flags);
439443
return node.isNullable ? signature.type.asNullable() : signature.type;
440444
}
441445

@@ -589,6 +593,28 @@ export class Resolver extends DiagnosticEmitter {
589593
return typeArgument.nonNullableType;
590594
}
591595

596+
private resolveBuiltinFirstClassFunctionType(
597+
/** The type to resolve. */
598+
node: NamedTypeNode,
599+
/** Contextual element. */
600+
ctxElement: Element,
601+
/** Contextual types, i.e. `T`. */
602+
ctxTypes: Map<string,Type> | null = null,
603+
/** How to proceed with eventual diagnostics. */
604+
reportMode: ReportMode = ReportMode.Report
605+
): Type | null {
606+
const typeArgumentNode = this.ensureOneTypeArgument(node, reportMode);
607+
if (!typeArgumentNode) return null;
608+
let typeArgument = this.resolveType(typeArgumentNode, ctxElement, ctxTypes, reportMode);
609+
if (!typeArgument) return null;
610+
let signature = typeArgument.getSignature();
611+
// TODO(error)
612+
if (!signature) return null;
613+
signature.setEnv();
614+
signature.thisType = this.program.objectInstance.type;
615+
return typeArgument;
616+
}
617+
592618
/** Resolves a type name to the program element it refers to. */
593619
resolveTypeName(
594620
/** The type name to resolve. */
@@ -2761,7 +2787,7 @@ export class Resolver extends DiagnosticEmitter {
27612787
type,
27622788
signatureReference.thisType,
27632789
signatureReference.requiredParameters,
2764-
signatureReference.hasRest,
2790+
signatureReference.flags,
27652791
);
27662792
}
27672793
}

0 commit comments

Comments
 (0)