mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 07:02:13 +00:00
Initial function calls
This commit is contained in:
parent
d3d4938b68
commit
dc74dd118d
@ -440,23 +440,27 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createCall(target: BinaryenFunctionRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
|
||||
createCall(target: string, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
|
||||
if (this.noEmit) return 0;
|
||||
const cStr: CString = allocString(target);
|
||||
const cArr: CArray<i32> = allocI32Array(operands);
|
||||
try {
|
||||
return _BinaryenCall(this.ref, target, cArr, operands.length, returnType);
|
||||
return _BinaryenCall(this.ref, cStr, cArr, operands.length, returnType);
|
||||
} finally {
|
||||
_free(cArr);
|
||||
_free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
createCallImport(target: BinaryenImportRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
|
||||
createCallImport(target: string, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
|
||||
if (this.noEmit) return 0;
|
||||
const cStr: CString = allocString(target);
|
||||
const cArr: CArray<i32> = allocI32Array(operands);
|
||||
try {
|
||||
return _BinaryenCallImport(this.ref, target, cArr, operands.length, returnType);
|
||||
return _BinaryenCallImport(this.ref, cStr, cArr, operands.length, returnType);
|
||||
} finally {
|
||||
_free(cArr);
|
||||
_free(cStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp, Type as BinaryenType, Relooper } from "./binaryen";
|
||||
import { PATH_DELIMITER } from "./constants";
|
||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||
import { Program, ClassPrototype, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program";
|
||||
import { Program, ClassPrototype, Class, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program";
|
||||
import { CharCode, I64, U64, normalizePath, sb } from "./util";
|
||||
import { Token } from "./tokenizer";
|
||||
import { Token, Range } from "./tokenizer";
|
||||
import {
|
||||
|
||||
Node,
|
||||
@ -376,18 +376,21 @@ export class Compiler extends DiagnosticEmitter {
|
||||
const element: Element | null = <Element | null>this.program.elements.get(internalName);
|
||||
if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE)
|
||||
throw new Error("unexpected missing function");
|
||||
const resolvedTypeArguments: Type[] | null = this.program.resolveTypeArguments(declaration.typeParameters, typeArguments, contextualTypeArguments, alternativeReportNode); // reports
|
||||
if (!resolvedTypeArguments)
|
||||
return;
|
||||
this.compileFunction(<FunctionPrototype>element, resolvedTypeArguments, contextualTypeArguments);
|
||||
this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode);
|
||||
}
|
||||
|
||||
compileFunction(template: FunctionPrototype, typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null = null): void {
|
||||
const instance: Function | null = template.resolve(typeArguments, contextualTypeArguments);
|
||||
if (!instance || instance.compiled)
|
||||
compileFunctionUsingTypeArguments(prototype: FunctionPrototype, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null) {
|
||||
const instance: Function | null = prototype.resolveInclTypeArguments(typeArguments, contextualTypeArguments, alternativeReportNode); // reports
|
||||
if (!instance)
|
||||
return;
|
||||
this.compileFunction(instance);
|
||||
}
|
||||
|
||||
compileFunction(instance: Function): void {
|
||||
if (instance.compiled)
|
||||
return;
|
||||
|
||||
const declaration: FunctionDeclaration | null = template.declaration;
|
||||
const declaration: FunctionDeclaration | null = instance.template.declaration;
|
||||
if (!declaration) // TODO: compile builtins
|
||||
throw new Error("not implemented");
|
||||
|
||||
@ -470,7 +473,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case ElementKind.CLASS_PROTOTYPE:
|
||||
if ((noTreeShaking || (<ClassPrototype>element).isExport) && !(<ClassPrototype>element).isGeneric)
|
||||
this.compileClass(<ClassPrototype>element, []);
|
||||
this.compileClassUsingTypeArguments(<ClassPrototype>element, []);
|
||||
break;
|
||||
|
||||
case ElementKind.ENUM:
|
||||
@ -479,7 +482,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case ElementKind.FUNCTION_PROTOTYPE:
|
||||
if ((noTreeShaking || (<FunctionPrototype>element).isExport) && !(<FunctionPrototype>element).isGeneric)
|
||||
this.compileFunction(<FunctionPrototype>element, []);
|
||||
this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, []);
|
||||
break;
|
||||
|
||||
case ElementKind.GLOBAL:
|
||||
@ -508,7 +511,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case ElementKind.CLASS_PROTOTYPE:
|
||||
if (!(<ClassPrototype>element).isGeneric)
|
||||
this.compileClass(<ClassPrototype>element, []);
|
||||
this.compileClassUsingTypeArguments(<ClassPrototype>element, []);
|
||||
break;
|
||||
|
||||
case ElementKind.ENUM:
|
||||
@ -517,7 +520,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
case ElementKind.FUNCTION_PROTOTYPE:
|
||||
if (!(<FunctionPrototype>element).isGeneric)
|
||||
this.compileFunction(<FunctionPrototype>element, []);
|
||||
this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, []);
|
||||
break;
|
||||
|
||||
case ElementKind.GLOBAL:
|
||||
@ -538,13 +541,17 @@ export class Compiler extends DiagnosticEmitter {
|
||||
const element: Element | null = <Element | null>this.program.elements.get(internalName);
|
||||
if (!element || element.kind != ElementKind.CLASS_PROTOTYPE)
|
||||
throw new Error("unexpected missing class");
|
||||
const resolvedTypeArguments: Type[] | null = this.program.resolveTypeArguments(declaration.typeParameters, typeArguments, contextualTypeArguments, alternativeReportNode); // reports
|
||||
if (!resolvedTypeArguments)
|
||||
return;
|
||||
this.compileClass(<ClassPrototype>element, resolvedTypeArguments, contextualTypeArguments);
|
||||
this.compileClassUsingTypeArguments(<ClassPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode);
|
||||
}
|
||||
|
||||
compileClass(cls: ClassPrototype, typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null = null) {
|
||||
compileClassUsingTypeArguments(prototype: ClassPrototype, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): void {
|
||||
const instance: Class | null = prototype.resolveInclTypeArguments(typeArguments, contextualTypeArguments, alternativeReportNode);
|
||||
if (!instance)
|
||||
return;
|
||||
this.compileClass(instance);
|
||||
}
|
||||
|
||||
compileClass(cls: Class) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
@ -1319,14 +1326,46 @@ export class Compiler extends DiagnosticEmitter {
|
||||
const element: Element | null = this.program.resolveElement(expression.expression, this.currentFunction); // reports
|
||||
if (!element)
|
||||
return this.module.createUnreachable();
|
||||
if (element.kind != ElementKind.FUNCTION_PROTOTYPE) {
|
||||
// TODO: report 'Cannot invoke an expression whose type lacks a call signature.'
|
||||
if (element.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
||||
const functionInstance: Function | null = (<FunctionPrototype>element).resolveInclTypeArguments(expression.typeArguments, this.currentFunction.contextualTypeArguments, expression); // reports
|
||||
if (!functionInstance)
|
||||
return this.module.createUnreachable();
|
||||
return this.compileCall(functionInstance, expression.arguments, expression);
|
||||
}
|
||||
this.error(DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures, expression.range, element.internalName);
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
|
||||
compileCall(functionInstance: Function, argumentExpressions: Expression[], reportNode: Node): BinaryenExpressionRef {
|
||||
if (!functionInstance.compiled)
|
||||
this.compileFunction(functionInstance);
|
||||
const parameters: Parameter[] = functionInstance.parameters;
|
||||
let k: i32 = parameters.length;
|
||||
if (argumentExpressions.length > k) {
|
||||
this.error(DiagnosticCode.Expected_0_arguments_but_got_1, reportNode.range, k.toString(10), argumentExpressions.length.toString(10));
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
throw new Error("not implemented");
|
||||
const operands: BinaryenExpressionRef[] = new Array(k);
|
||||
for (let i: i32 = 0; i < k; ++i) {
|
||||
if (argumentExpressions.length > i) {
|
||||
operands[i] = this.compileExpression(argumentExpressions[i], parameters[i].type);
|
||||
} else {
|
||||
const initializer: Expression | null = parameters[i].initializer;
|
||||
if (initializer) {
|
||||
operands[i] = this.compileExpression(initializer, parameters[i].type);
|
||||
} else {
|
||||
this.error(DiagnosticCode.Expected_at_least_0_arguments_but_got_1, reportNode.range, (i + 1).toString(10), argumentExpressions.length.toString(10));
|
||||
return this.module.createUnreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.module.createCall(functionInstance.internalName, operands, typeToBinaryenType(functionInstance.returnType));
|
||||
}
|
||||
|
||||
compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): BinaryenExpressionRef {
|
||||
const element: Element | null = this.program.resolveElement(expression.expression, this.currentFunction); // reports
|
||||
if (!element)
|
||||
return this.module.createUnreachable();
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
|
@ -52,12 +52,15 @@ export enum DiagnosticCode {
|
||||
Type_0_is_not_assignable_to_type_1 = 2322,
|
||||
_this_cannot_be_referenced_in_current_location = 2332,
|
||||
Property_0_does_not_exist_on_type_1 = 2339,
|
||||
Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures = 2349,
|
||||
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
|
||||
The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access = 2364,
|
||||
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
|
||||
Duplicate_function_implementation = 2393,
|
||||
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
|
||||
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
|
||||
Expected_0_arguments_but_got_1 = 2554,
|
||||
Expected_at_least_0_arguments_but_got_1 = 2555,
|
||||
Expected_0_type_arguments_but_got_1 = 2558,
|
||||
File_0_not_found = 6054
|
||||
}
|
||||
@ -115,12 +118,15 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
|
||||
case 2332: return "'this' cannot be referenced in current location.";
|
||||
case 2339: return "Property '{0}' does not exist on type '{1}'.";
|
||||
case 2349: return "Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.";
|
||||
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
|
||||
case 2364: return "The left-hand side of an assignment expression must be a variable or a property access.";
|
||||
case 2391: return "Function implementation is missing or not immediately following the declaration.";
|
||||
case 2393: return "Duplicate function implementation.";
|
||||
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
|
||||
case 2541: return "The target of an assignment must be a variable or a property access.";
|
||||
case 2554: return "Expected {0} arguments, but got {1}.";
|
||||
case 2555: return "Expected at least {0} arguments, but got {1}.";
|
||||
case 2558: return "Expected {0} type arguments, but got {1}.";
|
||||
case 6054: return "File '{0}' not found.";
|
||||
default: return "";
|
||||
|
@ -52,12 +52,15 @@
|
||||
"Type '{0}' is not assignable to type '{1}'.": 2322,
|
||||
"'this' cannot be referenced in current location.": 2332,
|
||||
"Property '{0}' does not exist on type '{1}'.": 2339,
|
||||
"Cannot invoke an expression whose type lacks a call signature. Type '{0}' has no compatible call signatures.": 2349,
|
||||
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
|
||||
"The left-hand side of an assignment expression must be a variable or a property access.": 2364,
|
||||
"Function implementation is missing or not immediately following the declaration.": 2391,
|
||||
"Duplicate function implementation.": 2393,
|
||||
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
|
||||
"The target of an assignment must be a variable or a property access.": 2541,
|
||||
"Expected {0} arguments, but got {1}.": 2554,
|
||||
"Expected at least {0} arguments, but got {1}.": 2555,
|
||||
"Expected {0} type arguments, but got {1}.": 2558,
|
||||
|
||||
"File '{0}' not found.": 6054
|
||||
|
@ -508,19 +508,19 @@ export class Program extends DiagnosticEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
resolveTypeArguments(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Type[] | null {
|
||||
resolveTypeArguments(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Type[] | null {
|
||||
const parameterCount: i32 = typeParameters.length;
|
||||
const argumentCount: i32 = typeArgumentNodes.length;
|
||||
const argumentCount: i32 = typeArgumentNodes ? typeArgumentNodes.length : 0;
|
||||
if (parameterCount != argumentCount) {
|
||||
if (argumentCount)
|
||||
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, Range.join(typeArgumentNodes[0].range, typeArgumentNodes[argumentCount - 1].range), parameterCount.toString(10), argumentCount.toString(10));
|
||||
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, Range.join((<TypeNode[]>typeArgumentNodes)[0].range, (<TypeNode[]>typeArgumentNodes)[argumentCount - 1].range), parameterCount.toString(10), argumentCount.toString(10));
|
||||
else if (alternativeReportNode)
|
||||
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, alternativeReportNode.range.atEnd, parameterCount.toString(10), "0");
|
||||
return null;
|
||||
}
|
||||
const typeArguments: Type[] = new Array(parameterCount);
|
||||
for (let i: i32 = 0; i < parameterCount; ++i) {
|
||||
const type: Type | null = this.resolveType(typeArgumentNodes[i], contextualTypeArguments, true); // reports
|
||||
const type: Type | null = this.resolveType((<TypeNode[]>typeArgumentNodes)[i], contextualTypeArguments, true); // reports
|
||||
if (!type)
|
||||
return null;
|
||||
// TODO: check extendsType
|
||||
@ -531,7 +531,7 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
resolveElement(expression: Expression, contextualFunction: Function): Element | null {
|
||||
|
||||
// this
|
||||
// this -> Class
|
||||
if (expression.kind == NodeKind.THIS) {
|
||||
if (contextualFunction.instanceMethodOf)
|
||||
return contextualFunction.instanceMethodOf;
|
||||
@ -539,6 +539,8 @@ export class Program extends DiagnosticEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
let ret: Element;
|
||||
|
||||
// local or global name
|
||||
if (expression.kind == NodeKind.IDENTIFIER) {
|
||||
const name: string = (<IdentifierExpression>expression).name;
|
||||
@ -557,10 +559,10 @@ export class Program extends DiagnosticEmitter {
|
||||
// static or instance property (incl. enum values) or method
|
||||
} else if (expression.kind == NodeKind.PROPERTYACCESS) {
|
||||
const target: Element | null = this.resolveElement((<PropertyAccessExpression>expression).expression, contextualFunction); // reports
|
||||
let member: Element | null = null;
|
||||
if (!target)
|
||||
return null;
|
||||
const propertyName: string = (<PropertyAccessExpression>expression).property.name;
|
||||
let member: Element | null = null;
|
||||
if (target.kind == ElementKind.ENUM)
|
||||
member = <EnumValue | null>(<Enum>target).members.get(propertyName);
|
||||
else if (target.kind == ElementKind.CLASS_PROTOTYPE)
|
||||
@ -702,10 +704,12 @@ export class Parameter {
|
||||
|
||||
name: string;
|
||||
type: Type;
|
||||
initializer: Expression | null;
|
||||
|
||||
constructor(name: string, type: Type) {
|
||||
constructor(name: string, type: Type, initializer: Expression | null = null) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -802,26 +806,51 @@ export class FunctionPrototype extends Element {
|
||||
this.instances.set(instanceKey, instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null, alternativeReportNode: Node | null): Function | null {
|
||||
let resolvedTypeArguments: Type[] | null;
|
||||
if (this.isGeneric) {
|
||||
if (!this.declaration)
|
||||
throw new Error("not implemented"); // generic builtin
|
||||
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
|
||||
if (!resolvedTypeArguments)
|
||||
return null;
|
||||
} else {
|
||||
// TODO: check typeArgumentNodes being empty
|
||||
resolvedTypeArguments = [];
|
||||
}
|
||||
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
|
||||
}
|
||||
}
|
||||
|
||||
/** A resolved function. */
|
||||
export class Function extends Element {
|
||||
|
||||
kind = ElementKind.FUNCTION;
|
||||
template: FunctionPrototype;
|
||||
typeArguments: Type[];
|
||||
parameters: Parameter[];
|
||||
returnType: Type;
|
||||
instanceMethodOf: Class | null;
|
||||
locals: Map<string,Local> = new Map();
|
||||
additionalLocals: Type[] = []; // without parameters
|
||||
breakContext: string | null = null;
|
||||
|
||||
/** Underlying function template. */
|
||||
template: FunctionPrototype;
|
||||
/** Concrete type arguments. */
|
||||
typeArguments: Type[];
|
||||
/** Concrete function parameters. */
|
||||
parameters: Parameter[];
|
||||
/** Concrete return type. */
|
||||
returnType: Type;
|
||||
/** If a method, the concrete class it is a member of. */
|
||||
instanceMethodOf: Class | null;
|
||||
/** Map of locals by name. */
|
||||
locals: Map<string,Local> = new Map();
|
||||
/** List of additional non-parameter locals. */
|
||||
additionalLocals: Type[] = [];
|
||||
/** Current break context label. */
|
||||
breakContext: string | null = null;
|
||||
/** Contextual type arguments. */
|
||||
contextualTypeArguments: Map<string,Type> = new Map();
|
||||
|
||||
private breakMajor: i32 = 0;
|
||||
private breakMinor: i32 = 0;
|
||||
|
||||
/** Constructs a new concrete function. */
|
||||
constructor(template: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) {
|
||||
super(template.program, internalName);
|
||||
this.template = template;
|
||||
@ -841,8 +870,10 @@ export class Function extends Element {
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests if this function is an instance method. */
|
||||
get isInstance(): bool { return this.instanceMethodOf != null; }
|
||||
|
||||
/** Adds a local of the specified type, with an optional name. */
|
||||
addLocal(type: Type, name: string | null = null): Local {
|
||||
// if it has a name, check previously as this method will throw otherwise
|
||||
let localIndex = this.parameters.length + this.additionalLocals.length;
|
||||
@ -857,12 +888,14 @@ export class Function extends Element {
|
||||
return local;
|
||||
}
|
||||
|
||||
/** Enters a(nother) break context. */
|
||||
enterBreakContext(): string {
|
||||
if (!this.breakMinor)
|
||||
this.breakMajor++;
|
||||
return this.breakContext = this.breakMajor.toString(10) + "." + (++this.breakMinor).toString(10);
|
||||
}
|
||||
|
||||
/** Leaves the current break context. */
|
||||
leaveBreakContext(): void {
|
||||
if (--this.breakMinor < 0)
|
||||
throw new Error("unexpected unbalanced break context");
|
||||
@ -931,6 +964,21 @@ export class ClassPrototype extends Namespace {
|
||||
throw new Error("unexpected instantiation of internal class");
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
resolveInclTypeArguments(typeArgumentNodes: TypeNode[] | null, contextualTypeArguments: Map<string,Type> | null, alternativeReportNode: Node | null): Class | null {
|
||||
let resolvedTypeArguments: Type[] | null;
|
||||
if (this.isGeneric) {
|
||||
if (!this.declaration)
|
||||
throw new Error("not implemented"); // generic builtin
|
||||
resolvedTypeArguments = this.program.resolveTypeArguments(this.declaration.typeParameters, typeArgumentNodes, contextualTypeArguments, alternativeReportNode);
|
||||
if (!resolvedTypeArguments)
|
||||
return null;
|
||||
} else {
|
||||
// TODO: check typeArgumentNodes being empty
|
||||
resolvedTypeArguments = [];
|
||||
}
|
||||
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
|
||||
}
|
||||
}
|
||||
|
||||
/** A resolved class. */
|
||||
|
Loading…
x
Reference in New Issue
Block a user