This commit is contained in:
dcodeIO 2017-11-17 14:33:51 +01:00
parent d1c1178f25
commit d3d4938b68
17 changed files with 580 additions and 303 deletions

6
package-lock.json generated
View File

@ -41,9 +41,9 @@
"dev": true
},
"binaryen": {
"version": "37.0.0-nightly.20170912",
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-37.0.0-nightly.20170912.tgz",
"integrity": "sha512-yXLgHkUvTMqEV1vkixAaldnS4w6WOrqY+30Cx9k+JjBzmA6wJTQr0F32xFg/4MPr4NRZOdP/AnI8ais4nhfgIw=="
"version": "39.0.0-nightly.20171116",
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-39.0.0-nightly.20171116.tgz",
"integrity": "sha512-ljl/qPne0+8hYtNWITRSAtxNM1EG5NnvTg+HRmSUdNAK2j9wcyAAg5uVj+TgipEqY82kmHt5C9+TSQNEwaxgrw=="
},
"chalk": {
"version": "2.1.0",

View File

@ -12,6 +12,10 @@
},
"dependencies": {
"@types/node": "^8.0.28",
"binaryen": "37.0.0-nightly.20170912"
"binaryen": "39.0.0-nightly.20171116"
},
"scripts": {
"build": "tsc -P src",
"test:compiler": "ts-node -P src tests/compiler"
}
}

View File

