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

View File

@ -12,6 +12,10 @@
}, },
"dependencies": { "dependencies": {
"@types/node": "^8.0.28", "@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; tokenizer: Tokenizer | null = null;
isEntry: bool; isEntry: bool;
constructor(path: string, text: string, isEntry: bool) { constructor(path: string, text: string, isEntry: bool = false) {
super(); super();
this.path = path; this.path = path;
this.normalizedPath = normalizePath(path, true); this.normalizedPath = normalizePath(path, true);
@ -1392,6 +1392,7 @@ export class ExpressionStatement extends Statement {
serialize(sb: string[]): void { serialize(sb: string[]): void {
this.expression.serialize(sb); this.expression.serialize(sb);
sb.push(";");
} }
} }
@ -1773,6 +1774,7 @@ export class ThrowStatement extends Statement {
serialize(sb: string[]): void { serialize(sb: string[]): void {
sb.push("throw "); sb.push("throw ");
this.expression.serialize(sb); 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 { 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); path = path.replace("/", PATH_DELIMITER);
if (PARENT_SUBST != "..") if (PARENT_SUBST != "..")
path = path.replace("..", PARENT_SUBST); path = path.replace("..", PARENT_SUBST); */
return path; 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 BinaryenIndex = u32;
declare type BinaryenType = u32; declare type BinaryenType = i32;
declare function _BinaryenNone(): BinaryenType; declare function _BinaryenNone(): BinaryenType;
declare function _BinaryenInt32(): BinaryenType; declare function _BinaryenInt32(): BinaryenType;
@ -24,6 +24,37 @@ declare function _BinaryenFloat32(): BinaryenType;
declare function _BinaryenFloat64(): BinaryenType; declare function _BinaryenFloat64(): BinaryenType;
declare function _BinaryenUndefined(): 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 type BinaryenModuleRef = usize;
declare function _BinaryenModuleCreate(): BinaryenModuleRef; declare function _BinaryenModuleCreate(): BinaryenModuleRef;
@ -34,10 +65,11 @@ declare type CString = usize;
declare type CArray<T> = usize; declare type CArray<T> = usize;
declare function _BinaryenAddFunctionType(module: BinaryenModuleRef, name: CString, result: BinaryenType, paramTypes: CArray<BinaryenType>, numParams: BinaryenIndex): BinaryenFunctionTypeRef; 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>; 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 (?) // union value starts at offset 8 due to alignment (?)
declare function _BinaryenLiteralInt32(out: BinaryenLiteral, x: i32): void; declare function _BinaryenLiteralInt32(out: BinaryenLiteral, x: i32): void;
declare function _BinaryenLiteralInt64(out: BinaryenLiteral, x: i32, y: 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 _BinaryenLeFloat64(): BinaryenOp;
declare function _BinaryenGtFloat64(): BinaryenOp; declare function _BinaryenGtFloat64(): BinaryenOp;
declare function _BinaryenGeFloat64(): BinaryenOp; declare function _BinaryenGeFloat64(): BinaryenOp;
declare function _BinaryenPageSize(): BinaryenOp;
declare function _BinaryenCurrentMemory(): BinaryenOp; declare type BinaryenHostOp = BinaryenOp;
declare function _BinaryenGrowMemory(): BinaryenOp;
declare function _BinaryenHasFeature(): 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; 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 _BinaryenHost(module: BinaryenModuleRef, op: BinaryenOp, name: CString | 0, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex): BinaryenExpressionRef;
declare function _BinaryenNop(module: BinaryenModuleRef): BinaryenExpressionRef; declare function _BinaryenNop(module: BinaryenModuleRef): BinaryenExpressionRef;
declare function _BinaryenUnreachable(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 _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; declare type BinaryenFunctionRef = usize;
@ -219,7 +274,9 @@ declare type BinaryenExportRef = usize;
declare function _BinaryenAddExport(module: BinaryenModuleRef, internalName: CString, externalName: CString): BinaryenExportRef; declare function _BinaryenAddExport(module: BinaryenModuleRef, internalName: CString, externalName: CString): BinaryenExportRef;
declare function _BinaryenRemoveExport(module: BinaryenModuleRef, externalName: CString): void; 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; 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 _BinaryenModulePrintAsmjs(module: BinaryenModuleRef): void;
declare function _BinaryenModuleValidate(module: BinaryenModuleRef): i32; declare function _BinaryenModuleValidate(module: BinaryenModuleRef): i32;
declare function _BinaryenModuleOptimize(module: BinaryenModuleRef): void; declare function _BinaryenModuleOptimize(module: BinaryenModuleRef): void;
declare function _BinaryenModuleRunPasses(module: BinaryenModuleRef, passes: string[]): void;
declare function _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void; declare function _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void;
declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize; declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize;
declare function _BinaryenModuleRead(input: CString, inputSize: usize): BinaryenModuleRef; 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 _RelooperRenderAndDispose(relooper: RelooperRef, entry: RelooperBlockRef, labelHelper: BinaryenIndex, module: BinaryenModuleRef): BinaryenExpressionRef;
declare function _BinaryenSetAPITracing(on: i32): void; 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"; import { Target } from "./compiler";
export enum Type { export enum Type {
@ -10,6 +10,37 @@ export enum Type {
Undefined = _BinaryenUndefined() 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 { export enum UnaryOp {
ClzI32 = _BinaryenClzInt32(), ClzI32 = _BinaryenClzInt32(),
CtzI32 = _BinaryenCtzInt32(), CtzI32 = _BinaryenCtzInt32(),
@ -146,13 +177,13 @@ export enum HostOp {
HasFeature = _BinaryenHasFeature() HasFeature = _BinaryenHasFeature()
} }
export enum AtomicRMWOp { // TODO: not yet part of the C-API export enum AtomicRMWOp {
Add, Add = _BinaryenAtomicRMWAdd(),
Sub, Sub = _BinaryenAtomicRMWSub(),
And, And = _BinaryenAtomicRMWAnd(),
Or, Or = _BinaryenAtomicRMWOr(),
Xor, Xor = _BinaryenAtomicRMWXor(),
Xchg Xchg = _BinaryenAtomicRMWXchg()
} }
export class MemorySegment { export class MemorySegment {
@ -216,8 +247,8 @@ export class Module {
try { try {
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length); return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length);
} finally { } finally {
_free(cStr);
_free(cArr); _free(cArr);
_free(cStr);
} }
} }
@ -274,8 +305,8 @@ export class Module {
try { try {
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<BinaryenExpressionRef[]>operands).length : 0); return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<BinaryenExpressionRef[]>operands).length : 0);
} finally { } finally {
_free(cStr);
_free(cArr); _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 // statements
createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef { createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
@ -323,8 +374,8 @@ export class Module {
try { try {
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type); return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
} finally { } finally {
_free(cStr);
_free(cArr); _free(cArr);
_free(cStr);
} }
} }
@ -383,9 +434,9 @@ export class Module {
try { try {
return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value); return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value);
} finally { } finally {
for (i = 0; i < k; ++i) _free(strs[i]);
_free(cArr);
_free(cStr); _free(cStr);
_free(cArr);
for (i = k - 1; i >= 0; --i) _free(strs[i]);
} }
} }
@ -416,7 +467,7 @@ export class Module {
// meta // 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; if (this.noEmit) return 0;
const cStr: CString = allocString(name); const cStr: CString = allocString(name);
try { try {
@ -433,8 +484,8 @@ export class Module {
try { try {
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body); return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body);
} finally { } finally {
_free(cStr);
_free(cArr); _free(cArr);
_free(cStr);
} }
} }
@ -445,8 +496,8 @@ export class Module {
try { try {
return _BinaryenAddExport(this.ref, cStr1, cStr2); return _BinaryenAddExport(this.ref, cStr1, cStr2);
} finally { } finally {
_free(cStr1);
_free(cStr2); _free(cStr2);
_free(cStr1);
} }
} }
@ -468,9 +519,9 @@ export class Module {
try { try {
return _BinaryenAddImport(this.ref, cStr1, cStr2, cStr3, type); return _BinaryenAddImport(this.ref, cStr1, cStr2, cStr3, type);
} finally { } finally {
_free(cStr1);
_free(cStr2);
_free(cStr3); _free(cStr3);
_free(cStr2);
_free(cStr1);
} }
} }
@ -506,11 +557,11 @@ export class Module {
try { try {
_BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k); _BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k);
} finally { } finally {
_free(cStr);
for (i = 0; i < k; ++i) _free(segs[i]);
_free(cArr1);
_free(cArr2);
_free(cArr3); _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 { export class Relooper {
module: Module; module: Module;
@ -596,7 +686,7 @@ export class Relooper {
} }
// helpers // 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> { function allocU8Array(u8s: Uint8Array | null): CArray<u8> {
if (!u8s) return 0; if (!u8s) return 0;

View File

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

View File

@ -2,7 +2,7 @@
export const PATH_DELIMITER: string = "/"; export const PATH_DELIMITER: string = "/";
export const PARENT_SUBST: string = ".."; export const PARENT_SUBST: string = "..";
export const GETTER_PREFIX: string = "get:"; export const GETTER_PREFIX: string = "get ";
export const SETTER_PREFIX: string = "set:"; export const SETTER_PREFIX: string = "set ";
export const INSTANCE_DELIMITER: string = "#"; export const INSTANCE_DELIMITER: string = "#";
export const STATIC_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_generic = 2315,
Type_0_is_not_assignable_to_type_1 = 2322, Type_0_is_not_assignable_to_type_1 = 2322,
_this_cannot_be_referenced_in_current_location = 2332, _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_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, 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, Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
Duplicate_function_implementation = 2393, 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, The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
Expected_0_type_arguments_but_got_1 = 2558, Expected_0_type_arguments_but_got_1 = 2558,
File_0_not_found = 6054 File_0_not_found = 6054
@ -112,10 +114,12 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2315: return "Type '{0}' is not generic."; case 2315: return "Type '{0}' is not generic.";
case 2322: return "Type '{0}' is not assignable to type '{1}'."; case 2322: return "Type '{0}' is not assignable to type '{1}'.";
case 2332: return "'this' cannot be referenced in current location."; 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 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 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 2391: return "Function implementation is missing or not immediately following the declaration.";
case 2393: return "Duplicate function implementation."; 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 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 2558: return "Expected {0} type arguments, but got {1}.";
case 6054: return "File '{0}' not found."; case 6054: return "File '{0}' not found.";

View File

@ -51,10 +51,12 @@
"Type '{0}' is not generic.": 2315, "Type '{0}' is not generic.": 2315,
"Type '{0}' is not assignable to type '{1}'.": 2322, "Type '{0}' is not assignable to type '{1}'.": 2322,
"'this' cannot be referenced in current location.": 2332, "'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 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, "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, "Function implementation is missing or not immediately following the declaration.": 2391,
"Duplicate function implementation.": 2393, "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, "The target of an assignment must be a variable or a property access.": 2541,
"Expected {0} type arguments, but got {1}.": 2558, "Expected {0} type arguments, but got {1}.": 2558,

View File

@ -11,10 +11,12 @@ export enum DiagnosticCategory {
} }
export function diagnosticCategoryToString(category: DiagnosticCategory): string { export function diagnosticCategoryToString(category: DiagnosticCategory): string {
if (category == DiagnosticCategory.INFO) return "INFO"; switch (category) {
if (category == DiagnosticCategory.WARNING) return "WARNING"; case DiagnosticCategory.INFO: return "INFO";
if (category == DiagnosticCategory.ERROR) return "ERROR"; case DiagnosticCategory.WARNING: return "WARNING";
return ""; case DiagnosticCategory.ERROR: return "ERROR";
default: return "";
}
} }
const colorBlue: string = "\u001b[93m"; const colorBlue: string = "\u001b[93m";
@ -23,10 +25,12 @@ const colorRed: string = "\u001b[91m";
const colorReset: string = "\u001b[0m"; const colorReset: string = "\u001b[0m";
export function diagnosticCategoryToColor(category: DiagnosticCategory): string { export function diagnosticCategoryToColor(category: DiagnosticCategory): string {
if (category == DiagnosticCategory.INFO) return colorBlue; switch (category) {
if (category == DiagnosticCategory.WARNING) return colorYellow; case DiagnosticCategory.INFO: return colorBlue;
if (category == DiagnosticCategory.ERROR) return colorRed; case DiagnosticCategory.WARNING: return colorYellow;
return ""; case DiagnosticCategory.ERROR: return colorRed;
default: return "";
}
} }
export class DiagnosticMessage { 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 { private initializeClass(declaration: ClassDeclaration): void {
const internalName: string = declaration.internalName; 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); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else return;
this.elements.set(internalName, template); }
const prototype: ClassPrototype = new ClassPrototype(this, internalName, declaration);
this.elements.set(internalName, prototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName)) 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 else
this.exports.set(internalName, template); this.exports.set(internalName, prototype);
} }
const memberDeclarations: DeclarationStatement[] = declaration.members; const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
const memberDeclaration: DeclarationStatement = memberDeclarations[j]; const memberDeclaration: DeclarationStatement = memberDeclarations[j];
switch (memberDeclaration.kind) { if (memberDeclaration.kind == NodeKind.FIELD)
this.initializeField(<FieldDeclaration>memberDeclaration, prototype);
case NodeKind.FIELD: else if (memberDeclaration.kind == NodeKind.METHOD)
this.initializeField(<FieldDeclaration>memberDeclaration, template); this.initializeMethod(<MethodDeclaration>memberDeclaration, prototype);
break; else
throw new Error("unexpected class member");
case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>memberDeclaration, template);
break;
default:
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; const name: string = declaration.identifier.name;
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { if (classPrototype.members.has(name)) {
const internalName: string = declaration.internalName; this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, declaration.internalName);
if (template.members.has(name)) return;
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); }
else { const internalName: string = declaration.internalName;
const global: Global = new Global(this, internalName, declaration, null); if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { // static fields become globals
template.members.set(name, global); const global: Global = new Global(this, internalName, declaration, null);
} classPrototype.members.set(name, global);
} else { } else {
if (template.fieldDeclarations.has(name)) const field: FieldPrototype = new FieldPrototype(classPrototype, internalName, declaration);
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); classPrototype.members.set(name, field);
else
template.fieldDeclarations.set(name, declaration);
} }
} }
private initializeMethod(declaration: MethodDeclaration, template: ClassTemplate): void { private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void {
let name: string = declaration.identifier.name; let name: string = declaration.identifier.name;
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) { const internalName: string = declaration.internalName;
const internalName: string = declaration.internalName; if (classPrototype.members.has(name)) {
if (template.members.has(name)) this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); return;
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 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 { private initializeEnum(declaration: EnumDeclaration): void {
const internalName: string = declaration.internalName; 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); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else { return;
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);
} }
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 { private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void {
const name: string = declaration.identifier.name; const name: string = declaration.identifier.name;
const internalName: string = declaration.internalName; 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); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else { return;
const value: EnumValue = new EnumValue(enm, this, internalName, declaration);
enm.members.set(name, value);
} }
const value: EnumValue = new EnumValue(enm, this, internalName, declaration);
enm.members.set(name, value);
} }
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void { 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 { private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map<string,QueuedExport>): void {
const exportName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name; const exportName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
if (queuedExports.has(exportName)) if (queuedExports.has(exportName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName); this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, member.externalIdentifier.range, exportName);
else { 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);
} }
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 { private initializeFunction(declaration: FunctionDeclaration): void {
const internalName: string = declaration.internalName; const internalName: string = declaration.internalName;
const template: FunctionTemplate = new FunctionTemplate(this, internalName, declaration); const prototype: FunctionPrototype = new FunctionPrototype(this, internalName, declaration, null);
if (this.elements.has(internalName)) if (this.elements.has(internalName)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else { return;
this.elements.set(internalName, template); }
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { this.elements.set(internalName, prototype);
if (this.exports.has(internalName)) if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); if (this.exports.has(internalName))
else this.error(DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, declaration.identifier.range, internalName);
this.exports.set(internalName, template); else
} this.exports.set(internalName, prototype);
} }
} }
@ -386,16 +371,16 @@ export class Program extends DiagnosticEmitter {
private initializeInterface(declaration: InterfaceDeclaration): void { private initializeInterface(declaration: InterfaceDeclaration): void {
const internalName: string = declaration.internalName; 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)) if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else else
this.elements.set(internalName, template); this.elements.set(internalName, interfacePrototype);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName)) 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 else
this.exports.set(internalName, template); this.exports.set(internalName, interfacePrototype);
} }
const memberDeclarations: DeclarationStatement[] = declaration.members; const memberDeclarations: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) { for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
@ -403,11 +388,11 @@ export class Program extends DiagnosticEmitter {
switch (memberDeclaration.kind) { switch (memberDeclaration.kind) {
case NodeKind.FIELD: case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>memberDeclaration, template); this.initializeField(<FieldDeclaration>memberDeclaration, interfacePrototype);
break; break;
case NodeKind.METHOD: case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>memberDeclaration, template); this.initializeMethod(<MethodDeclaration>memberDeclaration, interfacePrototype);
break; break;
default: default:
@ -418,16 +403,16 @@ export class Program extends DiagnosticEmitter {
private initializeNamespace(declaration: NamespaceDeclaration): void { private initializeNamespace(declaration: NamespaceDeclaration): void {
const internalName: string = declaration.internalName; 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)) if (this.elements.has(internalName))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
else { else {
this.elements.set(internalName, ns); this.elements.set(internalName, namespace);
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) { if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
if (this.exports.has(internalName)) 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 else
this.exports.set(internalName, ns); this.exports.set(internalName, namespace);
} }
} }
const members: Statement[] = declaration.members; const members: Statement[] = declaration.members;
@ -544,12 +529,12 @@ export class Program extends DiagnosticEmitter {
return typeArguments; return typeArguments;
} }
resolveElement(expression: Expression, contextualFunction: FunctionInstance): Element | null { resolveElement(expression: Expression, contextualFunction: Function): Element | null {
// this // this
if (expression.kind == NodeKind.THIS) { if (expression.kind == NodeKind.THIS) {
if (contextualFunction.instanceMethodOf) if (contextualFunction.instanceMethodOf)
return contextualFunction.instanceMethodOf.template; return contextualFunction.instanceMethodOf;
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range); this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
return null; return null;
} }
@ -566,22 +551,31 @@ export class Program extends DiagnosticEmitter {
const programGlobalElement: Element | null = <Element | null>this.elements.get(name); const programGlobalElement: Element | null = <Element | null>this.elements.get(name);
if (programGlobalElement) if (programGlobalElement)
return programGlobalElement; return programGlobalElement;
this.error(DiagnosticCode.Cannot_find_name_0, expression.range, name);
return null; return null;
// static or instance property (incl. enum values) or method // static or instance property (incl. enum values) or method
} else if (expression.kind == NodeKind.PROPERTYACCESS) { } 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) if (!target)
return null; return null;
switch (target.kind) { const propertyName: string = (<PropertyAccessExpression>expression).property.name;
case ElementKind.CLASS: if (target.kind == ElementKind.ENUM)
case ElementKind.ENUM: member = <EnumValue | null>(<Enum>target).members.get(propertyName);
case ElementKind.NAMESPACE: else if (target.kind == ElementKind.CLASS_PROTOTYPE)
} member = <Element | null>(<ClassPrototype>target).members.get(propertyName);
// TODO 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 { export enum ElementKind {
CLASS_PROTOTYPE,
CLASS, CLASS,
CLASSINSTANCE,
ENUM, ENUM,
ENUMVALUE, ENUMVALUE,
FIELD_PROTOTYPE,
FIELD,
FUNCTION_PROTOTYPE,
FUNCTION, FUNCTION,
FUNCTIONINSTANCE,
GLOBAL, GLOBAL,
INTERFACE_PROTOTYPE,
INTERFACE, INTERFACE,
INTERFACEINSTANCE,
LOCAL, LOCAL,
NAMESPACE NAMESPACE
} }
/** Base class of all program elements. */
export abstract class Element { export abstract class Element {
kind: ElementKind; kind: ElementKind;
program: Program; program: Program;
internalName: string; internalName: string;
globalExportName: string | null = null; globalExportName: string | null = null;
compiled: bool = false;
constructor(program: Program, internalName: string) { constructor(program: Program, internalName: string) {
this.program = program; this.program = program;
@ -629,59 +627,7 @@ export abstract class Element {
} }
} }
export class Enum extends Element { /** A namespace. Also the base class of other namespace-like program elements. */
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; }
}
export class Namespace extends Element { export class Namespace extends Element {
kind = ElementKind.NAMESPACE; 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; } 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 { export class Parameter {
name: string; name: string;
@ -708,6 +709,7 @@ export class Parameter {
} }
} }
/** A function local. */
export class Local extends Element { export class Local extends Element {
kind = ElementKind.LOCAL; 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; declaration: FunctionDeclaration | null;
instances: Map<string,FunctionInstance>; classPrototype: ClassPrototype | null;
instances: Map<string,Function> = new Map();
isGeneric: bool; 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); super(program, internalName);
this.declaration = declaration; this.declaration = declaration;
this.instances = new Map(); this.classPrototype = classPrototype;
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins set this 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 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 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 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; } 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, "", ""); 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) if (instance)
return instance; return instance;
const declaration: FunctionDeclaration | null = this.declaration; const declaration: FunctionDeclaration | null = this.declaration;
@ -793,31 +798,31 @@ export class FunctionTemplate extends Element {
let internalName: string = this.internalName; let internalName: string = this.internalName;
if (instanceKey.length) if (instanceKey.length)
internalName += "<" + instanceKey + ">"; 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); this.instances.set(instanceKey, instance);
return instance; return instance;
} }
} }
export class FunctionInstance extends Element { /** A resolved function. */
export class Function extends Element {
kind = ElementKind.FUNCTIONINSTANCE; kind = ElementKind.FUNCTION;
template: FunctionTemplate; template: FunctionPrototype;
typeArguments: Type[]; typeArguments: Type[];
parameters: Parameter[]; parameters: Parameter[];
returnType: Type; returnType: Type;
instanceMethodOf: ClassInstance | null; instanceMethodOf: Class | null;
locals: Map<string,Local> = new Map(); locals: Map<string,Local> = new Map();
additionalLocals: Type[] = []; additionalLocals: Type[] = []; // without parameters
breakContext: string | null = null; breakContext: string | null = null;
compiled: bool = false;
contextualTypeArguments: Map<string,Type> = new Map(); contextualTypeArguments: Map<string,Type> = new Map();
private breakMajor: i32 = 0; private breakMajor: i32 = 0;
private breakMinor: 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); super(template.program, internalName);
this.template = template; this.template = template;
this.typeArguments = typeArguments; 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; declaration: ClassDeclaration | null;
fieldDeclarations: Map<string,FieldDeclaration>; instances: Map<string,Class>;
methodDeclarations: Map<string,MethodDeclaration>;
instances: Map<string,ClassInstance>;
isGeneric: bool; isGeneric: bool;
constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) { constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) {
super(program, internalName, null); super(program, internalName, null);
this.declaration = declaration; this.declaration = declaration;
this.fieldDeclarations = new Map();
this.methodDeclarations = new Map();
this.instances = new Map(); this.instances = new Map();
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins can set this 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 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 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, "", ""); 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) if (instance)
return instance; return instance;
if (!this.declaration) 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; kind = ElementKind.CLASS;
template: ClassTemplate; declaration: ClassDeclaration | null;
template: ClassPrototype;
typeArguments: Type[]; typeArguments: Type[];
base: ClassInstance | null; base: Class | null;
type: Type; type: Type;
compiled: bool = false;
contextualTypeArguments: Map<string,Type> = new Map(); contextualTypeArguments: Map<string,Type> = new Map();
constructor(template: ClassTemplate, internalName: string, typeArguments: Type[], base: ClassInstance | null) { constructor(template: ClassPrototype, internalName: string, typeArguments: Type[], base: Class | null) {
super(template.program, internalName); super(template.program, internalName, template.declaration);
this.template = template; this.template = template;
this.typeArguments = typeArguments; this.typeArguments = typeArguments;
this.base = base; 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; declaration: InterfaceDeclaration | null;
constructor(program: Program, internalName: string, 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; kind = ElementKind.INTERFACE;
base: InterfaceInstance | null; 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); super(template, internalName, typeArguments, base);
} }
} }

View File

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

View File

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

View File

@ -1,7 +1,4 @@
export { CharCode, isLineBreak} from "./util/charcode"; export { CharCode, isLineBreak} from "./util/charcode";
export { I64, U64 } from "./util/i64"; export { I64, U64 } from "./util/i64";
export { normalize as normalizePath, resolve as resolvePath, dirname } from "./util/path"; 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 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; return -1;
} }
import { sub } from "../other";
export function what(): void {
sub(1,2);
}
`], `],
["../other", ["../other",
@ -54,6 +58,7 @@ do {
const program = parser.finish(); const program = parser.finish();
const compiler = new Compiler(program); const compiler = new Compiler(program);
const module = compiler.compile(); const module = compiler.compile();
console.log(program.elements.keys());
// module.optimize(); // module.optimize();
module.validate(); module.validate();

View File

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