Initial function calls

This commit is contained in:
dcodeIO 2017-11-20 23:39:50 +01:00
parent d3d4938b68
commit dc74dd118d
5 changed files with 141 additions and 41 deletions

View File

@ -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);
}
}

View File

@ -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");
}

View File

@ -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 "";

View File

@ -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

View File

@ -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. */