@ -1091,7 +1091,7 @@ export class Source extends Node {
tokenizer: Tokenizer | null = null;
isEntry: bool;
constructor(path: string, text: string, isEntry: bool) {
constructor(path: string, text: string, isEntry: bool = false) {
super();
this.path = path;
this.normalizedPath = normalizePath(path, true);
@ -1392,6 +1392,7 @@ export class ExpressionStatement extends Statement {
serialize(sb: string[]): void {
this.expression.serialize(sb);
sb.push(";");
}
}
@ -1773,6 +1774,7 @@ export class ThrowStatement extends Statement {
serialize(sb: string[]): void {
sb.push("throw ");
this.expression.serialize(sb);
sb.push(";");
}
}
@ -1895,10 +1897,11 @@ export function serialize(node: Node, indent: i32 = 0): string {
}
export function mangleInternalPath(path: string): string {
if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH)
// TODO: not necessary with current config
/* if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH)
path = path.replace("/", PATH_DELIMITER);
if (PARENT_SUBST != "..")
path = path.replace("..", PARENT_SUBST);
path = path.replace("..", PARENT_SUBST); */
return path;
}

74
src/binaryen.d.ts vendored
View File

@ -15,7 +15,7 @@ declare function _free(ptr: usize): void;
declare type BinaryenIndex = u32;
declare type BinaryenType = u32;
declare type BinaryenType = i32;
declare function _BinaryenNone(): BinaryenType;
declare function _BinaryenInt32(): BinaryenType;
@ -24,6 +24,37 @@ declare function _BinaryenFloat32(): BinaryenType;
declare function _BinaryenFloat64(): BinaryenType;
declare function _BinaryenUndefined(): BinaryenType;
declare type BinaryenExpressionId = i32;
declare function _BinaryenInvalidId(): BinaryenExpressionId;
declare function _BinaryenBlockId(): BinaryenExpressionId;
declare function _BinaryenIfId(): BinaryenExpressionId;
declare function _BinaryenLoopId(): BinaryenExpressionId;
declare function _BinaryenBreakId(): BinaryenExpressionId;
declare function _BinaryenSwitchId(): BinaryenExpressionId;
declare function _BinaryenCallId(): BinaryenExpressionId;
declare function _BinaryenCallImportId(): BinaryenExpressionId;
declare function _BinaryenCallIndirectId(): BinaryenExpressionId;
declare function _BinaryenGetLocalId(): BinaryenExpressionId;
declare function _BinaryenSetLocalId(): BinaryenExpressionId;
declare function _BinaryenGetGlobalId(): BinaryenExpressionId;
declare function _BinaryenSetGlobalId(): BinaryenExpressionId;
declare function _BinaryenLoadId(): BinaryenExpressionId;
declare function _BinaryenStoreId(): BinaryenExpressionId;
declare function _BinaryenConstId(): BinaryenExpressionId;
declare function _BinaryenUnaryId(): BinaryenExpressionId;
declare function _BinaryenBinaryId(): BinaryenExpressionId;
declare function _BinaryenSelectId(): BinaryenExpressionId;
declare function _BinaryenDropId(): BinaryenExpressionId;
declare function _BinaryenReturnId(): BinaryenExpressionId;
declare function _BinaryenHostId(): BinaryenExpressionId;
declare function _BinaryenNopId(): BinaryenExpressionId;
declare function _BinaryenUnreachableId(): BinaryenExpressionId;
declare function _BinaryenAtomicCmpxchgId(): BinaryenExpressionId;
declare function _BinaryenAtomicRMWId(): BinaryenExpressionId;
declare function _BinaryenAtomicWaitId(): BinaryenExpressionId;
declare function _BinaryenAtomicWakeId(): BinaryenExpressionId;
declare type BinaryenModuleRef = usize;
declare function _BinaryenModuleCreate(): BinaryenModuleRef;
@ -34,10 +65,11 @@ declare type CString = usize;
declare type CArray<T> = usize;
declare function _BinaryenAddFunctionType(module: BinaryenModuleRef, name: CString, result: BinaryenType, paramTypes: CArray<BinaryenType>, numParams: BinaryenIndex): BinaryenFunctionTypeRef;
declare function _BinaryenGetFunctionTypeBySignature(module: BinaryenModuleRef, result: BinaryenType, paramTypes: CArray<BinaryenType>, numParams: BinaryenIndex): BinaryenFunctionTypeRef;
declare type BinaryenLiteral = CArray<u8>;
// LLVM C ABI with `out` being a buffer of 16 bytes receiving the BinaryenLiteral struct
// LLVM C ABI with `out` being a buffer of 16 bytes receiving the BinaryenLiteral struct.
// union value starts at offset 8 due to alignment (?)
declare function _BinaryenLiteralInt32(out: BinaryenLiteral, x: i32): void;
declare function _BinaryenLiteralInt64(out: BinaryenLiteral, x: i32, y: i32): void;
@ -171,10 +203,22 @@ declare function _BinaryenLtFloat64(): BinaryenOp;
declare function _BinaryenLeFloat64(): BinaryenOp;
declare function _BinaryenGtFloat64(): BinaryenOp;
declare function _BinaryenGeFloat64(): BinaryenOp;
declare function _BinaryenPageSize(): BinaryenOp;
declare function _BinaryenCurrentMemory(): BinaryenOp;
declare function _BinaryenGrowMemory(): BinaryenOp;
declare function _BinaryenHasFeature(): BinaryenOp;
declare type BinaryenHostOp = BinaryenOp;
declare function _BinaryenPageSize(): BinaryenHostOp;
declare function _BinaryenCurrentMemory(): BinaryenHostOp;
declare function _BinaryenGrowMemory(): BinaryenHostOp;
declare function _BinaryenHasFeature(): BinaryenHostOp;
declare type BinaryenAtomicRMWOp = BinaryenOp;
declare function _BinaryenAtomicRMWAdd(): BinaryenAtomicRMWOp;
declare function _BinaryenAtomicRMWSub(): BinaryenAtomicRMWOp;
declare function _BinaryenAtomicRMWAnd(): BinaryenAtomicRMWOp;
declare function _BinaryenAtomicRMWOr(): BinaryenAtomicRMWOp;
declare function _BinaryenAtomicRMWXor(): BinaryenAtomicRMWOp;
declare function _BinaryenAtomicRMWXchg(): BinaryenAtomicRMWOp;
declare type BinaryenExpressionRef = usize;
@ -202,8 +246,19 @@ declare function _BinaryenReturn(module: BinaryenModuleRef, value: BinaryenExpre
declare function _BinaryenHost(module: BinaryenModuleRef, op: BinaryenOp, name: CString | 0, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex): BinaryenExpressionRef;
declare function _BinaryenNop(module: BinaryenModuleRef): BinaryenExpressionRef;
declare function _BinaryenUnreachable(module: BinaryenModuleRef): BinaryenExpressionRef;
declare function _BinaryenAtomicRMW(module: BinaryenModuleRef, op: BinaryenAtomicRMWOp, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenAtomicCmpxchg(module: BinaryenModuleRef, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, replacement: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenAtomicWait(module: BinaryenModuleRef, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, timeout: BinaryenExpressionRef, expectedType: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenAtomicWake(module: BinaryenModuleRef, ptr: BinaryenExpressionRef, wakeCount: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenExpressionGetId(expr: BinaryenExpressionRef): BinaryenExpressionId;
declare function _BinaryenExpressionGetType(expr: BinaryenExpressionRef): BinaryenType;
declare function _BinaryenExpressionPrint(expr: BinaryenExpressionRef): void;
declare function _BinaryenConstGetValueI32(expr: BinaryenExpressionRef): i32;
declare function _BinaryenConstGetValueI64Low(expr: BinaryenExpressionRef): i32;
declare function _BinaryenConstGetValueI64High(expr: BinaryenExpressionRef): i32;
declare function _BinaryenConstGetValueF32(expr: BinaryenExpressionRef): f32;
declare function _BinaryenConstGetValueF64(expr: BinaryenExpressionRef): f64;
declare type BinaryenFunctionRef = usize;
@ -219,7 +274,9 @@ declare type BinaryenExportRef = usize;
declare function _BinaryenAddExport(module: BinaryenModuleRef, internalName: CString, externalName: CString): BinaryenExportRef;
declare function _BinaryenRemoveExport(module: BinaryenModuleRef, externalName: CString): void;
declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenImportRef; // sic
declare type BinaryenGlobalRef = usize;
declare function _BinaryenAddGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType, mutable: i8, init: BinaryenExpressionRef): BinaryenGlobalRef;
declare function _BinaryenSetFunctionTable(module: BinaryenModuleRef, funcs: CArray<BinaryenFunctionRef>, numFuncs: BinaryenIndex): void;
@ -232,6 +289,7 @@ declare function _BinaryenModulePrint(module: BinaryenModuleRef): void;
declare function _BinaryenModulePrintAsmjs(module: BinaryenModuleRef): void;
declare function _BinaryenModuleValidate(module: BinaryenModuleRef): i32;
declare function _BinaryenModuleOptimize(module: BinaryenModuleRef): void;
declare function _BinaryenModuleRunPasses(module: BinaryenModuleRef, passes: string[]): void;
declare function _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void;
declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize;
declare function _BinaryenModuleRead(input: CString, inputSize: usize): BinaryenModuleRef;
@ -248,5 +306,3 @@ declare function _RelooperAddBranchForSwitch(from: RelooperBlockRef, to: Reloope
declare function _RelooperRenderAndDispose(relooper: RelooperRef, entry: RelooperBlockRef, labelHelper: BinaryenIndex, module: BinaryenModuleRef): BinaryenExpressionRef;
declare function _BinaryenSetAPITracing(on: i32): void;
declare function _BinaryenGetFunctionTypeBySignature(module: BinaryenModuleRef, result: BinaryenType, paramTypes: CArray<BinaryenType>, numParams: BinaryenIndex): BinaryenFunctionTypeRef;

View File

@ -1,4 +1,4 @@
import { U64 } from "./util";
import { I64, U64 } from "./util";
import { Target } from "./compiler";
export enum Type {
@ -10,6 +10,37 @@ export enum Type {
Undefined = _BinaryenUndefined()
}
export enum ExpressionId {
Invalid = _BinaryenInvalidId(),
Block = _BinaryenBlockId(),
If = _BinaryenIfId(),
Loop = _BinaryenLoopId(),
Break = _BinaryenBreakId(),
Switch = _BinaryenSwitchId(),
Call = _BinaryenCallId(),
CallImport = _BinaryenCallImportId(),
CallIndirect = _BinaryenCallIndirectId(),
GetLocal = _BinaryenGetLocalId(),
SetLocal = _BinaryenSetLocalId(),
GetGlobal = _BinaryenGetGlobalId(),
SetGlobal = _BinaryenSetGlobalId(),
Load = _BinaryenLoadId(),
Store = _BinaryenStoreId(),
Const = _BinaryenConstId(),
Unary = _BinaryenUnaryId(),
Binary = _BinaryenBinaryId(),
Select = _BinaryenSelectId(),
Drop = _BinaryenDropId(),
Return = _BinaryenReturnId(),
Host = _BinaryenHostId(),
Nop = _BinaryenNopId(),
Unreachable = _BinaryenUnreachableId(),
AtomicCmpxchg = _BinaryenAtomicCmpxchgId(),
AtomicRMW = _BinaryenAtomicRMWId(),
AtomicWait = _BinaryenAtomicWaitId(),
AtomicWake = _BinaryenAtomicWakeId()
}
export enum UnaryOp {
ClzI32 = _BinaryenClzInt32(),
CtzI32 = _BinaryenCtzInt32(),
@ -146,13 +177,13 @@ export enum HostOp {
HasFeature = _BinaryenHasFeature()
}
export enum AtomicRMWOp { // TODO: not yet part of the C-API
Add,
Sub,
And,
Or,
Xor,
Xchg
export enum AtomicRMWOp {
Add = _BinaryenAtomicRMWAdd(),
Sub = _BinaryenAtomicRMWSub(),
And = _BinaryenAtomicRMWAnd(),
Or = _BinaryenAtomicRMWOr(),
Xor = _BinaryenAtomicRMWXor(),
Xchg = _BinaryenAtomicRMWXchg()
}
export class MemorySegment {
@ -216,8 +247,8 @@ export class Module {
try {
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length);
} finally {
_free(cStr);
_free(cArr);
_free(cStr);
}
}
@ -274,8 +305,8 @@ export class Module {
try {
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<BinaryenExpressionRef[]>operands).length : 0);
} finally {
_free(cStr);
_free(cArr);
_free(cStr);
}
}
@ -299,6 +330,26 @@ export class Module {
}
}
createAtomicRMW(op: AtomicRMWOp, bytes: i32, offset: i32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: Type): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenAtomicRMW(this.ref, op, bytes, offset, ptr, value, type);
}
createAtomicCmpxchg(bytes: i32, offset: i32, ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, replacement: BinaryenExpressionRef, type: Type): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenAtomicCmpxchg(this.ref, bytes, offset, ptr, expected, replacement, type);
}
createAtomicWait(ptr: BinaryenExpressionRef, expected: BinaryenExpressionRef, timeout: BinaryenExpressionRef, expectedType: BinaryenType): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenAtomicWait(this.ref, ptr, expected, timeout, expectedType);
}
createAtomicWake(ptr: BinaryenExpressionRef, wakeCount: BinaryenExpressionRef): BinaryenExpressionRef {
if (this.noEmit) return 0;
return _BinaryenAtomicWake(this.ref, ptr, wakeCount);
}
// statements
createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
@ -323,8 +374,8 @@ export class Module {
try {
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
} finally {
_free(cStr);
_free(cArr);
_free(cStr);
}
}
@ -383,9 +434,9 @@ export class Module {
try {
return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value);
} finally {
for (i = 0; i < k; ++i) _free(strs[i]);
_free(cArr);
_free(cStr);
_free(cArr);
for (i = k - 1; i >= 0; --i) _free(strs[i]);
}
}
@ -416,7 +467,7 @@ export class Module {
// meta
addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenImportRef {
addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenGlobalRef {
if (this.noEmit) return 0;
const cStr: CString = allocString(name);
try {
@ -433,8 +484,8 @@ export class Module {
try {
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body);
} finally {
_free(cStr);
_free(cArr);
_free(cStr);
}
}
@ -445,8 +496,8 @@ export class Module {
try {
return _BinaryenAddExport(this.ref, cStr1, cStr2);
} finally {
_free(cStr1);
_free(cStr2);
_free(cStr1);
}
}
@ -468,9 +519,9 @@ export class Module {
try {
return _BinaryenAddImport(this.ref, cStr1, cStr2, cStr3, type);
} finally {
_free(cStr1);
_free(cStr2);
_free(cStr3);
_free(cStr2);
_free(cStr1);
}
}
@ -506,11 +557,11 @@ export class Module {
try {
_BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k);
} finally {
_free(cStr);
for (i = 0; i < k; ++i) _free(segs[i]);
_free(cArr1);
_free(cArr2);
_free(cArr3);
_free(cArr2);
_free(cArr1);
for (i = k - 1; i >= 0; --i) _free(segs[i]);
_free(cStr);
}
}
@ -540,6 +591,45 @@ export class Module {
}
}
export function getExpressionId(expr: BinaryenExpressionRef): ExpressionId {
return _BinaryenExpressionGetId(expr);
}
export function getExpressionType(expr: BinaryenExpressionRef): Type {
return _BinaryenExpressionGetType(expr);
}
export function printExpression(expr: BinaryenExpressionRef): void {
return _BinaryenExpressionPrint(expr);
}
export function getConstValueI32(expr: BinaryenExpressionRef): i32 {
return _BinaryenConstGetValueI32(expr);
}
export function getConstValueI64Low(expr: BinaryenExpressionRef): i32 {
return _BinaryenConstGetValueI64Low(expr);
}
export function getConstValueI64High(expr: BinaryenExpressionRef): i32 {
return _BinaryenConstGetValueI64High(expr);
}
export function getConstValueI64(expr: BinaryenExpressionRef): I64 {
return new I64(
_BinaryenConstGetValueI64Low(expr),
_BinaryenConstGetValueI64High(expr)
);
}
export function getConstValueF32(expr: BinaryenExpressionRef): f32 {
return _BinaryenConstGetValueF32(expr);
}
export function getConstValueF64(expr: BinaryenExpressionRef): f64 {
return _BinaryenConstGetValueF64(expr);
}
export class Relooper {
module: Module;
@ -596,7 +686,7 @@ export class Relooper {
}
// helpers
// TODO: investigate stack allocation?
// can't do stack allocation here: STACKTOP is a global in WASM but a hidden variable in asm.js
function allocU8Array(u8s: Uint8Array | null): CArray<u8> {
if (!u8s) return 0;

View File

@ -1,7 +1,7 @@
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, ClassTemplate, Element, ElementKind, Enum, FunctionTemplate, FunctionInstance, Global, Local, Namespace, Parameter } from "./program";
import { Program, ClassPrototype, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program";
import { CharCode, I64, U64, normalizePath, sb } from "./util";
import { Token } from "./tokenizer";
import {
@ -96,11 +96,11 @@ export class Compiler extends DiagnosticEmitter {
options: Options;
module: Module;
startFunction: FunctionInstance;
startFunction: Function;
startFunctionBody: BinaryenExpressionRef[] = new Array();
currentType: Type = Type.void;
currentFunction: FunctionInstance;
currentFunction: Function;
disallowContinue: bool = true;
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
@ -118,8 +118,8 @@ export class Compiler extends DiagnosticEmitter {
this.program = program;
this.options = options ? options : new Options();
this.module = this.options.noEmit ? Module.createStub() : Module.create();
const startFunctionTemplate: FunctionTemplate = new FunctionTemplate(program, "start", null);
const startFunctionInstance: FunctionInstance = new FunctionInstance(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null);
const startFunctionTemplate: FunctionPrototype = new FunctionPrototype(program, "start", null);
const startFunctionInstance: Function = new Function(startFunctionTemplate, startFunctionTemplate.internalName, [], [], Type.void, null);
this.currentFunction = this.startFunction = startFunctionInstance;
this.memoryOffset = new U64(2 * (this.options.target == Target.WASM64 ? 8 : 4), 0); // leave space for `null` and heapStart (both of usize type)
}
@ -251,7 +251,7 @@ export class Compiler extends DiagnosticEmitter {
// otherwise a top-level statement that is part of the start function's body
default: {
const previousFunction: FunctionInstance = this.currentFunction;
const previousFunction: Function = this.currentFunction;
this.currentFunction = this.startFunction;
this.startFunctionBody.push(this.compileStatement(statement));
this.currentFunction = previousFunction;
@ -290,7 +290,7 @@ export class Compiler extends DiagnosticEmitter {
let initializeInStart: bool;
if (element.hasConstantValue) {
if (type.isLongInteger)
initializer = this.module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi);
initializer = element.constantIntegerValue ? this.module.createI64(element.constantIntegerValue.lo, element.constantIntegerValue.hi) : this.module.createI64(0, 0);
else if (type.kind == TypeKind.F32)
initializer = this.module.createF32(element.constantFloatValue);
else if (type.kind == TypeKind.F64)
@ -298,11 +298,11 @@ export class Compiler extends DiagnosticEmitter {
else if (type.isSmallInteger) {
if (type.isSignedInteger) {
const shift: i32 = type.smallIntegerShift;
initializer = this.module.createI32(element.constantIntegerValue.toI32() << shift >> shift);
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() << shift >> shift : 0);
} else
initializer = this.module.createI32(element.constantIntegerValue.toI32() & type.smallIntegerMask);
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() & type.smallIntegerMask: 0);
} else
initializer = this.module.createI32(element.constantIntegerValue.toI32());
initializer = this.module.createI32(element.constantIntegerValue ? element.constantIntegerValue.toI32() : 0);
initializeInStart = false;
this.module.addGlobal(element.internalName, binaryenType, element.isMutable, initializer);
} else if (declaration) {
@ -374,16 +374,16 @@ export class Compiler extends DiagnosticEmitter {
compileFunctionDeclaration(declaration: FunctionDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): void {
const internalName: string = declaration.internalName;
const element: Element | null = <Element | null>this.program.elements.get(internalName);
if (!element || element.kind != ElementKind.FUNCTION)
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(<FunctionTemplate>element, resolvedTypeArguments, contextualTypeArguments);
this.compileFunction(<FunctionPrototype>element, resolvedTypeArguments, contextualTypeArguments);
}
compileFunction(template: FunctionTemplate, typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null = null): void {
const instance: FunctionInstance | null = template.instantiate(typeArguments, contextualTypeArguments);
compileFunction(template: FunctionPrototype, typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null = null): void {
const instance: Function | null = template.resolve(typeArguments, contextualTypeArguments);
if (!instance || instance.compiled)
return;
@ -398,7 +398,7 @@ export class Compiler extends DiagnosticEmitter {
instance.compiled = true;
// compile statements
const previousFunction: FunctionInstance = this.currentFunction;
const previousFunction: Function = this.currentFunction;
this.currentFunction = instance;
const stmts: BinaryenExpressionRef[] = this.compileStatements(<Statement[]>declaration.statements);
this.currentFunction = previousFunction;
@ -468,18 +468,18 @@ export class Compiler extends DiagnosticEmitter {
for (let [name, element] of ns.members) {
switch (element.kind) {
case ElementKind.CLASS:
if ((noTreeShaking || (<ClassTemplate>element).isExport) && !(<ClassTemplate>element).isGeneric)
this.compileClass(<ClassTemplate>element, []);
case ElementKind.CLASS_PROTOTYPE:
if ((noTreeShaking || (<ClassPrototype>element).isExport) && !(<ClassPrototype>element).isGeneric)
this.compileClass(<ClassPrototype>element, []);
break;
case ElementKind.ENUM:
this.compileEnum(<Enum>element);
break;
case ElementKind.FUNCTION:
if ((noTreeShaking || (<FunctionTemplate>element).isExport) && !(<FunctionTemplate>element).isGeneric)
this.compileFunction(<FunctionTemplate>element, []);
case ElementKind.FUNCTION_PROTOTYPE:
if ((noTreeShaking || (<FunctionPrototype>element).isExport) && !(<FunctionPrototype>element).isGeneric)
this.compileFunction(<FunctionPrototype>element, []);
break;
case ElementKind.GLOBAL:
@ -506,18 +506,18 @@ export class Compiler extends DiagnosticEmitter {
throw new Error("unexpected missing element");
switch (element.kind) {
case ElementKind.CLASS:
if (!(<ClassTemplate>element).isGeneric)
this.compileClass(<ClassTemplate>element, []);
case ElementKind.CLASS_PROTOTYPE:
if (!(<ClassPrototype>element).isGeneric)
this.compileClass(<ClassPrototype>element, []);
break;
case ElementKind.ENUM:
this.compileEnum(<Enum>element);
break;
case ElementKind.FUNCTION:
if (!(<FunctionTemplate>element).isGeneric)
this.compileFunction(<FunctionTemplate>element, []);
case ElementKind.FUNCTION_PROTOTYPE:
if (!(<FunctionPrototype>element).isGeneric)
this.compileFunction(<FunctionPrototype>element, []);
break;
case ElementKind.GLOBAL:
@ -536,15 +536,15 @@ export class Compiler extends DiagnosticEmitter {
compileClassDeclaration(declaration: ClassDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): void {
const internalName: string = declaration.internalName;
const element: Element | null = <Element | null>this.program.elements.get(internalName);
if (!element || element.kind != ElementKind.CLASS)
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(<ClassTemplate>element, resolvedTypeArguments, contextualTypeArguments);
this.compileClass(<ClassPrototype>element, resolvedTypeArguments, contextualTypeArguments);
}
compileClass(cls: ClassTemplate, typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null = null) {
compileClass(cls: ClassPrototype, typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null = null) {
throw new Error("not implemented");
}
@ -1053,9 +1053,9 @@ export class Compiler extends DiagnosticEmitter {
}
compileAssertionExpression(expression: AssertionExpression, contextualType: Type): BinaryenExpressionRef {
const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments, true); // reports
const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments); // reports
if (toType && toType != contextualType) {
const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, <Type>toType);
const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, <Type>toType, false);
return this.convertExpression(expr, this.currentType, <Type>toType);
}
return this.compileExpression(expression.expression, contextualType);
@ -1316,9 +1316,13 @@ export class Compiler extends DiagnosticEmitter {
}
compileCallExpression(expression: CallExpression, contextualType: Type): BinaryenExpressionRef {
const element: Element | null = this.program.resolveElement(expression, this.currentFunction);
if (!element || element.kind != ElementKind.FUNCTION)
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.'
return this.module.createUnreachable();
}
throw new Error("not implemented");
}
@ -1401,7 +1405,7 @@ export class Compiler extends DiagnosticEmitter {
// throw new Error("not implemented");
// getter
if (element.kind == ElementKind.FUNCTION && (<FunctionTemplate>element).isGetter)
if (element.kind == ElementKind.FUNCTION_PROTOTYPE && (<FunctionPrototype>element).isGetter)
throw new Error("not implemented");
this.error(DiagnosticCode.Operation_not_supported, expression.range);

View File

@ -2,7 +2,7 @@
export const PATH_DELIMITER: string = "/";
export const PARENT_SUBST: string = "..";
export const GETTER_PREFIX: string = "get:";
export const SETTER_PREFIX: string = "set:";
export const GETTER_PREFIX: string = "get ";
export const SETTER_PREFIX: string = "set ";
export const INSTANCE_DELIMITER: string = "#";
export const STATIC_DELIMITER: string = ".";

View File

@ -51,10 +51,12 @@ export enum DiagnosticCode {
Type_0_is_not_generic = 2315,
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,
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_type_arguments_but_got_1 = 2558,
File_0_not_found = 6054
@ -112,10 +114,12 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2315: return "Type '{0}' is not generic.";
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 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 2558: return "Expected {0} type arguments, but got {1}.";
case 6054: return "File '{0}' not found.";

View File

@ -51,10 +51,12 @@
"Type '{0}' is not generic.": 2315,
"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,
"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} type arguments, but got {1}.": 2558,

View File

@ -11,10 +11,12 @@ export enum DiagnosticCategory {
}
export function diagnosticCategoryToString(category: DiagnosticCategory): string {
if (category == DiagnosticCategory.INFO) return "INFO";
if (category == DiagnosticCategory.WARNING) return "WARNING";
if (category == DiagnosticCategory.ERROR) return "ERROR";
return "";
switch (category) {
case DiagnosticCategory.INFO: return "INFO";
case DiagnosticCategory.WARNING: return "WARNING";
case DiagnosticCategory.ERROR: return "ERROR";
default: return "";
}
}
const colorBlue: string = "\u001b[93m";
@ -23,10 +25,12 @@ const colorRed: string = "\u001b[91m";
const colorReset: string = "\u001b[0m";
export function diagnosticCategoryToColor(category: DiagnosticCategory): string {
if (category == DiagnosticCategory.INFO) return colorBlue;
if (category == DiagnosticCategory.WARNING) return colorYellow;
if (category == DiagnosticCategory.ERROR) return colorRed;
return "";
switch (category) {
case DiagnosticCategory.INFO: return colorBlue;
case DiagnosticCategory.WARNING: return colorYellow;
case DiagnosticCategory.ERROR: return colorRed;
default: return "";
}
}
export class DiagnosticMessage {

68
src/evaluator.ts Normal file
View File

@ -0,0 +1,68 @@
// TODO: not yet decided whether we'll need this
// https://github.com/WebAssembly/binaryen/pull/1294
// https://github.com/WebAssembly/binaryen/pull/1295
import { Type } from "./types";
import { I64 } from "./util";
import {
NodeKind,
Expression,
BinaryExpression,
LiteralExpression,
UnaryExpression,
UnaryPostfixExpression,
UnaryPrefixExpression
} from "./ast";
export class Evaluator {
success: bool = false;
type: Type;
integerValue: I64;
floatValue: f64;
stringValue: string;
constructor(initialType: Type) {
this.type = initialType;
}
evaluate(expression: Expression): this {
switch (expression.kind) {
case NodeKind.BINARY:
this.evaluateBinary(<BinaryExpression>expression);
break;
case NodeKind.LITERAL:
this.evaluateLiteral(<LiteralExpression>expression);
break;
case NodeKind.UNARYPREFIX:
this.evaluateUnaryPrefix(<UnaryPrefixExpression>expression);
break;
}
return this;
}
private evaluateBinary(expression: BinaryExpression): this {
// TODO
return this;
}
private evaluateLiteral(expression: LiteralExpression): this {
// TODO
return this;
}
private evaluateUnaryPrefix(expression: UnaryPrefixExpression): this {
// TODO
return this;
}
}
export function evaluate(expression: Expression, contextualType: Type): Evaluator {
return new Evaluator(contextualType).evaluate(expression);
}

View File

@ -201,113 +201,98 @@ export class Program extends DiagnosticEmitter {
private initializeClass(declaration: ClassDeclaration): void {
const internalName: string = declaration.internalName;
const template: ClassTemplate = new ClassTemplate(this, internalName, declaration);
if (this.elements.has(internalName))
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.elements.set(internalName, template);
return;
}
const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration);
this.elements.set(internalName, prototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, template);
this.exports.set(internalName, prototype);
}
const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
const memberDeclaration: DeclarationStatement = memberDeclarations[j];
switch (memberDeclaration.kind) {
case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>memberDeclaration, template);
break;
case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>memberDeclaration, template);
break;
default:
throw new Error("unexpected class member");
}
if (memberDeclaration.kind == NodeKind.FIELD)
this.initializeField(<FieldDeclaration>memberDeclaration, prototype);
else if (memberDeclaration.kind == NodeKind.METHOD)
this.initializeMethod(<MethodDeclaration>memberDeclaration, prototype);
else
throw new Error("unexpected class member");
}
}
private initializeField(declaration: FieldDeclaration, template: ClassTemplate): void {
private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void {
const name: string = declaration.identifier.name;
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
const internalName: string = declaration.internalName;
if (template.members.has(name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
const global: Global = new Global(this, internalName, declaration, null);
template.members.set(name, global);
}
if (classPrototype.members.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName);
return;
}
const internalName: string = declaration.internalName;
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { // static fields become globals
const global: Global = new Global(this, internalName, declaration, null);
classPrototype.members.set(name, global);
} else {
if (template.fieldDeclarations.has(name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
else
template.fieldDeclarations.set(name, declaration);
const field: FieldPrototype = new FieldPrototype(classPrototype, internalName, declaration);
classPrototype.members.set(name, field);
}
}
private initializeMethod(declaration: MethodDeclaration, template: ClassTemplate): void {
private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void {
let name: string = declaration.identifier.name;
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
const internalName: string = declaration.internalName;
if (template.members.has(name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
const func: FunctionTemplate = new FunctionTemplate(this, internalName, declaration);
let modifiers: Modifier[] | null;
if (modifiers = declaration.modifiers) {
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) {
const modifier: Modifier = modifiers[i];
if (modifier.modifierKind == ModifierKind.GET) {
name = GETTER_PREFIX + name;
break;
} else if (modifier.modifierKind == ModifierKind.SET) {
name = SETTER_PREFIX + name;
break;
}
}
}
template.members.set(name, func);
}
} else {
if (template.methodDeclarations.has(name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
else
template.methodDeclarations.set(name, declaration);
const internalName: string = declaration.internalName;
if (classPrototype.members.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
return;
}
const func: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, hasModifier(ModifierKind.STATIC, declaration.modifiers) ? null : classPrototype);
let modifiers: Modifier[] | null;
if (modifiers = declaration.modifiers) {
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) {
const modifier: Modifier = modifiers[i];
if (modifier.modifierKind == ModifierKind.GET) {
name = GETTER_PREFIX + name;
break;
} else if (modifier.modifierKind == ModifierKind.SET) {
name = SETTER_PREFIX + name;
break;
}
}
}
classPrototype.members.set(name, func);
}
private initializeEnum(declaration: EnumDeclaration): void {
const internalName: string = declaration.internalName;
const enm: Enum = new Enum(this, internalName, declaration);
if (this.elements.has(internalName))
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
this.elements.set(internalName, enm);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, enm);
}
const members: EnumValueDeclaration[] = declaration.members;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
this.initializeEnumValue(members[i], enm);
return;
}
const enm: Enum = new Enum(this, internalName, declaration);
this.elements.set(internalName, enm);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, enm);
}
const values: EnumValueDeclaration[] = declaration.members;
for (let i: i32 = 0, k: i32 = values.length; i < k; ++i)
this.initializeEnumValue(values[i], enm);
}
private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void {
const name: string = declaration.identifier.name;
const internalName: string = declaration.internalName;
if (enm.members.has(name))
if (enm.members.has(name)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
const value: EnumValue = new EnumValue(enm, this, internalName, declaration);
enm.members.set(name, value);
return;
}
const value: EnumValue = new EnumValue(enm, this, internalName, declaration);
enm.members.set(name, value);
}
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void {
@ -318,35 +303,35 @@ export class Program extends DiagnosticEmitter {
private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map<string,QueuedExport>): void {
const exportName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
if (queuedExports.has(exportName))
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName);
else {
const queuedExport: QueuedExport = new QueuedExport();
if (internalPath == null) {
queuedExport.isForeign = false;
queuedExport.referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name;
} else {
queuedExport.isForeign = true;
queuedExport.referencedName = (<string>internalPath) + PATH_DELIMITER + member.identifier.name;
}
queuedExport.member = member;
queuedExports.set(exportName, queuedExport);
if (queuedExports.has(exportName)) {
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, exportName);
return;
}
const queuedExport: QueuedExport = new QueuedExport();
if (internalPath == null) {
queuedExport.isForeign = false;
queuedExport.referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name;
} else {
queuedExport.isForeign = true;
queuedExport.referencedName = (<string>internalPath) + PATH_DELIMITER + member.identifier.name;
}
queuedExport.member = member;
queuedExports.set(exportName, queuedExport);
}
private initializeFunction(declaration: FunctionDeclaration): void {
const internalName: string = declaration.internalName;
const template: FunctionTemplate = new FunctionTemplate(this, internalName, declaration);
if (this.elements.has(internalName))
const prototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null);
if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
this.elements.set(internalName, template);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, template);
}
return;
}
this.elements.set(internalName, prototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, prototype);
}
}
@ -386,16 +371,16 @@ export class Program extends DiagnosticEmitter {
private initializeInterface(declaration: InterfaceDeclaration): void {
const internalName: string = declaration.internalName;
const template: InterfaceTemplate = new InterfaceTemplate(this, internalName, declaration);
const interfacePrototype: InterfacePrototype = new InterfacePrototype(this, internalName, declaration);
if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else
this.elements.set(internalName, template);
this.elements.set(internalName, interfacePrototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, template);
this.exports.set(internalName, interfacePrototype);
}
const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
@ -403,11 +388,11 @@ export class Program extends DiagnosticEmitter {
switch (memberDeclaration.kind) {
case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>memberDeclaration, template);
this.initializeField(<FieldDeclaration>memberDeclaration, interfacePrototype);
break;
case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>memberDeclaration, template);
this.initializeMethod(<MethodDeclaration>memberDeclaration, interfacePrototype);
break;
default:
@ -418,16 +403,16 @@ export class Program extends DiagnosticEmitter {
private initializeNamespace(declaration: NamespaceDeclaration): void {
const internalName: string = declaration.internalName;
const ns: Namespace = new Namespace(this, internalName, declaration);
const namespace: Namespace = new Namespace(this, internalName, declaration);
if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else {
this.elements.set(internalName, ns);
this.elements.set(internalName, namespace);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
else
this.exports.set(internalName, ns);
this.exports.set(internalName, namespace);
}
}
const members: Statement[] = declaration.members;
@ -544,12 +529,12 @@ export class Program extends DiagnosticEmitter {
return typeArguments;
}
resolveElement(expression: Expression, contextualFunction: FunctionInstance): Element | null {
resolveElement(expression: Expression, contextualFunction: Function): Element | null {
// this
if (expression.kind == NodeKind.THIS) {
if (contextualFunction.instanceMethodOf)
return contextualFunction.instanceMethodOf.template;
return contextualFunction.instanceMethodOf;
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
return null;
}
@ -566,22 +551,31 @@ export class Program extends DiagnosticEmitter {
const programGlobalElement: Element | null = <Element | null>this.elements.get(name);
if (programGlobalElement)
return programGlobalElement;
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, name);
return null;
// 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);
const target: Element | null = this.resolveElement((<PropertyAccessExpression>expression).expression, contextualFunction); // reports
let member: Element | null = null;
if (!target)
return null;
switch (target.kind) {
case ElementKind.CLASS:
case ElementKind.ENUM:
case ElementKind.NAMESPACE:
}
// TODO
const propertyName: string = (<PropertyAccessExpression>expression).property.name;
if (target.kind == ElementKind.ENUM)
member = <EnumValue | null>(<Enum>target).members.get(propertyName);
else if (target.kind == ElementKind.CLASS_PROTOTYPE)
member = <Element | null>(<ClassPrototype>target).members.get(propertyName);
else if (target.kind == ElementKind.CLASS)
member = <Element | null>(<Class>target).members.get(propertyName);
else if (target.kind == ElementKind.NAMESPACE)
member = <Element | null>(<Namespace>target).members.get(propertyName);
if (member)
return member;
this.error(DiagnosticCode.Property_0_does_not_exist_on_type_1, expression.range, (<PropertyAccessExpression>expression).property.name, target.internalName);
return null;
}
return null;
throw new Error("not implemented: " + expression.kind);
}
}
@ -603,25 +597,29 @@ function checkGlobalDecorator(decorators: DecoratorStatement[]): string | null {
}
export enum ElementKind {
CLASS_PROTOTYPE,
CLASS,
CLASSINSTANCE,
ENUM,
ENUMVALUE,
FIELD_PROTOTYPE,
FIELD,
FUNCTION_PROTOTYPE,
FUNCTION,
FUNCTIONINSTANCE,
GLOBAL,
INTERFACE_PROTOTYPE,
INTERFACE,
INTERFACEINSTANCE,
LOCAL,
NAMESPACE
}
/** Base class of all program elements. */
export abstract class Element {
kind: ElementKind;
program: Program;
internalName: string;
globalExportName: string | null = null;
compiled: bool = false;
constructor(program: Program, internalName: string) {
this.program = program;
@ -629,59 +627,7 @@ export abstract class Element {
}
}
export class Enum extends Element {
kind = ElementKind.ENUM;
declaration: EnumDeclaration | null;
members: Map<string,EnumValue> = new Map();
compiled: bool = false;
constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) {
super(program, internalName);
this.declaration = declaration;
}
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
get isGlobalExport(): bool { return this.globalExportName != null; }
get isConstant(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are const */ true; }
}
export class EnumValue extends Element {
kind = ElementKind.ENUMVALUE;
declaration: EnumValueDeclaration | null;
enum: Enum;
hasConstantValue: bool = false;
constantValue: i32 = 0;
constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) {
super(program, internalName);
this.enum = enm;
if (!(this.declaration = declaration)) this.hasConstantValue = true;
}
}
export class Global extends Element {
kind = ElementKind.GLOBAL;
declaration: VariableLikeDeclarationStatement | null;
type: Type | null;
hasConstantValue: bool = false;
constantIntegerValue: I64 = new I64(0, 0);
constantFloatValue: f64 = 0;
compiled: bool = false;
constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) {
super(program, internalName);
if (!(this.declaration = declaration)) this.hasConstantValue = true;
this.type = type; // resolved later if `null`
}
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
get isGlobalExport(): bool { return this.globalExportName != null; }
get isMutable(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; }
}
/** A namespace. Also the base class of other namespace-like program elements. */
export class Namespace extends Element {
kind = ElementKind.NAMESPACE;
@ -697,6 +643,61 @@ export class Namespace extends Element {
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't exports */ false; }
}
/** An enum. */
export class Enum extends Namespace {
kind = ElementKind.ENUM;
declaration: EnumDeclaration | null;
members: Map<string,EnumValue> = new Map(); // more specific
constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) {
super(program, internalName, null);
this.declaration = declaration;
}
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
get isGlobalExport(): bool { return this.globalExportName != null; }
get isConstant(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are const */ true; }
}
/** An enum value. */
export class EnumValue extends Element {
kind = ElementKind.ENUMVALUE;
declaration: EnumValueDeclaration | null;
enum: Enum;
hasConstantValue: bool;
constantValue: i32 = 0;
constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) {
super(program, internalName);
this.enum = enm;
if (!(this.declaration = declaration)) this.hasConstantValue = true;
}
}
/** A global variable. */
export class Global extends Element {
kind = ElementKind.GLOBAL;
declaration: VariableLikeDeclarationStatement | null;
type: Type | null;
hasConstantValue: bool = false;
constantIntegerValue: I64 | null = null;
constantFloatValue: f64 = 0;
constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) {
super(program, internalName);
if (!(this.declaration = declaration)) this.hasConstantValue = true;
this.type = type; // resolved later if `null`, also updates constantKind
}
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
get isGlobalExport(): bool { return this.globalExportName != null; }
get isMutable(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; }
}
/** A function parameter. */
export class Parameter {
name: string;
@ -708,6 +709,7 @@ export class Parameter {
}
}
/** A function local. */
export class Local extends Element {
kind = ElementKind.LOCAL;
@ -721,28 +723,31 @@ export class Local extends Element {
}
}
export class FunctionTemplate extends Element {
/** A yet unresolved function prototype. */
export class FunctionPrototype extends Element {
kind = ElementKind.FUNCTION;
kind = ElementKind.FUNCTION_PROTOTYPE;
declaration: FunctionDeclaration | null;
instances: Map<string,FunctionInstance>;
classPrototype: ClassPrototype | null;
instances: Map<string,Function> = new Map();
isGeneric: bool;
constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null) {
constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null, classPrototype: ClassPrototype | null = null) {
super(program, internalName);
this.declaration = declaration;
this.instances = new Map();
this.classPrototype = classPrototype;
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins set this
}
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; }
get isInstance(): bool { return this.classPrototype != null; }
get isGetter(): bool { return this.declaration ? hasModifier(ModifierKind.GET, this.declaration.modifiers) : /* internals aren't getters */ false; }
get isSetter(): bool { return this.declaration ? hasModifier(ModifierKind.SET, this.declaration.modifiers) : /* internals aren't setters */ false; }
instantiate(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): FunctionInstance | null {
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Function | null {
const instanceKey: string = typesToString(typeArguments, "", "");
let instance: FunctionInstance | null = <FunctionInstance | null>this.instances.get(instanceKey);
let instance: Function | null = <Function | null>this.instances.get(instanceKey);
if (instance)
return instance;
const declaration: FunctionDeclaration | null = this.declaration;
@ -793,31 +798,31 @@ export class FunctionTemplate extends Element {
let internalName: string = this.internalName;
if (instanceKey.length)
internalName += "<" + instanceKey + ">";
instance = new FunctionInstance(this, internalName, typeArguments, parameters, returnType, null); // TODO: class
instance = new Function(this, internalName, typeArguments, parameters, returnType, null); // TODO: class
this.instances.set(instanceKey, instance);
return instance;
}
}
export class FunctionInstance extends Element {
/** A resolved function. */
export class Function extends Element {
kind = ElementKind.FUNCTIONINSTANCE;
template: FunctionTemplate;
kind = ElementKind.FUNCTION;
template: FunctionPrototype;
typeArguments: Type[];
parameters: Parameter[];
returnType: Type;
instanceMethodOf: ClassInstance | null;
instanceMethodOf: Class | null;
locals: Map<string,Local> = new Map();
additionalLocals: Type[] = [];
additionalLocals: Type[] = []; // without parameters
breakContext: string | null = null;
compiled: bool = false;
contextualTypeArguments: Map<string,Type> = new Map();
private breakMajor: i32 = 0;
private breakMinor: i32 = 0;
constructor(template: FunctionTemplate, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: ClassInstance | null) {
constructor(template: FunctionPrototype, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: Class | null) {
super(template.program, internalName);
this.template = template;
this.typeArguments = typeArguments;
@ -866,20 +871,50 @@ export class FunctionInstance extends Element {
}
}
export class ClassTemplate extends Namespace {
/** A yet unresolved instance field prototype. */
export class FieldPrototype extends Element {
kind = ElementKind.CLASS;
kind = ElementKind.FIELD_PROTOTYPE;
declaration: FieldDeclaration | null;
classPrototype: ClassPrototype;
constructor(classPrototype: ClassPrototype, internalName: string, declaration: FieldDeclaration | null) {
super(classPrototype.program, internalName);
this.classPrototype = classPrototype;
}
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; }
}
/** A resolved instance field. */
export class Field extends Element {
kind = ElementKind.FIELD;
template: FieldPrototype;
type: Type;
hasConstantValue: bool = false;
constantIntegerValue: I64 | null = null;
constantFloatValue: f64 = 0;
constructor(template: FieldPrototype, internalName: string, type: Type) {
super(template.program, internalName);
if (!this.template.declaration) this.hasConstantValue = true;
this.type = type;
}
}
/** A yet unresolved class prototype. */
export class ClassPrototype extends Namespace {
kind = ElementKind.CLASS_PROTOTYPE;
declaration: ClassDeclaration | null;
fieldDeclarations: Map<string,FieldDeclaration>;
methodDeclarations: Map<string,MethodDeclaration>;
instances: Map<string,ClassInstance>;
instances: Map<string,Class>;
isGeneric: bool;
constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) {
super(program, internalName, null);
this.declaration = declaration;
this.fieldDeclarations = new Map();
this.methodDeclarations = new Map();
this.instances = new Map();
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins can set this
}
@ -887,9 +922,9 @@ export class ClassTemplate extends Namespace {
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; }
instantiate(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): ClassInstance {
resolve(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): Class {
const key: string = typesToString(typeArguments, "", "");
let instance: ClassInstance | null = <ClassInstance | null>this.instances.get(key);
let instance: Class | null = <Class | null>this.instances.get(key);
if (instance)
return instance;
if (!this.declaration)
@ -898,19 +933,20 @@ export class ClassTemplate extends Namespace {
}
}
export class ClassInstance extends Element {
/** A resolved class. */
export class Class extends Namespace {
kind = ElementKind.CLASSINSTANCE;
template: ClassTemplate;
kind = ElementKind.CLASS;
declaration: ClassDeclaration | null;
template: ClassPrototype;
typeArguments: Type[];
base: ClassInstance | null;
base: Class | null;
type: Type;
compiled: bool = false;
contextualTypeArguments: Map<string,Type> = new Map();
constructor(template: ClassTemplate, internalName: string, typeArguments: Type[], base: ClassInstance | null) {
super(template.program, internalName);
constructor(template: ClassPrototype, internalName: string, typeArguments: Type[], base: Class | null) {
super(template.program, internalName, template.declaration);
this.template = template;
this.typeArguments = typeArguments;
this.base = base;
@ -933,9 +969,10 @@ export class ClassInstance extends Element {
}
}
export class InterfaceTemplate extends ClassTemplate {
/** A yet unresvoled interface. */
export class InterfacePrototype extends ClassPrototype {
kind = ElementKind.INTERFACE;
kind = ElementKind.INTERFACE_PROTOTYPE;
declaration: InterfaceDeclaration | null;
constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null) {
@ -943,12 +980,14 @@ export class InterfaceTemplate extends ClassTemplate {
}
}
export class InterfaceInstance extends ClassInstance {
/** A resolved interface. */
export class Interface extends Class {
kind = ElementKind.INTERFACEINSTANCE;
base: InterfaceInstance | null;
kind = ElementKind.INTERFACE;
template: InterfacePrototype;
base: Interface | null;
constructor(template: InterfaceTemplate, internalName: string, typeArguments: Type[], base: InterfaceInstance | null) {
constructor(template: InterfacePrototype, internalName: string, typeArguments: Type[], base: Interface | null) {
super(template, internalName, typeArguments, base);
}
}

View File

@ -25,6 +25,7 @@
"compiler.ts",
"diagnosticMessages.generated.ts",
"diagnostics.ts",
"evaluator.ts",
"glue/js.d.ts",
"glue/js.ts",
"index.ts",

View File

@ -1,4 +1,4 @@
import { ClassInstance } from "./program";
import { Class } from "./program";
import { sb } from "./util";
export const enum TypeKind {
@ -29,7 +29,7 @@ export class Type {
kind: TypeKind;
size: i32;
classType: ClassInstance | null;
classType: Class | null;
nullable: bool = false;
nullableType: Type | null = null; // cached, of this type
@ -50,7 +50,7 @@ export class Type {
get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; }
get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; }
asClass(classType: ClassInstance): Type {
asClass(classType: Class): Type {
const ret: Type = new Type(this.kind, this.size);
ret.classType = classType;
return ret;

View File

@ -1,7 +1,4 @@
export { CharCode, isLineBreak} from "./util/charcode";
export { I64, U64 } from "./util/i64";
export { normalize as normalizePath, resolve as resolvePath, dirname } from "./util/path";
export const sb: string[] = new Array(256); // shared string builder. 64-bit without growing: (4+4+8) + 8*256 = 16b + 2kb

View File

@ -30,6 +30,10 @@ const files: Map<string,string> = new Map([
}
return -1;
}
import { sub } from "../other";
export function what(): void {
sub(1,2);
}
`],
["../other",
@ -54,6 +58,7 @@ do {
const program = parser.finish();
const compiler = new Compiler(program);
const module = compiler.compile();
console.log(program.elements.keys());
// module.optimize();
module.validate();

View File

@ -1,6 +1,6 @@
import "../src/glue/js";
import { Tokenizer, Token } from "../src/tokenizer";
import { Source } from "../src/reflection";
import { Source } from "../src/ast";
import * as fs from "fs";
const text = fs.readFileSync(__dirname + "/../src/tokenizer.ts").toString();