Initial commit

This commit is contained in:
dcodeIO 2017-09-28 13:08:25 +02:00
commit 1d53303b47
32 changed files with 8460 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules/
npm-debug.*
out/

25
README.md Normal file
View File

@ -0,0 +1,25 @@
AssemblyScript NEXT
===================
This repository contains compiler component prototypes for the next iteration of the AssemblyScript compiler written in AssemblyScript itself.
Note that the code uses some features and standard library components that are not yet supported by any version of asc. To account for this, the code has been written in "portable AssemblyScript", a TypeScript-compatible subset of a subset of a superset of JavaScript, that also compiles to JavaScript using TSC.
Why is this necessary?
----------------------
Well, it isn't, but: In order to be able to compile the AssemblyScript compiler itself to WebAssembly eventually, we cannot depend on TypeScript because it is written in vanilla TypeScript and makes use of quite a few non-AOT-compatible dynamic features of JavaScript.
Cons:
- A lot of work
- Dealing with TypeScript compatibility issues
Pros:
- One day compiling to WebAssembly for performance
- Necessary features only, reducing binary size
- Linking against Binaryen compiled to WebAssembly, reducing overhead
Side effects:
- Good fire test for the compiler
- Good benchmark when comparing both versions
- Benefits standard library design ideas

219
package-lock.json generated Normal file
View File

@ -0,0 +1,219 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"@types/chalk": {
"version": "0.4.31",
"resolved": "https://registry.npmjs.org/@types/chalk/-/chalk-0.4.31.tgz",
"integrity": "sha1-ox10JBprHtu5c8822XooloNKUfk=",
"dev": true
},
"@types/diff": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/diff/-/diff-3.2.2.tgz",
"integrity": "sha512-q3zfJvaTroV5BjAAR+peTHEGAAhGrPX0z2EzCzpt2mwFA+qzUn2nigJLqSekXRtdULKmT8am7zjvTMZSapIgHw==",
"dev": true
},
"@types/long": {
"version": "3.0.32",
"resolved": "https://registry.npmjs.org/@types/long/-/long-3.0.32.tgz",
"integrity": "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==",
"dev": true
},
"@types/node": {
"version": "8.0.28",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz",
"integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ=="
},
"ansi-styles": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
"dev": true,
"requires": {
"color-convert": "1.9.0"
}
},
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"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=="
},
"chalk": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz",
"integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==",
"dev": true,
"requires": {
"ansi-styles": "3.2.0",
"escape-string-regexp": "1.0.5",
"supports-color": "4.4.0"
}
},
"color-convert": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
"integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"diff": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
"integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"has-flag": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
"dev": true
},
"long": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
"dev": true
},
"make-error": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.0.tgz",
"integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=",
"dev": true
},
"minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
"dev": true
},
"source-map-support": {
"version": "0.4.18",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
"dev": true,
"requires": {
"source-map": "0.5.7"
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
"integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
"dev": true,
"requires": {
"has-flag": "2.0.0"
}
},
"ts-node": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz",
"integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=",
"dev": true,
"requires": {
"arrify": "1.0.1",
"chalk": "2.1.0",
"diff": "3.3.1",
"make-error": "1.3.0",
"minimist": "1.2.0",
"mkdirp": "0.5.1",
"source-map-support": "0.4.18",
"tsconfig": "6.0.0",
"v8flags": "3.0.0",
"yn": "2.0.0"
}
},
"tsconfig": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-6.0.0.tgz",
"integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=",
"dev": true,
"requires": {
"strip-bom": "3.0.0",
"strip-json-comments": "2.0.1"
}
},
"typescript": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz",
"integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=",
"dev": true
},
"user-home": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz",
"integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
"dev": true
},
"v8flags": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.0.tgz",
"integrity": "sha512-AGl+C+4qpeSu2g3JxCD/mGFFOs/vVZ3XREkD3ibQXEqr4Y4zgIrPWW124/IKJFHOIVFIoH8miWrLf0o84HYjwA==",
"dev": true,
"requires": {
"user-home": "1.1.1"
}
},
"yn": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz",
"integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=",
"dev": true
}
}
}

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"devDependencies": {
"@types/chalk": "^0.4.31",
"@types/diff": "^3.2.2",
"@types/long": "^3.0.32",
"chalk": "^2.1.0",
"diff": "^3.3.1",
"long": "^3.2.0",
"ts-node": "^3.3.0",
"typescript": "^2.5.2"
},
"dependencies": {
"@types/node": "^8.0.28",
"binaryen": "37.0.0-nightly.20170912"
}
}

View File

@ -0,0 +1,32 @@
var fs = require("fs");
var messages = require(__dirname + "/../src/diagnosticMessages.json");
var sb = [ "// code below is generated from diagnosticsMessages.json by scripts/build-diagnostics\n\n" ];
function makeKey(text) {
return text.replace(/[^\w]+/g, "_").replace(/_+$/, "");
}
sb.push("export enum DiagnosticCode {\n");
var first = true;
Object.keys(messages).forEach(text => {
var key = makeKey(text);
if (first)
first = false;
else {
sb.push(",\n");
}
sb.push(" " + key + " = " + messages[text]);
});
sb.push("\n}\n\nexport function diagnosticCodeToString(code: DiagnosticCode): string {\n switch (code) {\n");
Object.keys(messages).forEach(text => {
sb.push(" case " + messages[text] + ": return " + JSON.stringify(text) + ";\n");
});
sb.push(" default: return \"\";\n }\n}\n");
fs.writeFileSync(__dirname + "/../src/diagnosticMessages.generated.ts", sb.join(""), { encoding: "utf8" });

1785
src/ast.ts Normal file

File diff suppressed because it is too large Load Diff

252
src/binaryen.d.ts vendored Normal file
View File

@ -0,0 +1,252 @@
/*
Binaryen's C-API.
The WebAssembly version of the compiler will be linked against Binaryen
compiled to WebAssembly with these functions present in the binary while the
JS version calls them on the global object.
see: https://github.com/WebAssembly/binaryen/blob/master/src/binaryen-c.h
*/
declare function _malloc(size: usize): usize;
declare function _free(ptr: usize): void;
declare type BinaryenIndex = u32;
declare type BinaryenType = u32;
declare function _BinaryenNone(): BinaryenType;
declare function _BinaryenInt32(): BinaryenType;
declare function _BinaryenInt64(): BinaryenType;
declare function _BinaryenFloat32(): BinaryenType;
declare function _BinaryenFloat64(): BinaryenType;
declare function _BinaryenUndefined(): BinaryenType;
declare type BinaryenModuleRef = usize;
declare function _BinaryenModuleCreate(): BinaryenModuleRef;
declare function _BinaryenModuleDispose(module: BinaryenModuleRef): void;
declare type BinaryenFunctionTypeRef = usize;
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 type BinaryenLiteral = CArray<u8>;
// 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;
declare function _BinaryenLiteralFloat32(out: BinaryenLiteral, x: f32): void;
declare function _BinaryenLiteralFloat64(out: BinaryenLiteral, x: f64): void;
declare function _BinaryenLiteralFloat32Bits(out: BinaryenLiteral, x: i32): void;
declare function _BinaryenLiteralFloat64Bits(out: BinaryenLiteral, x: i32, y: i32): void;
declare type BinaryenOp = i32;
declare function _BinaryenClzInt32(): BinaryenOp;
declare function _BinaryenCtzInt32(): BinaryenOp;
declare function _BinaryenPopcntInt32(): BinaryenOp;
declare function _BinaryenNegFloat32(): BinaryenOp;
declare function _BinaryenAbsFloat32(): BinaryenOp;
declare function _BinaryenCeilFloat32(): BinaryenOp;
declare function _BinaryenFloorFloat32(): BinaryenOp;
declare function _BinaryenTruncFloat32(): BinaryenOp;
declare function _BinaryenNearestFloat32(): BinaryenOp;
declare function _BinaryenSqrtFloat32(): BinaryenOp;
declare function _BinaryenEqZInt32(): BinaryenOp;
declare function _BinaryenClzInt64(): BinaryenOp;
declare function _BinaryenCtzInt64(): BinaryenOp;
declare function _BinaryenPopcntInt64(): BinaryenOp;
declare function _BinaryenNegFloat64(): BinaryenOp;
declare function _BinaryenAbsFloat64(): BinaryenOp;
declare function _BinaryenCeilFloat64(): BinaryenOp;
declare function _BinaryenFloorFloat64(): BinaryenOp;
declare function _BinaryenTruncFloat64(): BinaryenOp;
declare function _BinaryenNearestFloat64(): BinaryenOp;
declare function _BinaryenSqrtFloat64(): BinaryenOp;
declare function _BinaryenEqZInt64(): BinaryenOp;
declare function _BinaryenExtendSInt32(): BinaryenOp;
declare function _BinaryenExtendUInt32(): BinaryenOp;
declare function _BinaryenWrapInt64(): BinaryenOp;
declare function _BinaryenTruncSFloat32ToInt32(): BinaryenOp;
declare function _BinaryenTruncSFloat32ToInt64(): BinaryenOp;
declare function _BinaryenTruncUFloat32ToInt32(): BinaryenOp;
declare function _BinaryenTruncUFloat32ToInt64(): BinaryenOp;
declare function _BinaryenTruncSFloat64ToInt32(): BinaryenOp;
declare function _BinaryenTruncSFloat64ToInt64(): BinaryenOp;
declare function _BinaryenTruncUFloat64ToInt32(): BinaryenOp;
declare function _BinaryenTruncUFloat64ToInt64(): BinaryenOp;
declare function _BinaryenReinterpretFloat32(): BinaryenOp;
declare function _BinaryenReinterpretFloat64(): BinaryenOp;
declare function _BinaryenConvertSInt32ToFloat32(): BinaryenOp;
declare function _BinaryenConvertSInt32ToFloat64(): BinaryenOp;
declare function _BinaryenConvertUInt32ToFloat32(): BinaryenOp;
declare function _BinaryenConvertUInt32ToFloat64(): BinaryenOp;
declare function _BinaryenConvertSInt64ToFloat32(): BinaryenOp;
declare function _BinaryenConvertSInt64ToFloat64(): BinaryenOp;
declare function _BinaryenConvertUInt64ToFloat32(): BinaryenOp;
declare function _BinaryenConvertUInt64ToFloat64(): BinaryenOp;
declare function _BinaryenPromoteFloat32(): BinaryenOp;
declare function _BinaryenDemoteFloat64(): BinaryenOp;
declare function _BinaryenReinterpretInt32(): BinaryenOp;
declare function _BinaryenReinterpretInt64(): BinaryenOp;
declare function _BinaryenAddInt32(): BinaryenOp;
declare function _BinaryenSubInt32(): BinaryenOp;
declare function _BinaryenMulInt32(): BinaryenOp;
declare function _BinaryenDivSInt32(): BinaryenOp;
declare function _BinaryenDivUInt32(): BinaryenOp;
declare function _BinaryenRemSInt32(): BinaryenOp;
declare function _BinaryenRemUInt32(): BinaryenOp;
declare function _BinaryenAndInt32(): BinaryenOp;
declare function _BinaryenOrInt32(): BinaryenOp;
declare function _BinaryenXorInt32(): BinaryenOp;
declare function _BinaryenShlInt32(): BinaryenOp;
declare function _BinaryenShrUInt32(): BinaryenOp;
declare function _BinaryenShrSInt32(): BinaryenOp;
declare function _BinaryenRotLInt32(): BinaryenOp;
declare function _BinaryenRotRInt32(): BinaryenOp;
declare function _BinaryenEqInt32(): BinaryenOp;
declare function _BinaryenNeInt32(): BinaryenOp;
declare function _BinaryenLtSInt32(): BinaryenOp;
declare function _BinaryenLtUInt32(): BinaryenOp;
declare function _BinaryenLeSInt32(): BinaryenOp;
declare function _BinaryenLeUInt32(): BinaryenOp;
declare function _BinaryenGtSInt32(): BinaryenOp;
declare function _BinaryenGtUInt32(): BinaryenOp;
declare function _BinaryenGeSInt32(): BinaryenOp;
declare function _BinaryenGeUInt32(): BinaryenOp;
declare function _BinaryenAddInt64(): BinaryenOp;
declare function _BinaryenSubInt64(): BinaryenOp;
declare function _BinaryenMulInt64(): BinaryenOp;
declare function _BinaryenDivSInt64(): BinaryenOp;
declare function _BinaryenDivUInt64(): BinaryenOp;
declare function _BinaryenRemSInt64(): BinaryenOp;
declare function _BinaryenRemUInt64(): BinaryenOp;
declare function _BinaryenAndInt64(): BinaryenOp;
declare function _BinaryenOrInt64(): BinaryenOp;
declare function _BinaryenXorInt64(): BinaryenOp;
declare function _BinaryenShlInt64(): BinaryenOp;
declare function _BinaryenShrUInt64(): BinaryenOp;
declare function _BinaryenShrSInt64(): BinaryenOp;
declare function _BinaryenRotLInt64(): BinaryenOp;
declare function _BinaryenRotRInt64(): BinaryenOp;
declare function _BinaryenEqInt64(): BinaryenOp;
declare function _BinaryenNeInt64(): BinaryenOp;
declare function _BinaryenLtSInt64(): BinaryenOp;
declare function _BinaryenLtUInt64(): BinaryenOp;
declare function _BinaryenLeSInt64(): BinaryenOp;
declare function _BinaryenLeUInt64(): BinaryenOp;
declare function _BinaryenGtSInt64(): BinaryenOp;
declare function _BinaryenGtUInt64(): BinaryenOp;
declare function _BinaryenGeSInt64(): BinaryenOp;
declare function _BinaryenGeUInt64(): BinaryenOp;
declare function _BinaryenAddFloat32(): BinaryenOp;
declare function _BinaryenSubFloat32(): BinaryenOp;
declare function _BinaryenMulFloat32(): BinaryenOp;
declare function _BinaryenDivFloat32(): BinaryenOp;
declare function _BinaryenCopySignFloat32(): BinaryenOp;
declare function _BinaryenMinFloat32(): BinaryenOp;
declare function _BinaryenMaxFloat32(): BinaryenOp;
declare function _BinaryenEqFloat32(): BinaryenOp;
declare function _BinaryenNeFloat32(): BinaryenOp;
declare function _BinaryenLtFloat32(): BinaryenOp;
declare function _BinaryenLeFloat32(): BinaryenOp;
declare function _BinaryenGtFloat32(): BinaryenOp;
declare function _BinaryenGeFloat32(): BinaryenOp;
declare function _BinaryenAddFloat64(): BinaryenOp;
declare function _BinaryenSubFloat64(): BinaryenOp;
declare function _BinaryenMulFloat64(): BinaryenOp;
declare function _BinaryenDivFloat64(): BinaryenOp;
declare function _BinaryenCopySignFloat64(): BinaryenOp;
declare function _BinaryenMinFloat64(): BinaryenOp;
declare function _BinaryenMaxFloat64(): BinaryenOp;
declare function _BinaryenEqFloat64(): BinaryenOp;
declare function _BinaryenNeFloat64(): BinaryenOp;
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 BinaryenExpressionRef = usize;
declare function _BinaryenBlock(module: BinaryenModuleRef, name: CString, children: CArray<BinaryenExpressionRef>, numChildren: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenIf(module: BinaryenModuleRef, condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenLoop(module: BinaryenModuleRef, name: CString, body: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenBreak(module: BinaryenModuleRef, name: CString, condition: BinaryenExpressionRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenSwitch(module: BinaryenModuleRef, names: CArray<CString>, numNames: BinaryenIndex, defaultName: CString, condition: BinaryenExpressionRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenCall(module: BinaryenModuleRef, target: CString, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex, returnType: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenCallImport(module: BinaryenModuleRef, target: CString, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex, returnType: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenCallIndirect(module: BinaryenModuleRef, target: BinaryenExpressionRef, operands: CArray<BinaryenExpressionRef>, numOperands: BinaryenIndex, type: CString): BinaryenExpressionRef;
declare function _BinaryenGetLocal(module: BinaryenModuleRef, index: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenSetLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenTeeLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenGetGlobal(module: BinaryenModuleRef, name: CString, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenSetGlobal(module: BinaryenModuleRef, name: CString, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenLoad(module: BinaryenModuleRef, bytes: u32, signed: i8, offset: u32, align: u32, type: BinaryenType, ptr: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenStore(module: BinaryenModuleRef, bytes: u32, offset: u32, align: u32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef;
declare function _BinaryenConst(module: BinaryenModuleRef, value: BinaryenLiteral): BinaryenExpressionRef;
declare function _BinaryenUnary(module: BinaryenModuleRef, op: BinaryenOp, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenBinary(module: BinaryenModuleRef, op: BinaryenOp, left: BinaryenExpressionRef, right: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenSelect(module: BinaryenModuleRef, condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenDrop(module: BinaryenModuleRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
declare function _BinaryenReturn(module: BinaryenModuleRef, value: BinaryenExpressionRef): 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 _BinaryenUnreachable(module: BinaryenModuleRef): BinaryenExpressionRef;
declare function _BinaryenExpressionPrint(expr: BinaryenExpressionRef): void;
declare type BinaryenFunctionRef = usize;
declare function _BinaryenAddFunction(module: BinaryenModuleRef, name: CString, type: BinaryenFunctionTypeRef, varTypes: CArray<BinaryenType>, numVarTypes: BinaryenIndex, body: BinaryenExpressionRef): BinaryenFunctionRef;
declare type BinaryenImportRef = usize;
declare function _BinaryenAddImport(module: BinaryenModuleRef, internalName: CString, externalModuleName: CString, externalBaseName: CString, type: BinaryenFunctionTypeRef): BinaryenImportRef;
declare function _BinaryenRemoveImport(module: BinaryenModuleRef, internalName: CString): void;
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;
declare function _BinaryenSetFunctionTable(module: BinaryenModuleRef, funcs: CArray<BinaryenFunctionRef>, numFuncs: BinaryenIndex): void;
declare function _BinaryenSetMemory(module: BinaryenModuleRef, initial: BinaryenIndex, maximum: BinaryenIndex, exportName: CString, segments: CArray<CArray<u8>>, segmentOffsets: CArray<BinaryenExpressionRef>, segmentSizes: CArray<BinaryenIndex>, numSegments: BinaryenIndex): void;
declare function _BinaryenSetStart(module: BinaryenModuleRef, start: BinaryenFunctionRef): void;
declare function _BinaryenModuleParse(text: CString): BinaryenModuleRef;
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 _BinaryenModuleAutoDrop(module: BinaryenModuleRef): void;
declare function _BinaryenModuleWrite(module: BinaryenModuleRef, output: CString, outputSize: usize): usize;
declare function _BinaryenModuleRead(input: CString, inputSize: usize): BinaryenModuleRef;
declare function _BinaryenModuleInterpret(module: BinaryenModuleRef): void;
declare type RelooperRef = usize;
declare type RelooperBlockRef = usize;
declare function _RelooperCreate(): RelooperRef;
declare function _RelooperAddBlock(relooper: RelooperRef, code: BinaryenExpressionRef): RelooperBlockRef;
declare function _RelooperAddBranch(from: RelooperBlockRef, to: RelooperBlockRef, condition: BinaryenExpressionRef, code: BinaryenExpressionRef): void;
declare function _RelooperAddBlockWithSwitch(relooper: RelooperRef, code: BinaryenExpressionRef, condition: BinaryenExpressionRef): RelooperBlockRef;
declare function _RelooperAddBranchForSwitch(from: RelooperBlockRef, to: RelooperBlockRef, indexes: CArray<BinaryenIndex>, numIndexes: BinaryenIndex, code: BinaryenExpressionRef): void;
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;

585
src/binaryen.ts Normal file
View File

@ -0,0 +1,585 @@
import { U64 } from "./util";
import { Target } from "./compiler";
export enum Type {
None = _BinaryenNone(),
I32 = _BinaryenInt32(),
I64 = _BinaryenInt64(),
F32 = _BinaryenFloat32(),
F64 = _BinaryenFloat64(),
Undefined = _BinaryenUndefined()
}
export enum UnaryOp {
ClzI32 = _BinaryenClzInt32(),
CtzI32 = _BinaryenCtzInt32(),
PopcntI32 = _BinaryenPopcntInt32(),
NegF32 = _BinaryenNegFloat32(),
AbsF32 = _BinaryenAbsFloat32(),
CeilF32 = _BinaryenCeilFloat32(),
FloorF32 = _BinaryenFloorFloat32(),
TruncF32 = _BinaryenTruncFloat32(),
NearestF32 = _BinaryenNearestFloat32(),
SqrtF32 = _BinaryenSqrtFloat32(),
EqzI32 = _BinaryenEqZInt32(),
ClzI64 = _BinaryenClzInt64(),
CtzI64 = _BinaryenCtzInt64(),
PopcntI64 = _BinaryenPopcntInt64(),
NegF64 = _BinaryenNegFloat64(),
AbsF64 = _BinaryenAbsFloat64(),
CeilF64 = _BinaryenCeilFloat64(),
FloorF64 = _BinaryenFloorFloat64(),
TruncF64 = _BinaryenTruncFloat64(),
NearestF64 = _BinaryenNearestFloat64(),
SqrtF64 = _BinaryenSqrtFloat64(),
EqzI64 = _BinaryenEqZInt64(),
ExtendI32 = _BinaryenExtendSInt32(),
ExtendU32 = _BinaryenExtendUInt32(),
WrapI64 = _BinaryenWrapInt64(),
TruncF32_I32 = _BinaryenTruncSFloat32ToInt32(),
TruncF32_I64 = _BinaryenTruncSFloat32ToInt64(),
TruncF32_U32 = _BinaryenTruncUFloat32ToInt32(),
TruncF32_U64 = _BinaryenTruncUFloat32ToInt64(),
TruncF64_I32 = _BinaryenTruncSFloat64ToInt32(),
TruncF64_I64 = _BinaryenTruncSFloat64ToInt64(),
TruncF64_U32 = _BinaryenTruncUFloat64ToInt32(),
TruncF64_U64 = _BinaryenTruncUFloat64ToInt64(),
ReinterpretF32 = _BinaryenReinterpretFloat32(),
ReinterpretF64 = _BinaryenReinterpretFloat64(),
ConvertI32_F32 = _BinaryenConvertSInt32ToFloat32(),
ConvertI32_F64 = _BinaryenConvertSInt32ToFloat64(),
ConvertU32_F32 = _BinaryenConvertUInt32ToFloat32(),
ConvertU32_F64 = _BinaryenConvertUInt32ToFloat64(),
ConvertI64_F32 = _BinaryenConvertSInt64ToFloat32(),
ConvertI64_F64 = _BinaryenConvertSInt64ToFloat64(),
ConvertU64_F32 = _BinaryenConvertUInt64ToFloat32(),
ConvertU64_F64 = _BinaryenConvertUInt64ToFloat64(),
PromoteF32 = _BinaryenPromoteFloat32(),
DemoteF64 = _BinaryenDemoteFloat64(),
ReinterpretI32 = _BinaryenReinterpretInt32(),
ReinterpretI64 = _BinaryenReinterpretInt64()
}
export enum BinaryOp {
AddI32 = _BinaryenAddInt32(),
SubI32 = _BinaryenSubInt32(),
MulI32 = _BinaryenMulInt32(),
DivI32 = _BinaryenDivSInt32(),
DivU32 = _BinaryenDivUInt32(),
RemI32 = _BinaryenRemSInt32(),
RemU32 = _BinaryenRemUInt32(),
AndI32 = _BinaryenAndInt32(),
OrI32 = _BinaryenOrInt32(),
XorI32 = _BinaryenXorInt32(),
ShlI32 = _BinaryenShlInt32(),
ShrU32 = _BinaryenShrUInt32(),
ShrI32 = _BinaryenShrSInt32(),
RotlI32 = _BinaryenRotLInt32(),
RotrI32 = _BinaryenRotRInt32(),
EqI32 = _BinaryenEqInt32(),
NeI32 = _BinaryenNeInt32(),
LtI32 = _BinaryenLtSInt32(),
LtU32 = _BinaryenLtUInt32(),
LeI32 = _BinaryenLeSInt32(),
LeU32 = _BinaryenLeUInt32(),
GtI32 = _BinaryenGtSInt32(),
GtU32 = _BinaryenGtUInt32(),
GeI32 = _BinaryenGeSInt32(),
GeU32 = _BinaryenGeUInt32(),
AddI64 = _BinaryenAddInt64(),
SubI64 = _BinaryenSubInt64(),
MulI64 = _BinaryenMulInt64(),
DivI64 = _BinaryenDivSInt64(),
DivU64 = _BinaryenDivUInt64(),
RemI64 = _BinaryenRemSInt64(),
RemU64 = _BinaryenRemUInt64(),
AndI64 = _BinaryenAndInt64(),
OrI64 = _BinaryenOrInt64(),
XorI64 = _BinaryenXorInt64(),
ShlI64 = _BinaryenShlInt64(),
ShrU64 = _BinaryenShrUInt64(),
ShrI64 = _BinaryenShrSInt64(),
RotlI64 = _BinaryenRotLInt64(),
RotrI64 = _BinaryenRotRInt64(),
EqI64 = _BinaryenEqInt64(),
NeI64 = _BinaryenNeInt64(),
LtI64 = _BinaryenLtSInt64(),
LtU64 = _BinaryenLtUInt64(),
LeI64 = _BinaryenLeSInt64(),
LeU64 = _BinaryenLeUInt64(),
GtI64 = _BinaryenGtSInt64(),
GtU64 = _BinaryenGtUInt64(),
GeI64 = _BinaryenGeSInt64(),
GeU64 = _BinaryenGeUInt64(),
AddF32 = _BinaryenAddFloat32(),
SubF32 = _BinaryenSubFloat32(),
MulF32 = _BinaryenMulFloat32(),
DivF32 = _BinaryenDivFloat32(),
CopysignF32 = _BinaryenCopySignFloat32(),
MinF32 = _BinaryenMinFloat32(),
MaxF32 = _BinaryenMaxFloat32(),
EqF32 = _BinaryenEqFloat32(),
NeF32 = _BinaryenNeFloat32(),
LtF32 = _BinaryenLtFloat32(),
LeF32 = _BinaryenLeFloat32(),
GtF32 = _BinaryenGtFloat32(),
GeF32 = _BinaryenGeFloat32(),
AddF64 = _BinaryenAddFloat64(),
SubF64 = _BinaryenSubFloat64(),
MulF64 = _BinaryenMulFloat64(),
DivF64 = _BinaryenDivFloat64(),
CopysignF64 = _BinaryenCopySignFloat64(),
MinF64 = _BinaryenMinFloat64(),
MaxF64 = _BinaryenMaxFloat64(),
EqF64 = _BinaryenEqFloat64(),
NeF64 = _BinaryenNeFloat64(),
LtF64 = _BinaryenLtFloat64(),
LeF64 = _BinaryenLeFloat64(),
GtF64 = _BinaryenGtFloat64(),
GeF64 = _BinaryenGeFloat64()
}
export enum HostOp {
PageSize = _BinaryenPageSize(),
CurrentMemory = _BinaryenCurrentMemory(),
GrowMemory = _BinaryenGrowMemory(),
HasFeature = _BinaryenHasFeature()
}
export enum AtomicRMWOp { // TODO: not yet part of the C-API
Add,
Sub,
And,
Or,
Xor,
Xchg
}
export class MemorySegment {
buffer: Uint8Array;
offset: U64;
static create(buffer: Uint8Array, offset: U64) {
const segment: MemorySegment = new MemorySegment();
segment.buffer = buffer;
segment.offset = offset;
return segment;
}
}
export class Module {
ref: BinaryenModuleRef;
lit: BinaryenLiteral;
static create(): Module {
const module: Module = new Module();
module.ref = _BinaryenModuleCreate();
module.lit = _malloc(16);
return module;
}
static createFrom(buffer: Uint8Array): Module {
const cArr: CArray<u8> = allocU8Array(buffer);
try {
const module: Module = new Module();
module.ref = _BinaryenModuleRead(cArr, buffer.length);
module.lit = _malloc(16);
return module;
} finally {
_free(cArr);
}
}
static MAX_MEMORY_WASM32: BinaryenIndex = 0xffff;
// types
addFunctionType(name: string, result: Type, paramTypes: Type[]): BinaryenFunctionRef {
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(paramTypes);
try {
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes.length);
} finally {
_free(cStr);
_free(cArr);
}
}
getFunctionTypeBySignature(result: Type, paramTypes: Type[]): BinaryenFunctionTypeRef {
const cArr: CArray<i32> = allocI32Array(paramTypes);
try {
return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes.length);
} finally {
_free(cArr);
}
}
// expressions
createI32(value: i32): BinaryenExpressionRef {
_BinaryenLiteralInt32(this.lit, value);
return _BinaryenConst(this.ref, this.lit);
}
createI64(lo: i32, hi: i32): BinaryenExpressionRef {
_BinaryenLiteralInt64(this.lit, lo, hi);
return _BinaryenConst(this.ref, this.lit);
}
createF32(value: f32): BinaryenExpressionRef {
_BinaryenLiteralFloat32(this.lit, value);
return _BinaryenConst(this.ref, this.lit);
}
createF64(value: f64): BinaryenExpressionRef {
_BinaryenLiteralFloat64(this.lit, value);
return _BinaryenConst(this.ref, this.lit);
}
createUnary(op: UnaryOp, expr: BinaryenExpressionRef): BinaryenExpressionRef {
return _BinaryenUnary(this.ref, op, expr);
}
createBinary(op: BinaryOp, left: BinaryenExpressionRef, right: BinaryenExpressionRef): BinaryenExpressionRef {
return _BinaryenBinary(this.ref, op, left, right);
}
createHost(op: HostOp, name: string | null = null, operands: BinaryenExpressionRef[] | null = null): BinaryenExpressionRef {
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(operands);
try {
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<BinaryenExpressionRef[]>operands).length : 0);
} finally {
_free(cStr);
_free(cArr);
}
}
createGetLocal(index: i32, type: Type): BinaryenExpressionRef {
return _BinaryenGetLocal(this.ref, index, type);
}
createTeeLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
return _BinaryenTeeLocal(this.ref, index, value);
}
createGetGlobal(name: string, type: Type): BinaryenExpressionRef {
const cStr: CString = allocString(name);
try {
return _BinaryenGetGlobal(this.ref, cStr, type);
} finally {
_free(cStr);
}
}
createTeeGlobal(name: string, value: BinaryenExpressionRef, type: Type): BinaryenExpressionRef {
// emulated, lives here for simplicity reasons
return this.createBlock(null, [
this.createSetGlobal(name, value),
this.createGetGlobal(name, type)
], type);
}
// statements
createSetLocal(index: i32, value: BinaryenExpressionRef): BinaryenExpressionRef {
return _BinaryenSetLocal(this.ref, index, value);
}
createSetGlobal(name: string, value: BinaryenExpressionRef): BinaryenExpressionRef {
const cStr: CString = allocString(name);
try {
return _BinaryenSetGlobal(this.ref, cStr, value);
} finally {
_free(cStr);
}
}
createBlock(label: string | null, children: BinaryenExpressionRef[], type: Type = Type.Undefined): BinaryenExpressionRef {
const cStr: CString = allocString(label);
const cArr: CArray<i32> = allocI32Array(children);
try {
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
} finally {
_free(cStr);
_free(cArr);
}
}
createBreak(label: string | null, condition: BinaryenExpressionRef = 0, value: BinaryenExpressionRef = 0): BinaryenExpressionRef {
const cStr: CString = allocString(label);
try {
return _BinaryenBreak(this.ref, cStr, condition, value);
} finally {
_free(cStr);
}
}
createDrop(expression: BinaryenExpressionRef): BinaryenExpressionRef {
return _BinaryenDrop(this.ref, expression);
}
createLoop(label: string | null, body: BinaryenExpressionRef): BinaryenExpressionRef {
const cStr: CString = allocString(label);
try {
return _BinaryenLoop(this.ref, cStr, body);
} finally {
_free(cStr);
}
}
createIf(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef = 0): BinaryenExpressionRef {
return _BinaryenIf(this.ref, condition, ifTrue, ifFalse);
}
createNop(): BinaryenExpressionRef {
return _BinaryenNop(this.ref);
}
createReturn(expression: BinaryenExpressionRef = 0): BinaryenExpressionRef {
return _BinaryenReturn(this.ref, expression);
}
createSelect(condition: BinaryenExpressionRef, ifTrue: BinaryenExpressionRef, ifFalse: BinaryenExpressionRef): BinaryenExpressionRef {
return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse);
}
createSwitch(names: string[], defaultName: string | null, condition: BinaryenExpressionRef, value: BinaryenExpressionRef = 0): BinaryenExpressionRef {
const strs: CString[] = new Array(names.length);
let i: i32, k: i32 = names.length;
for (i = 0; i < k; ++i) strs[i] = allocString(names[i]);
const cArr: CArray<i32> = allocI32Array(strs);
const cStr: CString = allocString(defaultName);
try {
return _BinaryenSwitch(this.ref, cArr, k, cStr, condition, value);
} finally {
for (i = 0; i < k; ++i) _free(strs[i]);
_free(cArr);
_free(cStr);
}
}
createCall(target: BinaryenFunctionRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
const cArr: CArray<i32> = allocI32Array(operands);
try {
return _BinaryenCall(this.ref, target, cArr, operands.length, returnType);
} finally {
_free(cArr);
}
}
createCallImport(target: BinaryenImportRef, operands: BinaryenExpressionRef[], returnType: Type): BinaryenExpressionRef {
const cArr: CArray<i32> = allocI32Array(operands);
try {
return _BinaryenCallImport(this.ref, target, cArr, operands.length, returnType);
} finally {
_free(cArr);
}
}
createUnreachable(): BinaryenExpressionRef {
return _BinaryenUnreachable(this.ref);
}
// meta
addGlobal(name: string, type: Type, mutable: bool, initializer: BinaryenExpressionRef): BinaryenImportRef {
const cStr: CString = allocString(name);
try {
return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer);
} finally {
_free(cStr);
}
}
addFunction(name: string, type: Type, varTypes: Type[], body: BinaryenExpressionRef): BinaryenFunctionRef {
const cStr: CString = allocString(name);
const cArr: CArray<i32> = allocI32Array(varTypes);
try {
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes.length, body);
} finally {
_free(cStr);
_free(cArr);
}
}
addExport(internalName: string, externalName: string): BinaryenExportRef {
const cStr1: CString = allocString(internalName);
const cStr2: CString = allocString(externalName);
try {
return _BinaryenAddExport(this.ref, cStr1, cStr2);
} finally {
_free(cStr1);
_free(cStr2);
}
}
removeExport(externalName: string): void {
const cStr = allocString(externalName);
try {
_BinaryenRemoveExport(this.ref, cStr);
} finally {
_free(cStr);
}
}
addImport(internalName: string, externalModuleName: string, externalBaseName: string, type: BinaryenFunctionTypeRef): BinaryenImportRef {
const cStr1: CString = allocString(internalName);
const cStr2: CString = allocString(externalModuleName);
const cStr3: CString = allocString(externalBaseName);
try {
return _BinaryenAddImport(this.ref, cStr1, cStr2, cStr3, type);
} finally {
_free(cStr1);
_free(cStr2);
_free(cStr3);
}
}
removeImport(internalName: string): void {
const cStr: CString = allocString(internalName);
try {
_BinaryenRemoveImport(this.ref, cStr);
} finally {
_free(cStr);
}
}
setMemory(initial: BinaryenIndex, maximum: BinaryenIndex, segments: MemorySegment[], target: Target, exportName: string | null = null): void {
const cStr: CString = allocString(exportName);
let i: i32, k: i32 = segments.length;
const segs: CArray<u8>[] = new Array(k);
const offs: BinaryenExpressionRef[] = new Array(k);
const sizs: BinaryenIndex[] = new Array(k);
for (i = 0; i < k; ++i) {
const buffer: Uint8Array = segments[i].buffer;
const offset: U64 = segments[i].offset;
segs[i] = allocU8Array(buffer);
offs[i] = target == Target.WASM64
? this.createI64(offset.lo, offset.hi)
: this.createI32(offset.toI32());
sizs[i] = buffer.length;
}
const cArr1: CArray<i32> = allocI32Array(segs);
const cArr2: CArray<i32> = allocI32Array(offs);
const cArr3: CArray<i32> = allocI32Array(sizs);
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);
}
}
setStart(func: BinaryenFunctionRef): void {
_BinaryenSetStart(this.ref, func);
}
optimize(): void {
_BinaryenModuleOptimize(this.ref);
}
validate(): bool {
return _BinaryenModuleValidate(this.ref) == 1;
}
dispose(): void {
_BinaryenModuleDispose(this.ref);
_free(this.lit);
}
}
// helpers
// TODO: investigate stack allocation?
function allocU8Array(u8s: Uint8Array | null): CArray<u8> {
if (!u8s) return 0;
const ptr: usize = _malloc((<Uint8Array>u8s).length);
let idx: usize = ptr;
for (let i: i32 = 0, k: i32 = (<Uint8Array>u8s).length; i < k; ++i)
store<u8>(idx++, (<Uint8Array>u8s)[i])
return ptr;
}
function allocI32Array(i32s: i32[] | null): CArray<i32> {
if (!i32s) return 0;
const ptr: usize = _malloc((<i32[]>i32s).length << 2);
let idx: usize = ptr;
for (let i: i32 = 0, k: i32 = (<i32[]>i32s).length; i < k; ++i) {
let val: i32 = (<i32[]>i32s)[i];
store<u8>(idx , ( val & 0xff) as u8);
store<u8>(idx + 1, ((val >> 8) & 0xff) as u8);
store<u8>(idx + 2, ((val >> 16) & 0xff) as u8);
store<u8>(idx + 3, ( val >>> 24 ) as u8);
idx += 4;
}
return ptr;
}
function stringLengthUTF8(str: string): usize {
let len: i32 = 0;
for (let i: i32 = 0, k: i32 = str.length; i < k; ++i) {
let u: i32 = str.charCodeAt(i);
if (u >= 0xD800 && u <= 0xDFFF && i + 1 < k)
u = 0x10000 + ((u & 0x3FF) << 10) | (str.charCodeAt(++i) & 0x3FF);
if (u <= 0x7F)
++len;
else if (u <= 0x7FF)
len += 2;
else if (u <= 0xFFFF)
len += 3;
else if (u <= 0x1FFFFF)
len += 4;
else if (u <= 0x3FFFFFF)
len += 5;
else
len += 6;
}
return len;
}
function allocString(str: string | null): CString {
if (!str) return 0;
const ptr: usize = _malloc(stringLengthUTF8((<string>str)) + 1);
let idx: usize = ptr;
for (let i: i32 = 0, k = (<string>str).length; i < k; ++i) {
let u: i32 = (<string>str).charCodeAt(i);
if (u >= 0xD800 && u <= 0xDFFF && i + 1 < k)
u = 0x10000 + ((u & 0x3FF) << 10) | ((<string>str).charCodeAt(++i) & 0x3FF);
if (u <= 0x7F)
store<u8>(idx++, u as u8);
else if (u <= 0x7FF) {
store<u8>(idx++, (0xC0 | (u >>> 6) ) as u8);
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
} else if (u <= 0xFFFF) {
store<u8>(idx++, (0xE0 | (u >>> 12) ) as u8);
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
} else if (u <= 0x1FFFFF) {
store<u8>(idx++, (0xF0 | (u >>> 18) ) as u8);
store<u8>(idx++, (0x80 | ((u >>> 12) & 63)) as u8);
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
} else if (u <= 0x3FFFFFF) {
store<u8>(idx++, (0xF8 | (u >>> 24) ) as u8);
store<u8>(idx++, (0x80 | ((u >>> 18) & 63)) as u8);
store<u8>(idx++, (0x80 | ((u >>> 12) & 63)) as u8);
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
} else {
store<u8>(idx++, (0xFC | (u >>> 30) ) as u8);
store<u8>(idx++, (0x80 | ((u >>> 24) & 63)) as u8);
store<u8>(idx++, (0x80 | ((u >>> 18) & 63)) as u8);
store<u8>(idx++, (0x80 | ((u >>> 12) & 63)) as u8);
store<u8>(idx++, (0x80 | ((u >>> 6) & 63)) as u8);
store<u8>(idx++, (0x80 | ( u & 63)) as u8);
}
}
store<u8>(idx, 0);
return ptr;
}

653
src/compiler.ts Normal file
View File

@ -0,0 +1,653 @@
import { Module, MemorySegment, UnaryOp, BinaryOp, HostOp } from "./binaryen";
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
import { hasModifier } from "./parser";
import { Program } from "./program";
import { CharCode, U64 } from "./util";
import {
NodeKind,
TypeNode,
// statements
BlockStatement,
BreakStatement,
ClassDeclaration,
ContinueStatement,
DoStatement,
EmptyStatement,
EnumDeclaration,
ExpressionStatement,
FunctionDeclaration,
ForStatement,
IfStatement,
MethodDeclaration,
ModifierKind,
NamespaceDeclaration,
ReturnStatement,
Statement,
SwitchStatement,
ThrowStatement,
TryStatement,
VariableDeclaration,
VariableStatement,
WhileStatement,
// expressions
ArrayLiteralExpression,
AssertionExpression,
BinaryExpression,
CallExpression,
ElementAccessExpression,
Expression,
FloatLiteralExpression,
IdentifierExpression,
IntegerLiteralExpression,
LiteralExpression,
LiteralKind,
NewExpression,
ParenthesizedExpression,
PropertyAccessExpression,
SelectExpression,
StringLiteralExpression,
UnaryPostfixExpression,
UnaryPrefixExpression
} from "./ast";
import {
Enum,
Class,
Field,
Function,
GlobalVariable,
LocalVariable,
Namespace,
Method,
Source,
Type,
TypeKind
} from "./reflection";
export enum Target {
WASM32,
WASM64
}
export class Options {
target: Target = Target.WASM32;
}
export class Compiler extends DiagnosticEmitter {
program: Program;
options: Options;
module: Module;
currentType: Type = Type.void;
currentClass: Class | null = null;
currentFunction: Function | null = null;
breakMajor: i32 = 0;
breakMinor: i32 = 0;
memoryOffset: U64 = new U64(8, 0); // leave space for (any size of) NULL
memorySegments: MemorySegment[] = new Array();
static compile(program: Program, options: Options | null = null): Module {
const compiler: Compiler = new Compiler(program, options);
return compiler.compile();
}
constructor(program: Program, options: Options | null = null) {
super(program.diagnostics);
this.program = program;
this.options = options ? options : new Options();
this.module = Module.create();
}
compile(): Module {
const program: Program = this.program;
// initialize lookup maps
program.initialize(this.options.target);
// start by compiling entry file exports
const entrySource: Source = program.sources[0];
for (let i: i32 = 0, k = entrySource.statements.length; i < k; ++i) {
const statement: Statement = entrySource.statements[i];
switch (statement.kind) {
case NodeKind.CLASS:
if (hasModifier(ModifierKind.EXPORT, (<ClassDeclaration>statement).modifiers) && !(<ClassDeclaration>statement).typeParameters.length) {
const cl: Class = Class.create(<ClassDeclaration>statement, []).exportAs((<ClassDeclaration>statement).identifier.name);
this.program.addClass(cl);
this.compileClass(cl);
}
break;
case NodeKind.ENUM:
if (hasModifier(ModifierKind.EXPORT, (<EnumDeclaration>statement).modifiers)) {
const en: Enum = Enum.create(<EnumDeclaration>statement).exportAs((<EnumDeclaration>statement).identifier.name);
this.program.addEnum(en);
this.compileEnum(en);
}
break;
case NodeKind.FUNCTION:
if (hasModifier(ModifierKind.EXPORT, (<FunctionDeclaration>statement).modifiers) && !(<FunctionDeclaration>statement).typeParameters.length) {
const fn: Function = Function.create(<FunctionDeclaration>statement, []).exportAs((<FunctionDeclaration>statement).identifier.name);
this.program.addFunction(fn);
this.compileFunction(fn);
}
break;
case NodeKind.NAMESPACE:
if (hasModifier(ModifierKind.EXPORT, (<NamespaceDeclaration>statement).modifiers)) {
const ns: Namespace = Namespace.create(<NamespaceDeclaration>statement).exportAs((<NamespaceDeclaration>statement).identifier.name);
this.program.addNamespace(ns);
this.compileNamespace(ns);
}
break;
case NodeKind.VARIABLE:
if (hasModifier(ModifierKind.EXPORT, (<VariableStatement>statement).modifiers)) {
const gls: GlobalVariable[] = GlobalVariable.create(<VariableStatement>statement);
for (let j: i32 = 0, l: i32 = gls.length; j < l; ++j) {
const gl: GlobalVariable = gls[j];
gl.exportName = (<VariableDeclaration>gl.declaration).identifier.name; // WASM can't do this right now
this.program.addGlobal(gl);
this.compileGlobal(gl);
}
}
break;
case NodeKind.EXPORT:
// obtain referenced declaration and export that
break;
}
}
// set up memory size and static segments
const initial: U64 = this.memoryOffset.clone();
const initialOverlaps: U64 = initial.clone();
initialOverlaps.and32(0xffff);
if (!initialOverlaps.isZero) {
initial.or32(0xffff);
initial.add32(1);
}
initial.shru32(16);
this.module.setMemory(initial.toI32(), Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */, this.memorySegments, this.options.target, "memory");
return this.module;
}
compileClass(cl: Class): void {
throw new Error("not implemented");
}
compileEnum(en: Enum): void {
throw new Error("not implemented");
}
compileFunction(fn: Function): void {
throw new Error("not implemented");
}
compileGlobal(gl: GlobalVariable): void {
throw new Error("not implemented");
}
compileNamespace(ns: Namespace): void {
throw new Error("not implemented");
}
// memory
addMemorySegment(buffer: Uint8Array): MemorySegment {
if (this.memoryOffset.lo & 7) { // align to 8 bytes so any possible data type is aligned here
this.memoryOffset.or32(7);
this.memoryOffset.add32(1);
}
const segment: MemorySegment = MemorySegment.create(buffer, this.memoryOffset.clone());
this.memorySegments.push(segment);
this.memoryOffset.add32(buffer.length);
return segment;
}
// types
resolveType(node: TypeNode, reportNotFound: bool = true): Type | null {
const types: Map<string,Type> = this.program.types;
const name: string = node.identifier.name;
if (types.has(name)) {
const type: Type = <Type>types.get(name);
return type;
}
if (reportNotFound)
this.error(DiagnosticCode.Cannot_find_name_0, node.identifier.range, name);
return null;
}
// statements
compileStatement(statement: Statement): BinaryenExpressionRef {
switch (statement.kind) {
case NodeKind.BLOCK:
return this.compileBlockStatement(<BlockStatement>statement);
case NodeKind.BREAK:
return this.compileBreakStatement(<BreakStatement>statement);
case NodeKind.CONTINUE:
return this.compileContinueStatement(<ContinueStatement>statement);
case NodeKind.DO:
return this.compileDoStatement(<DoStatement>statement);
case NodeKind.EMPTY:
return this.compileEmptyStatement(<EmptyStatement>statement);
case NodeKind.EXPRESSION:
return this.compileExpressionStatement(<ExpressionStatement>statement);
case NodeKind.FOR:
return this.compileForStatement(<ForStatement>statement);
case NodeKind.IF:
return this.compileIfStatement(<IfStatement>statement);
case NodeKind.RETURN:
return this.compileReturnStatement(<ReturnStatement>statement);
case NodeKind.SWITCH:
return this.compileSwitchStatement(<SwitchStatement>statement);
case NodeKind.THROW:
return this.compileThrowStatement(<ThrowStatement>statement);
case NodeKind.TRY:
return this.compileTryStatement(<TryStatement>statement);
case NodeKind.VARIABLE:
return this.compileVariableStatement(<VariableStatement>statement);
case NodeKind.WHILE:
return this.compileWhileStatement(<WhileStatement>statement);
}
throw new Error("not implemented");
}
compileBlockStatement(statement: BlockStatement): BinaryenExpressionRef {
const substatements: Statement[] = statement.statements;
const children: BinaryenExpressionRef[] = new Array(substatements.length);
for (let i: i32 = 0, k: i32 = substatements.length; i < k; ++i)
children[i] = this.compileStatement(substatements[i]);
return this.module.createBlock(null, children);
}
compileBreakStatement(statement: BreakStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileContinueStatement(statement: ContinueStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileDoStatement(statement: DoStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileEmptyStatement(statement: EmptyStatement): BinaryenExpressionRef {
return this.module.createNop();
}
compileExpressionStatement(statement: ExpressionStatement): BinaryenExpressionRef {
const expression: BinaryenExpressionRef = this.compileExpression(statement.expression, Type.void);
return this.currentType == Type.void ? expression : this.module.createDrop(expression);
}
compileForStatement(statement: ForStatement): BinaryenExpressionRef {
const initializer: BinaryenExpressionRef = statement.initializer ? this.compileStatement(<Statement>statement.initializer) : 0;
const condition: BinaryenExportRef = statement.condition ? this.compileExpression(<Expression>statement.condition, Type.i32) : 0;
const incrementor: BinaryenExportRef = statement.incrementor ? this.compileExpression(<Expression>statement.incrementor, Type.void) : 0;
throw new Error("not implemented");
}
compileIfStatement(statement: IfStatement): BinaryenExpressionRef {
const condition: BinaryenExpressionRef = this.compileExpression(statement.condition, Type.i32);
const ifTrue: BinaryenExpressionRef = this.compileStatement(statement.statement);
const ifFalse: BinaryenExportRef = statement.elseStatement ? this.compileStatement(<Statement>statement.elseStatement) : 0;
return this.module.createIf(condition, ifTrue, ifFalse);
}
compileReturnStatement(statement: ReturnStatement): BinaryenExpressionRef {
if (this.currentFunction) {
const expression: BinaryenExpressionRef = statement.expression ? this.compileExpression(<Expression>statement.expression, (<Function>this.currentFunction).returnType) : 0;
return this.module.createReturn(expression);
}
return this.module.createUnreachable();
}
compileSwitchStatement(statement: SwitchStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileThrowStatement(statement: ThrowStatement): BinaryenExpressionRef {
return this.module.createUnreachable(); // TODO: waiting for exception-handling spec
}
compileTryStatement(statement: TryStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileVariableStatement(statement: VariableStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileWhileStatement(statement: WhileStatement): BinaryenExpressionRef {
throw new Error("not implemented");
}
// expressions
compileExpression(expression: Expression, resultType: Type): BinaryenExpressionRef {
this.currentType = resultType;
let expr: BinaryenExpressionRef;
switch (expression.kind) {
case NodeKind.ASSERTION:
expr = this.compileAssertionExpression(<AssertionExpression>expression, resultType);
break;
case NodeKind.BINARY:
expr = this.compileBinaryExpression(<BinaryExpression>expression, resultType);
break;
case NodeKind.CALL:
expr = this.compileCallExpression(<CallExpression>expression, resultType);
break;
case NodeKind.ELEMENTACCESS:
expr = this.compileElementAccessExpression(<ElementAccessExpression>expression, resultType);
break;
case NodeKind.IDENTIFIER:
expr = this.compileIdentifierExpression(<IdentifierExpression>expression, resultType);
break;
case NodeKind.LITERAL:
expr = this.compileLiteralExpression(<LiteralExpression>expression, resultType);
break;
case NodeKind.NEW:
expr = this.compileNewExpression(<NewExpression>expression, resultType);
break;
case NodeKind.PARENTHESIZED:
expr = this.compileParenthesizedExpression(<ParenthesizedExpression>expression, resultType);
break;
case NodeKind.PROPERTYACCESS:
expr = this.compilePropertyAccessExpression(<PropertyAccessExpression>expression, resultType);
break;
case NodeKind.SELECT:
expr = this.compileSelectExpression(<SelectExpression>expression, resultType);
break;
case NodeKind.UNARYPOSTFIX:
expr = this.compileUnaryPostfixExpression(<UnaryPostfixExpression>expression, resultType);
break;
case NodeKind.UNARYPREFIX:
expr = this.compileUnaryPrefixExpression(<UnaryPrefixExpression>expression, resultType);
break;
default:
throw new Error("unexpected expression kind");
}
if (this.currentType != resultType) {
expr = this.convertExpression(expr, this.currentType, resultType);
this.currentType = resultType;
}
return expr;
}
convertExpression(expr: BinaryenExpressionRef, fromType: Type, toType: Type): BinaryenExpressionRef {
// void to any
if (fromType.kind == TypeKind.VOID)
throw new Error("illegal conversion");
// any to void
if (toType.kind == TypeKind.VOID)
return this.module.createDrop(expr);
const fromFloat: bool = fromType.isAnyFloat;
const toFloat: bool = toType.isAnyFloat;
const mod: Module = this.module;
let losesInformation: bool = false;
if (fromFloat) {
// float to float
if (toFloat) {
if (fromType.kind == TypeKind.F32) {
// f32 to f64
if (toType.kind == TypeKind.F64)
expr = mod.createUnary(UnaryOp.PromoteF32, expr);
// f64 to f32
} else if (toType.kind == TypeKind.F32) {
losesInformation = true;
expr = mod.createUnary(UnaryOp.DemoteF64, expr);
}
// float to int
} else {
losesInformation = true;
// f32 to int
if (fromType.kind == TypeKind.F32) {
if (toType.isSignedInteger) {
if (toType.isLongInteger)
expr = mod.createUnary(UnaryOp.TruncF32_I64, expr);
else {
expr = mod.createUnary(UnaryOp.TruncF32_I32, expr);
if (toType.isSmallInteger) {
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
}
}
} else {
if (toType.isLongInteger)
expr = mod.createUnary(UnaryOp.TruncF32_U64, expr);
else {
expr = mod.createUnary(UnaryOp.TruncF32_U32, expr);
if (toType.isSmallInteger)
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
}
}
// f64 to int
} else {
if (toType.isSignedInteger) {
if (toType.isLongInteger)
expr = mod.createUnary(UnaryOp.TruncF64_I64, expr);
else {
expr = mod.createUnary(UnaryOp.TruncF64_I32, expr);
if (toType.isSmallInteger) {
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
}
}
} else {
if (toType.isLongInteger)
expr = mod.createUnary(UnaryOp.TruncF64_U64, expr);
else {
expr = mod.createUnary(UnaryOp.TruncF64_U32, expr);
if (toType.isSmallInteger)
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
}
}
}
}
// int to float
} else if (toFloat) {
// int to f32
if (toType.kind == TypeKind.F32) {
if (fromType.isLongInteger) {
losesInformation = true;
if (fromType.isSignedInteger)
expr = mod.createUnary(UnaryOp.ConvertI64_F32, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU64_F32, expr);
} else
if (fromType.isSignedInteger)
expr = mod.createUnary(UnaryOp.ConvertI32_F32, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU32_F32, expr);
// int to f64
} else {
if (fromType.isLongInteger) {
losesInformation = true;
if (fromType.isSignedInteger)
expr = mod.createUnary(UnaryOp.ConvertI64_F64, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU64_F64, expr);
} else
if (fromType.isSignedInteger)
expr = mod.createUnary(UnaryOp.ConvertI32_F64, expr);
else
expr = mod.createUnary(UnaryOp.ConvertU32_F64, expr);
}
// int to int
} else {
if (fromType.isLongInteger) {
// i64 to i32
if (!toType.isLongInteger) {
losesInformation = true;
expr = mod.createUnary(UnaryOp.WrapI64, expr);
if (toType.isSmallInteger) {
if (toType.isSignedInteger) {
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
} else
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
}
}
// i32 to i64
} else if (toType.isLongInteger) {
if (toType.isSignedInteger)
expr = mod.createUnary(UnaryOp.ExtendI32, expr);
else
expr = mod.createUnary(UnaryOp.ExtendU32, expr);
// i32 to smaller/change of signage i32
} else if (toType.isSmallInteger && (fromType.size > toType.size || (fromType.size == toType.size && fromType.kind != toType.kind))) {
losesInformation = true;
if (toType.isSignedInteger) {
expr = mod.createBinary(BinaryOp.ShlI32, expr, mod.createI32(toType.smallIntegerShift));
expr = mod.createBinary(BinaryOp.ShrI32, expr, mod.createI32(toType.smallIntegerShift));
} else
expr = mod.createBinary(BinaryOp.AndI32, expr, mod.createI32(toType.smallIntegerMask));
}
}
return expr;
}
compileAssertionExpression(expression: AssertionExpression, contextualType: Type): BinaryenExpressionRef {
const toType: Type | null = this.resolveType(expression.toType); // reports
if (toType && toType != contextualType) {
const expr: BinaryenExpressionRef = this.compileExpression(expression.expression, <Type>toType);
return this.convertExpression(expr, this.currentType, <Type>toType);
}
return this.compileExpression(expression.expression, contextualType);
}
compileBinaryExpression(expression: BinaryExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileCallExpression(expression: CallExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileElementAccessExpression(expression: ElementAccessExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileIdentifierExpression(expression: IdentifierExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileLiteralExpression(expression: LiteralExpression, contextualType: Type): BinaryenExpressionRef {
switch (expression.literalKind) {
// case LiteralKind.ARRAY:
case LiteralKind.FLOAT:
if (contextualType == Type.f32)
return this.module.createF32((<FloatLiteralExpression>expression).value);
this.currentType = Type.f64;
return this.module.createF64((<FloatLiteralExpression>expression).value);
case LiteralKind.INTEGER:
if (contextualType == Type.bool)
return this.module.createI32((<IntegerLiteralExpression>expression).value.isOdd ? 1 : 0)
if (contextualType.isLongInteger)
return this.module.createI64((<IntegerLiteralExpression>expression).value.lo, (<IntegerLiteralExpression>expression).value.hi);
const value: i32 = (<IntegerLiteralExpression>expression).value.toI32();
if (contextualType.isSmallInteger)
return contextualType.isSignedInteger
? this.module.createI32(value << contextualType.smallIntegerShift >> contextualType.smallIntegerShift)
: this.module.createI32(value & contextualType.smallIntegerMask);
return this.module.createI32(value);
// case LiteralKind.OBJECT:
// case LiteralKind.REGEXP:
// case LiteralKind.STRING:
}
throw new Error("not implemented");
}
compileNewExpression(expression: NewExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): BinaryenExpressionRef {
return this.compileExpression(expression.expression, contextualType);
}
compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileSelectExpression(expression: SelectExpression, contextualType: Type): BinaryenExpressionRef {
const condition: BinaryenExpressionRef = this.compileExpression(expression.condition, Type.i32);
const ifThen: BinaryenExpressionRef = this.compileExpression(expression.ifThen, contextualType);
const ifElse: BinaryenExpressionRef = this.compileExpression(expression.ifElse, contextualType);
return this.module.createSelect(condition, ifThen, ifElse);
}
compileUnaryPostfixExpression(expression: UnaryPostfixExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
compileUnaryPrefixExpression(expression: UnaryPrefixExpression, contextualType: Type): BinaryenExpressionRef {
throw new Error("not implemented");
}
}

View File

@ -0,0 +1,100 @@
// code below is generated from diagnosticsMessages.json by scripts/build-diagnostics
export enum DiagnosticCode {
Conversion_from_type_0_to_1_requires_an_explicit_cast = 100,
Basic_type_0_cannot_be_nullable = 101,
Unterminated_string_literal = 1002,
Identifier_expected = 1003,
_0_expected = 1005,
A_file_cannot_have_a_reference_to_itself = 1006,
Trailing_comma_not_allowed = 1009,
Unexpected_token = 1012,
A_rest_parameter_must_be_last_in_a_parameter_list = 1014,
A_required_parameter_cannot_follow_an_optional_parameter = 1016,
Statements_are_not_allowed_in_ambient_contexts = 1036,
Initializers_are_not_allowed_in_ambient_contexts = 1039,
_0_modifier_cannot_be_used_here = 1042,
Type_parameters_cannot_appear_on_a_constructor_declaration = 1092,
Type_annotation_cannot_appear_on_a_constructor_declaration = 1093,
An_accessor_cannot_have_type_parameters = 1094,
A_set_accessor_cannot_have_a_return_type_annotation = 1095,
Type_parameter_list_cannot_be_empty = 1098,
A_return_statement_can_only_be_used_within_a_function_body = 1108,
Expression_expected = 1109,
Type_expected = 1110,
A_default_clause_cannot_appear_more_than_once_in_a_switch_statement = 1113,
Duplicate_label_0 = 1114,
Octal_literals_are_not_allowed_in_strict_mode = 1121,
Digit_expected = 1124,
Hexadecimal_digit_expected = 1125,
Unexpected_end_of_text = 1126,
Invalid_character = 1127,
_case_or_default_expected = 1130,
String_literal_expected = 1141,
Line_break_not_permitted_here = 1142,
Declaration_expected = 1146,
Unterminated_regular_expression_literal = 1161,
Binary_digit_expected = 1177,
Octal_digit_expected = 1178,
An_implementation_cannot_be_declared_in_ambient_contexts = 1183,
An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive = 1198,
Unterminated_Unicode_escape_sequence = 1199,
_abstract_modifier_can_only_appear_on_a_class_method_or_property_declaration = 1242,
Duplicate_identifier_0 = 2300,
Cannot_find_name_0 = 2304,
Generic_type_0_requires_1_type_argument_s = 2314,
Type_0_is_not_generic = 2315,
Type_0_is_not_assignable_to_type_1 = 2322,
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391
}
export function diagnosticCodeToString(code: DiagnosticCode): string {
switch (code) {
case 100: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
case 101: return "Basic type '{0}' cannot be nullable.";
case 1002: return "Unterminated string literal.";
case 1003: return "Identifier expected.";
case 1005: return "'{0}' expected.";
case 1006: return "A file cannot have a reference to itself.";
case 1009: return "Trailing comma not allowed.";
case 1012: return "Unexpected token.";
case 1014: return "A rest parameter must be last in a parameter list.";
case 1016: return "A required parameter cannot follow an optional parameter.";
case 1036: return "Statements are not allowed in ambient contexts.";
case 1039: return "Initializers are not allowed in ambient contexts.";
case 1042: return "'{0}' modifier cannot be used here.";
case 1092: return "Type parameters cannot appear on a constructor declaration.";
case 1093: return "Type annotation cannot appear on a constructor declaration.";
case 1094: return "An accessor cannot have type parameters.";
case 1095: return "A 'set' accessor cannot have a return type annotation.";
case 1098: return "Type parameter list cannot be empty.";
case 1108: return "A 'return' statement can only be used within a function body.";
case 1109: return "Expression expected.";
case 1110: return "Type expected.";
case 1113: return "A 'default' clause cannot appear more than once in a 'switch' statement.";
case 1114: return "Duplicate label '{0}'.";
case 1121: return "Octal literals are not allowed in strict mode.";
case 1124: return "Digit expected.";
case 1125: return "Hexadecimal digit expected.";
case 1126: return "Unexpected end of text.";
case 1127: return "Invalid character.";
case 1130: return "'case' or 'default' expected.";
case 1141: return "String literal expected.";
case 1142: return "Line break not permitted here.";
case 1146: return "Declaration expected.";
case 1161: return "Unterminated regular expression literal.";
case 1177: return "Binary digit expected.";
case 1178: return "Octal digit expected.";
case 1183: return "An implementation cannot be declared in ambient contexts.";
case 1198: return "An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.";
case 1199: return "Unterminated Unicode escape sequence.";
case 1242: return "'abstract' modifier can only appear on a class, method, or property declaration.";
case 2300: return "Duplicate identifier '{0}'.";
case 2304: return "Cannot find name '{0}'.";
case 2314: return "Generic type '{0}' requires {1} type argument(s).";
case 2315: return "Type '{0}' is not generic.";
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
case 2391: return "Function implementation is missing or not immediately following the declaration.";
default: return "";
}
}

View File

@ -0,0 +1,49 @@
{
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 100,
"Basic type '{0}' cannot be nullable.": 101,
"Unterminated string literal.": 1002,
"Identifier expected.": 1003,
"'{0}' expected.": 1005,
"A file cannot have a reference to itself.": 1006,
"Trailing comma not allowed.": 1009,
"Unexpected token.": 1012,
"A rest parameter must be last in a parameter list.": 1014,
"A required parameter cannot follow an optional parameter.": 1016,
"Statements are not allowed in ambient contexts.": 1036,
"Initializers are not allowed in ambient contexts.": 1039,
"'{0}' modifier cannot be used here.": 1042,
"Type parameters cannot appear on a constructor declaration.": 1092,
"Type annotation cannot appear on a constructor declaration.": 1093,
"An accessor cannot have type parameters.": 1094,
"A 'set' accessor cannot have a return type annotation.": 1095,
"Type parameter list cannot be empty.": 1098,
"A 'return' statement can only be used within a function body.": 1108,
"Expression expected.": 1109,
"Type expected.": 1110,
"A 'default' clause cannot appear more than once in a 'switch' statement.": 1113,
"Duplicate label '{0}'.": 1114,
"Octal literals are not allowed in strict mode.": 1121,
"Digit expected.": 1124,
"Hexadecimal digit expected.": 1125,
"Unexpected end of text.": 1126,
"Invalid character.": 1127,
"'case' or 'default' expected.": 1130,
"String literal expected.": 1141,
"Line break not permitted here.": 1142,
"Declaration expected.": 1146,
"Unterminated regular expression literal.": 1161,
"Binary digit expected.": 1177,
"Octal digit expected.": 1178,
"An implementation cannot be declared in ambient contexts.": 1183,
"An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.": 1198,
"Unterminated Unicode escape sequence.": 1199,
"'abstract' modifier can only appear on a class, method, or property declaration.": 1242,
"Duplicate identifier '{0}'.": 2300,
"Cannot find name '{0}'.": 2304,
"Generic type '{0}' requires {1} type argument(s).": 2314,
"Type '{0}' is not generic.": 2315,
"Type '{0}' is not assignable to type '{1}'.": 2322,
"Function implementation is missing or not immediately following the declaration.": 2391
}

167
src/diagnostics.ts Normal file
View File

@ -0,0 +1,167 @@
import { Range } from "./ast";
import { CharCode, isLineBreak, sb } from "./util";
import { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
export { DiagnosticCode, diagnosticCodeToString } from "./diagnosticMessages.generated";
export enum DiagnosticCategory {
INFO,
WARNING,
ERROR
}
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 "";
}
const colorBlue: string = "\u001b[93m";
const colorYellow: string = "\u001b[93m";
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 "";
}
export class DiagnosticMessage {
code: i32;
category: DiagnosticCategory;
message: string;
range: Range | null = null;
constructor(code: i32, category: DiagnosticCategory, message: string) {
this.code = code;
this.category = category;
this.message = message;
}
static create(code: DiagnosticCode, category: DiagnosticCategory, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
let message: string = diagnosticCodeToString(code);
if (arg0 != null)
message = message.replace("{0}", arg0);
if (arg1 != null)
message = message.replace("{1}", arg1);
return new DiagnosticMessage(code, category, message);
}
static createInfo(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
return DiagnosticMessage.create(code, DiagnosticCategory.INFO, arg0, arg1);
}
static createWarning(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
return DiagnosticMessage.create(code, DiagnosticCategory.WARNING, arg0, arg1);
}
static createError(code: DiagnosticCode, arg0: string | null = null, arg1: string | null = null): DiagnosticMessage {
return DiagnosticMessage.create(code, DiagnosticCategory.ERROR, arg0, arg1);
}
withRange(range: Range): this {
this.range = range;
return this;
}
}
export function formatDiagnosticMessage(message: DiagnosticMessage, useColors: bool = false, showContext: bool = false): string {
// format context first (uses same string builder)
let context: string = "";
if (message.range && showContext)
context = formatDiagnosticContext(message.range, useColors)
// general information
sb.length = 0;
if (useColors) sb.push(diagnosticCategoryToColor(message.category));
sb.push(diagnosticCategoryToString(message.category));
if (useColors) sb.push(colorReset);
sb.push(" AS");
sb.push(message.code.toString());
sb.push(": ");
sb.push(message.message);
// range information if available
if (message.range) {
const range: Range = message.range;
const text: string = range.source.text;
if (showContext) {
sb.push("\n");
sb.push(context);
}
sb.push("\n");
let pos: i32 = range.start;
let line: i32 = 1;
let column: i32 = 0;
while (pos-- > 0)
if (isLineBreak(text.charCodeAt(pos)))
line++;
else if (line == 1)
column++;
sb.push(" in ");
sb.push(range.source.path);
sb.push("(");
sb.push(line.toString());
sb.push(",");
sb.push(column.toString());
sb.push(")");
}
return sb.join("");
}
export function formatDiagnosticContext(range: Range, useColors: bool = false): string {
const text: string = range.source.text;
const len: i32 = text.length;
let start: i32 = range.start;
let end: i32 = range.end;
while (start > 0 && !isLineBreak(text.charCodeAt(start - 1)))
start--;
while (end < len && !isLineBreak(text.charCodeAt(end)))
end++;
sb.length = 0;
sb.push("\n ");
sb.push(text.substring(start, end));
sb.push("\n ");
while (start < range.start) {
sb.push(" ");
start++;
}
if (useColors) sb.push(colorRed);
if (range.start == range.end) {
sb.push("^");
} else while (start++ < range.end)
sb.push("~");
if (useColors) sb.push(colorReset);
return sb.join("");
}
export abstract class DiagnosticEmitter {
diagnostics: DiagnosticMessage[];
constructor(diagnostics: DiagnosticMessage[] | null = null) {
this.diagnostics = diagnostics ? <DiagnosticMessage[]>diagnostics : new Array();
}
emitDiagnostic(code: DiagnosticCode, category: DiagnosticCategory, range: Range, arg0: string | null = null, arg1: string | null = null) {
const message: DiagnosticMessage = DiagnosticMessage.create(code, category, arg0, arg1).withRange(range);
this.diagnostics.push(message);
console.log(formatDiagnosticMessage(message, true, true)); // temporary
}
error(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
this.emitDiagnostic(code, DiagnosticCategory.ERROR, range, arg0, arg1);
}
info(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
this.emitDiagnostic(code, DiagnosticCategory.INFO, range, arg0, arg1);
}
warning(code: DiagnosticCode, range: Range, arg0: string | null = null, arg1: string | null = null): void {
this.emitDiagnostic(code, DiagnosticCategory.WARNING, range, arg0, arg1);
}
}

16
src/glue/js.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
// Aliased AssemblyScript types. Beware of semantic differences.
declare type i8 = number;
declare type u8 = number;
declare type i16 = number;
declare type u16 = number;
declare type i32 = number;
declare type u32 = number;
declare type isize = number;
declare type usize = number;
declare type f32 = number;
declare type f64 = number;
declare type bool = boolean;
// Raw memory access (here: Binaryen memory, T=u8)
declare function store<T>(ptr: usize, val: u8): void;
declare function load<T>(ptr: usize): u8;

16
src/glue/js.ts Normal file
View File

@ -0,0 +1,16 @@
const globalScope = typeof window !== "undefined" && window
|| typeof global !== "undefined" && global
|| self;
globalScope["store"] = function store_u8(ptr, val) {
binaryen.HEAPU8[ptr] = val;
};
globalScope["load"] = function load_u8(ptr) {
return binaryen.HEAPU8[ptr];
};
const binaryen = require("binaryen");
for (const key in binaryen)
if (/^_(?:Binaryen|Relooper|malloc$|free$)/.test(key))
globalScope[key] = binaryen[key];

0
src/glue/wasm.ts Normal file
View File

58
src/index.ts Normal file
View File

@ -0,0 +1,58 @@
/*
Exports a C-like API to the embedder.
[obtain entrySource, entryPath]
parseFile(entrySource, entryPath) -> parser
while nextPath = nextFile(parser)
[obtain nextSource]
parseFile(nextSource, nextPath)
Checking for errors:
while diagnostic = nextDiagnostic(parser)
[print] formatDiagnostic(diagnostic, useColors?, showContext?)
if (isError(diagnostic))
[abort parsing afterwards]
compile(parser) -> module
*/
import { Module } from "./binaryen";
import { Compiler } from "./compiler";
import { DiagnosticMessage, DiagnosticCategory, DiagnosticCode } from "./diagnostics";
import { Parser } from "./parser";
import { Program } from "./program";
export function parseFile(text: string, path: string, parser: Parser | null = null): Parser {
let isEntry: bool = false;
if (!parser) {
parser = new Parser();
isEntry = true;
}
parser.parseFile(text, path, isEntry);
return parser;
}
export function nextFile(parser: Parser): string | null {
return parser.nextFile();
}
export function nextDiagnostic(parser: Parser): DiagnosticMessage | null {
const program: Program = parser.program;
if (program.diagnosticsOffset < program.diagnostics.length)
return program.diagnostics[program.diagnosticsOffset++];
return null;
}
export function isError(message: DiagnosticMessage): bool {
return message.category == DiagnosticCategory.ERROR;
}
export function compile(parser: Parser): Module {
const program: Program = parser.finish();
return Compiler.compile(program);
}
export { formatDiagnosticMessage as formatDiagnostic } from "./diagnostics";

1543
src/parser.ts Normal file

File diff suppressed because it is too large Load Diff

325
src/program.ts Normal file
View File

@ -0,0 +1,325 @@
import { Target } from "./compiler";
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
import { Source, Type, Class, Enum, Function, GlobalVariable, Namespace } from "./reflection";
import { hasModifier } from "./parser";
import { normalizePath, resolvePath } from "./util";
import {
Node,
NodeKind,
SourceNode,
ModifierKind,
ClassDeclaration,
DeclarationStatement,
EnumDeclaration,
EnumValueDeclaration,
FieldDeclaration,
FunctionDeclaration,
ImportDeclaration,
ImportStatement,
InterfaceDeclaration,
MethodDeclaration,
NamespaceDeclaration,
Statement,
VariableDeclaration,
VariableStatement
} from "./ast";
export class Program extends DiagnosticEmitter {
sources: Source[];
diagnosticsOffset: i32 = 0;
target: Target = Target.WASM32;
names: Map<string,DeclarationStatement> = new Map();
types: Map<string,Type> = new Map();
classes: Class[] = new Array();
enums: Enum[] = new Array();
functions: Function[] = new Array();
globals: GlobalVariable[] = new Array();
namespaces: Namespace[] = new Array();
constructor(diagnostics: DiagnosticMessage[] | null = null) {
super(diagnostics);
this.sources = new Array();
}
initialize(target: Target): void {
this.target = target;
initializeBasicTypes(this.types, target);
const importStatements: ImportStatement[] = new Array();
// build a lookup map of global names to declarations
for (let i: i32 = 0, k: i32 = this.sources.length; i < k; ++i) {
const source: Source = this.sources[i];
const statements: Statement[] = source.statements;
for (let j: i32 = 0, l: i32 = statements.length; j < l; ++j) {
const statement: Statement = statements[j];
switch (statement.kind) {
case NodeKind.CLASS:
this.initializeClass(<ClassDeclaration>statement);
break;
case NodeKind.ENUM:
this.initializeEnum(<EnumDeclaration>statement);
break;
case NodeKind.FUNCTION:
this.initializeFunction(<FunctionDeclaration>statement);
break;
case NodeKind.IMPORT:
this.initializeImports(<ImportStatement>statement);
importStatements.push(<ImportStatement>statement);
break;
case NodeKind.INTERFACE:
this.initializeInterface(<InterfaceDeclaration>statement);
break;
case NodeKind.NAMESPACE:
this.initializeNamespace(<NamespaceDeclaration>statement);
break;
case NodeKind.VARIABLE:
this.initializeVariables(<VariableStatement>statement);
break;
}
}
}
// resolve imports to their respective declarations
for (let i: i32 = 0, k: i32 = importStatements.length; i < k; ++i) {
const statement: ImportStatement = importStatements[i];
const importPath: string = resolvePath(normalizePath(statement.path), statement.range.source.normalizedPath);
const members: ImportDeclaration[] = statement.declarations;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j){
const declaration: ImportDeclaration = members[j];
const importedName: string = declaration.externalIdentifier.name;
for (let m: i32 = 0, n: i32 = this.sources.length; m < n; ++n) {
const source: Source = this.sources[m];
if (source.path == importPath) {
// TODO
}
}
}
}
}
initializeClass(declaration: ClassDeclaration): void {
this.addName(declaration);
const members: DeclarationStatement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
const statement: Statement = members[j];
switch (statement.kind) {
case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>statement);
break;
case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>statement);
break;
default:
throw new Error("unexpected class member");
}
}
}
initializeField(declaration: FieldDeclaration): void {
this.addName(declaration);
}
initializeEnum(declaration: EnumDeclaration): void {
this.addName(declaration);
const members: EnumValueDeclaration[] = declaration.members;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
this.initializeEnumValue(members[i]);
}
initializeEnumValue(declaration: EnumValueDeclaration): void {
this.addName(declaration);
}
initializeFunction(declaration: FunctionDeclaration): void {
this.addName(declaration);
}
initializeImports(statement: ImportStatement): void {
const members: ImportDeclaration[] = statement.declarations;
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
this.initializeImport(members[i]);
}
initializeImport(declaration: ImportDeclaration): void {
this.addName(declaration);
}
initializeInterface(declaration: InterfaceDeclaration): void {
this.addName(declaration);
const members: Statement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
const statement: Statement = members[j];
switch (statement.kind) {
case NodeKind.FIELD:
this.initializeField(<FieldDeclaration>statement);
break;
case NodeKind.METHOD:
this.initializeMethod(<MethodDeclaration>statement);
break;
default:
throw new Error("unexpected interface member");
}
}
}
initializeMethod(declaration: MethodDeclaration): void {
this.addName(declaration);
}
initializeNamespace(declaration: NamespaceDeclaration): void {
this.addName(declaration);
const members: Statement[] = declaration.members;
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
const statement: Statement = members[j];
switch (statement.kind) {
case NodeKind.CLASS:
this.initializeClass(<ClassDeclaration>statement);
break;
case NodeKind.ENUM:
this.initializeEnum(<EnumDeclaration>statement);
break;
case NodeKind.FUNCTION:
this.initializeFunction(<FunctionDeclaration>statement);
break;
case NodeKind.INTERFACE:
this.initializeInterface(<InterfaceDeclaration>statement);
break;
case NodeKind.NAMESPACE:
this.initializeNamespace(<NamespaceDeclaration>statement);
break;
case NodeKind.VARIABLE:
this.initializeVariables(<VariableStatement>statement);
break;
default:
throw new Error("unexpected namespace member");
}
}
}
initializeVariables(statement: VariableStatement): void {
const declarations: VariableDeclaration[] = statement.members;
for (let i: i32 = 0, k = declarations.length; i < k; ++i) {
const declaration: VariableDeclaration = declarations[i];
this.addName(declaration);
}
}
addName(declaration: DeclarationStatement): void {
const name: string = this.mangleName(declaration);
if (this.names.has(name))
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name); // recoverable
else
this.names.set(name, declaration);
}
mangleName(declaration: DeclarationStatement): string {
let name: string = declaration.identifier.name;
let parent: Node | null = declaration.parent;
if (parent) {
switch (parent.kind) {
case NodeKind.SOURCE:
return (<SourceNode>parent).path + "/" + name;
case NodeKind.CLASS: {
if (
(declaration.kind == NodeKind.FIELD && !hasModifier(ModifierKind.STATIC, (<FieldDeclaration>declaration).modifiers)) ||
(declaration.kind == NodeKind.METHOD && !hasModifier(ModifierKind.STATIC, (<MethodDeclaration>declaration).modifiers))
)
return this.mangleName(<DeclarationStatement>parent) + "#" + name;
// otherwise fall through
}
case NodeKind.ENUM:
case NodeKind.ENUMVALUE:
case NodeKind.NAMESPACE:
return this.mangleName(<DeclarationStatement>parent) + "." + name;
case NodeKind.IMPORT: {
const impParent: Node | null = (<ImportStatement>parent).parent;
if (impParent && impParent.kind == NodeKind.SOURCE)
return (<SourceNode>impParent).path + "/" + name;
break;
}
case NodeKind.VARIABLE: {
const varParent: Node | null = (<VariableStatement>parent).parent;
if (varParent) {
if (varParent.kind == NodeKind.SOURCE)
return <SourceNode>varParent == this.sources[0] ? name : (<SourceNode>varParent).path + "/" + name;
if (varParent.kind == NodeKind.NAMESPACE)
return this.mangleName(<DeclarationStatement>varParent) + "." + name;
}
break;
}
}
}
throw new Error("unexpected parent");
}
addClass(cl: Class): void {
cl.declaration.reflectionIndex = this.classes.length;
this.classes.push(cl);
}
addEnum(en: Enum): void {
en.declaration.reflectionIndex = this.enums.length;
this.enums.push(en);
}
addFunction(fn: Function): void {
fn.declaration.reflectionIndex = this.functions.length;
this.functions.push(fn);
}
addGlobal(gl: GlobalVariable): void {
gl.declaration.reflectionIndex = this.globals.length;
this.globals.push(gl);
}
addNamespace(ns: Namespace): void {
ns.declaration.reflectionIndex = this.namespaces.length;
this.namespaces.push(ns);
}
}
function initializeBasicTypes(types: Map<string,Type>, target: Target) {
types.set("i8", Type.i8);
types.set("i16", Type.i16);
types.set("i32", Type.i32);
types.set("i64", Type.i64);
types.set("isize", target == Target.WASM32 ? Type.isize32 : Type.isize64);
types.set("u8", Type.u8);
types.set("u16", Type.u16);
types.set("u32", Type.u32);
types.set("u64", Type.u64);
types.set("usize", target == Target.WASM32 ? Type.usize32 : Type.usize64);
types.set("bool", Type.bool);
types.set("void", Type.void);
}

310
src/reflection.ts Normal file
View File

@ -0,0 +1,310 @@
/*
Reflection objects are largely independent of their respective declarations in
order to make it easier to introduce internal objects.
Base
Class
Enum
Field
Function
Method
Import
Namespace
Source
Type ~ TypeKind
VariableBase
GlobalVariable
LocalVariable
*/
import {
ClassDeclaration,
DeclarationStatement,
EnumDeclaration,
Expression,
FunctionDeclaration,
ImportDeclaration,
ImportStatement,
MethodDeclaration,
ModifierKind,
NamespaceDeclaration,
Node,
FieldDeclaration,
SourceNode,
Statement,
NodeKind,
TypeParameter,
VariableDeclaration,
VariableStatement
} from "./ast";
import { DiagnosticMessage } from "./diagnostics";
import { Token, Tokenizer, Range } from "./tokenizer";
import { hasModifier } from "./parser";
import { normalizePath } from "./util";
export abstract class Base {
name: string;
exportName: string | null = null;
constructor(name: string) {
this.name = name;
}
get isExport(): bool { return this.exportName != null; }
exportAs(exportName: string): this {
this.exportName = exportName;
return this;
}
}
export const enum TypeKind {
I8,
I16,
I32,
I64,
ISIZE,
U8,
U16,
U32,
U64,
USIZE,
F32,
F64,
BOOL,
VOID
}
export function typeKindToString(kind: TypeKind): string {
switch (kind) {
case TypeKind.I8: return "i8";
case TypeKind.I16: return "i16";
case TypeKind.I32: return "i32";
case TypeKind.I64: return "i64";
case TypeKind.ISIZE: return "isize";
case TypeKind.U8: return "u8";
case TypeKind.U16: return "u16";
case TypeKind.U32: return "u32";
case TypeKind.U64: return "u64";
case TypeKind.USIZE: return "usize";
case TypeKind.F32: return "f32";
case TypeKind.F64: return "f64";
case TypeKind.BOOL: return "bool";
case TypeKind.VOID: return "void";
}
return "invalid";
}
export class Type extends Base {
kind: TypeKind;
size: i32;
constructor(kind: TypeKind, size: i32) {
super(typeKindToString(kind));
this.kind = kind;
this.size = size;
}
get bitSize(): i32 { return this.size << 3; }
get smallIntegerShift(): i32 { return 32 - (this.size << 3); }
get smallIntegerMask(): i32 { return -1 >>> 32 - (this.size << 3); }
get isAnyInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.USIZE; }
get isSmallInteger(): bool { return this.size == 1 || this.size == 2; }
get isLongInteger(): bool { return this.size == 8 && this.kind != TypeKind.F64; }
get isUnsignedInteger(): bool { return this.kind >= TypeKind.U8 && this.kind <= TypeKind.USIZE; }
get isSignedInteger(): bool { return this.kind >= TypeKind.I8 && this.kind <= TypeKind.ISIZE; }
get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; }
get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; }
toString(): string {
return typeKindToString(this.kind);
}
static readonly i8: Type = new Type(TypeKind.I8, 1);
static readonly i16: Type = new Type(TypeKind.I16, 2);
static readonly i32: Type = new Type(TypeKind.I32, 4);
static readonly i64: Type = new Type(TypeKind.I64, 8);
static readonly isize32: Type = new Type(TypeKind.I32, 4);
static readonly isize64: Type = new Type(TypeKind.I64, 8);
static readonly u8: Type = new Type(TypeKind.U8, 1);
static readonly u16: Type = new Type(TypeKind.U16, 2);
static readonly u32: Type = new Type(TypeKind.U32, 4);
static readonly u64: Type = new Type(TypeKind.U64, 8);
static readonly usize32: Type = new Type(TypeKind.U32, 4);
static readonly usize64: Type = new Type(TypeKind.U64, 8);
static readonly f32: Type = new Type(TypeKind.F32, 4);
static readonly f64: Type = new Type(TypeKind.F64, 8);
static readonly bool: Type = new Type(TypeKind.BOOL, 1);
static readonly void: Type = new Type(TypeKind.VOID, 0);
}
export class Source extends SourceNode {
text: string;
tokenizer: Tokenizer | null;
statements: Statement[];
isEntry: bool;
normalizedPath: string;
constructor(path: string, text: string, isEntry: bool = false) {
super();
this.range = new Range(this, 0, text.length);
this.path = path;
this.text = text;
this.statements = new Array();
this.isEntry = isEntry;
this.normalizedPath = normalizePath(path);
}
get isDeclaration(): bool { return !this.isEntry && this.path.endsWith(".d.ts"); }
}
export class Import extends Base {
declaration: ImportDeclaration | null;
externalName: string;
static create(declaration: ImportStatement): Import[] {
const count: i32 = declaration.declarations.length;
const imports: Import[] = new Array(count);
for (let i: i32 = 0; i < count; ++i) {
const decl: ImportDeclaration = declaration.declarations[i];
const imprt: Import = new Import(decl.identifier.name);
imprt.declaration = decl;
imprt.externalName = decl.externalIdentifier.name;
imports[i] = imprt;
}
return imports;
}
}
export abstract class Variable extends Base {
type: Type;
}
export class GlobalVariable extends Variable {
declaration: VariableDeclaration;
mutable: bool;
static create(declaration: VariableStatement): GlobalVariable[] {
const mutable: bool = hasModifier(ModifierKind.CONST, declaration.modifiers);
const count: i32 = declaration.members.length;
const variables: GlobalVariable[] = new Array(count);
for (let i: i32 = 0; i < count; ++i) {
const decl: VariableDeclaration = declaration.members[i];
const variable: GlobalVariable = new GlobalVariable(decl.identifier.name);
variable.declaration = decl;
variable.mutable = mutable;
variables[i] = variable;
}
return variables;
}
}
export class LocalVariable extends Variable {
declaration: VariableDeclaration;
index: i32;
static create(declaration: VariableStatement, index: i32): LocalVariable[] {
const count: i32 = declaration.members.length;
const variables: LocalVariable[] = new Array(count);
for (let i: i32 = 0; i < count; ++i) {
const decl: VariableDeclaration = declaration.members[i];
const variable: LocalVariable = new LocalVariable(decl.identifier.name);
variable.declaration = decl;
variable.index = index;
variables[i] = variable;
}
return variables;
}
}
export class Namespace extends Base {
declaration: NamespaceDeclaration;
members: Base[];
static create(declaration: NamespaceDeclaration): Namespace {
const ns: Namespace = new Namespace(declaration.identifier.name);
ns.declaration = declaration;
const members: Base[] = ns.members = new Array();
// TODO: insert members
return ns;
}
}
export class Enum extends Base {
declaration: EnumDeclaration;
isConst: bool;
values: Map<string,Expression | null> = new Map();
static create(declaration: EnumDeclaration): Enum {
const enm: Enum = new Enum(declaration.identifier.name);
enm.declaration = declaration;
enm.isConst = hasModifier(ModifierKind.CONST, declaration.modifiers);
// TODO: insert values
return enm;
}
}
export class Function extends Base {
declaration: FunctionDeclaration;
typeArguments: Type[];
returnType: Type;
statements: Statement[];
static create(declaration: FunctionDeclaration, typeArguments: Type[]): Function {
throw new Error("not implemented");
}
}
export class Class extends Base {
declaration: ClassDeclaration;
typeArguments: Type[];
baseClass: Class | null;
memberNames: Set<string>;
methods: Map<string,Method>;
fields: Map<string,Field>;
static create(declaration: ClassDeclaration, typeArguments: Type[]): Class {
const clazz: Class = new Class(declaration.identifier.name);
clazz.typeArguments = typeArguments;
return clazz;
}
}
export class Method extends Function {
declaration: MethodDeclaration; // more specific
isInstance: bool;
static create(declaration: MethodDeclaration, typeArguments: Type[]): Method {
throw new Error("not implemented");
}
}
export class Field extends Base {
declaration: FieldDeclaration | null;
type: Type;
offset: i32;
initializer: Expression | null;
static create(declaration: FieldDeclaration, offset: i32): Field {
const field: Field = new Field(declaration.identifier.name);
field.declaration = declaration;
field.offset = offset;
field.initializer = declaration.initializer;
return field;
}
}

1158
src/tokenizer.ts Normal file

File diff suppressed because one or more lines are too long

33
src/tsconfig.json Normal file
View File

@ -0,0 +1,33 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": [
"dom",
"es6"
],
"types": [
"node"
],
"strictNullChecks": true,
"alwaysStrict": true,
"outDir": "../out"
},
"files": [
"ast.ts",
"binaryen.d.ts",
"binaryen.ts",
"compiler.ts",
"diagnosticMessages.generated.ts",
"diagnostics.ts",
"glue/js.d.ts",
"glue/js.ts",
"index.ts",
"parser.ts",
"program.ts",
"reflection.ts",
"tokenizer.ts",
"util.ts",
"util/i64.ts"
]
}

228
src/util.ts Normal file
View File

@ -0,0 +1,228 @@
export { I64, U64 } from "./util/i64";
export const enum CharCode {
NULL = 0,
LINEFEED = 0x0A,
CARRIAGERETURN = 0x0D,
LINESEPARATOR = 0x2028,
PARAGRAPHSEPARATOR = 0x2029,
NEXTLINE = 0x0085,
SPACE = 0x20,
NONBREAKINGSPACE = 0xA0,
ENQUAD = 0x2000,
EMQUAD = 0x2001,
ENSPACE = 0x2002,
EMSPACE = 0x2003,
THREEPEREMSPACE = 0x2004,
FOURPEREMSPACE = 0x2005,
SIXPEREMSPACE = 0x2006,
FIGURESPACE = 0x2007,
PUNCTUATIONSPACE = 0x2008,
THINSPACE = 0x2009,
HAIRSPACE = 0x200A,
ZEROWIDTHSPACE = 0x200B,
NARRINOBREAKSPACE = 0x202F,
IDEOGRAPHICSPACE = 0x3000,
MATHEMATICALSPACE = 0x205F,
OGHAM = 0x1680,
_ = 0x5F,
$ = 0x24,
_0 = 0x30,
_1 = 0x31,
_2 = 0x32,
_3 = 0x33,
_4 = 0x34,
_5 = 0x35,
_6 = 0x36,
_7 = 0x37,
_8 = 0x38,
_9 = 0x39,
a = 0x61,
b = 0x62,
c = 0x63,
d = 0x64,
e = 0x65,
f = 0x66,
g = 0x67,
h = 0x68,
i = 0x69,
j = 0x6A,
k = 0x6B,
l = 0x6C,
m = 0x6D,
n = 0x6E,
o = 0x6F,
p = 0x70,
q = 0x71,
r = 0x72,
s = 0x73,
t = 0x74,
u = 0x75,
v = 0x76,
w = 0x77,
x = 0x78,
y = 0x79,
z = 0x7A,
A = 0x41,
B = 0x42,
C = 0x43,
D = 0x44,
E = 0x45,
F = 0x46,
G = 0x47,
H = 0x48,
I = 0x49,
J = 0x4A,
K = 0x4B,
L = 0x4C,
M = 0x4D,
N = 0x4E,
O = 0x4F,
P = 0x50,
Q = 0x51,
R = 0x52,
S = 0x53,
T = 0x54,
U = 0x55,
V = 0x56,
W = 0x57,
X = 0x58,
Y = 0x59,
Z = 0x5a,
AMPERSAND = 0x26,
ASTERISK = 0x2A,
AT = 0x40,
BACKSLASH = 0x5C,
BACKTICK = 0x60,
BAR = 0x7C,
CARET = 0x5E,
CLOSEBRACE = 0x7D,
CLOSEBRACKET = 0x5D,
CLOSEPAREN = 0x29,
COLON = 0x3A,
COMMA = 0x2C,
DOT = 0x2E,
DOUBLEQUOTE = 0x22,
EQUALS = 0x3D,
EXCLAMATION = 0x21,
GREATERTHAN = 0x3E,
HASH = 0x23,
LESSTHAN = 0x3C,
MINUS = 0x2D,
OPENBRACE = 0x7B,
OPENBRACKET = 0x5B,
OPENPAREN = 0x28,
PERCENT = 0x25,
PLUS = 0x2B,
QUESTION = 0x3F,
SEMICOLON = 0x3B,
SINGLEQUOTE = 0x27,
SLASH = 0x2F,
TILDE = 0x7E,
BACKSPACE = 0x08,
FORMFEED = 0x0C,
BYTEORDERMARK = 0xFEFF,
TAB = 0x09,
VERTICALTAB = 0x0B
}
export function isLineBreak(c: i32): bool {
return c == CharCode.LINEFEED
|| c == CharCode.CARRIAGERETURN
|| c == CharCode.LINESEPARATOR
|| c == CharCode.PARAGRAPHSEPARATOR;
}
export const sb: string[] = new Array(256); // shared string builder. 64-bit without growing: (4+4+8) + 8*256 = 16b + 2kb
export function normalizePath(path: string, separator: CharCode = CharCode.SLASH): string {
// expects a relative path
let pos: i32 = 0;
let len: i32 = path.length;
// trim leading './'
while (pos + 1 < len && path.charCodeAt(pos) == CharCode.DOT && path.charCodeAt(pos + 1) == separator)
pos += 2;
if (pos > 0) {
path = path.substring(pos);
len -= pos;
pos = 0;
}
let atEnd: bool;
while (pos + 1 < len) {
atEnd = false;
// we are only interested in '/.' sequences ...
if (path.charCodeAt(pos) == separator && path.charCodeAt(pos + 1) == CharCode.DOT) {
// '/.' ( '/' | $ )
if (
(atEnd = pos + 2 == len)
||
pos + 2 < len && path.charCodeAt(pos + 2) == separator
) {
path = atEnd
? path.substring(0, pos)
: path.substring(0, pos) + path.substring(pos + 2);
len -= 2;
continue;
}
// '/.' ( './' | '.' $ )
if (
(atEnd = pos + 3 == len) && path.charCodeAt(pos + 2) == CharCode.DOT
||
pos + 3 < len && path.charCodeAt(pos + 2) == CharCode.DOT && path.charCodeAt(pos + 3) == separator
) {
// find preceeding '/'
let ipos: i32 = pos;
while (--ipos >= 0) {
if (path.charCodeAt(ipos) == separator) {
if (pos - ipos != 3 || path.charCodeAt(ipos + 1) != CharCode.DOT || path.charCodeAt(ipos + 2) != CharCode.DOT) { // exclude '..' itself
path = atEnd
? path.substring(0, ipos)
: path.substring(0, ipos) + path.substring(pos + 3);
len -= pos + 3 - ipos;
pos = ipos - 1; // incremented again at end of loop
}
break;
}
}
// if there's no preceeding '/', trim start if non-empty
if (ipos < 0 && pos > 0) {
if (pos != 2 || path.charCodeAt(0) != CharCode.DOT || path.charCodeAt(1) != CharCode.DOT) { // exclude '..' itself
path = path.substring(pos + 4);
len = path.length;
continue;
}
}
}
}
pos++;
}
return len > 0 ? path : ".";
}
export function dirname(normalizedPath: string, separator: CharCode = CharCode.SLASH): string {
let pos: i32 = normalizedPath.length;
while (--pos > 0)
if (normalizedPath.charCodeAt(pos) == separator)
return normalizedPath.substring(0, pos);
return ".";
}
export function resolvePath(normalizedPath: string, normalizedOrigin: string, separator: CharCode = CharCode.SLASH): string {
return normalizePath(dirname(normalizedOrigin, separator) + String.fromCharCode(separator) + normalizedPath);
}

520
src/util/i64.ts Normal file
View File

@ -0,0 +1,520 @@
/*
To remain compatible with TSC / compiling to JS, we have to emulate I64s in a
portable way. The following is based on long.js with the main difference being
that instances are mutable and operations affect 'this'. In our scenario,
that's useful because it's mostly used for constant evaluation and we are
exclusively interested in the result (saves a heap of allocations).
*/
// TODO: div/mod
const I64_MIN_LO: i32 = 0;
const I64_MIN_HI: i32 = 0x80000000 | 0;
export class I64 {
lo: i32;
hi: i32;
static fromI32(n: i32): I64 {
return new I64(n, n < 0 ? -1 : 0);
}
constructor(lo: i32 = 0, hi: i32 = 0) {
this.lo = lo;
this.hi = hi;
}
get isZero(): bool {
return this.lo == 0 && this.hi == 0;
}
get isPositive(): bool {
return this.hi >= 0;
}
get isNegative(): bool {
return this.hi < 0;
}
get isOdd(): bool {
return (this.lo & 1) == 1;
}
get isEven(): bool {
return (this.lo & 1) == 0;
}
toI32(): i32 {
return this.lo;
}
eq(other: I64): bool {
return this.eq32(other.lo, other.hi);
}
eq32(lo: i32, hi: i32 = 0): bool {
return this.lo == lo && this.hi == hi;
}
ne(other: I64): bool {
return this.ne32(other.lo, other.hi);
}
ne32(lo: i32, hi: i32 = 0): bool {
return this.lo != lo || this.hi != hi;
}
neg(): void {
this.lo = ~this.lo;
this.hi = ~this.hi;
this.add32(1, 0);
}
add(other: I64): void {
this.add32(other.lo, other.hi);
}
add32(lo: i32, hi: i32 = 0): void {
i64_add_internal(this.lo, this.hi, lo, hi);
this.lo = i64_lo;
this.hi = i64_hi;
}
sub(other: I64): void {
this.sub32(other.lo, other.hi);
}
sub32(lo: i32, hi: i32 = 0): void {
i64_add_internal(~lo, ~hi, 1, 0);
this.add32(i64_lo, i64_hi);
}
comp(other: I64): i32 {
return this.comp32(other.lo, other.hi);
}
comp32(lo: i32, hi: i32 = 0): i32 {
if (this.lo == lo && this.hi == hi)
return 0;
if (this.hi < 0 && hi >= 0)
return -1;
if (this.hi >= 0 && hi < 0)
return 1;
i64_add_internal(~lo, ~hi, 1, 0);
i64_add_internal(this.lo, this.hi, i64_lo, i64_hi);
return i64_hi < 0 ? -1 : 1;
}
lt(other: I64): bool {
return this.lt32(other.lo, other.hi);
}
lt32(lo: i32, hi: i32 = 0): bool {
return this.comp32(lo, hi) < 0;
}
lte(other: I64): bool {
return this.lte32(other.lo, other.hi);
}
lte32(lo: i32, hi: i32 = 0): bool {
return this.comp32(lo, hi) <= 0;
}
gt(other: I64): bool {
return this.gt32(other.lo, other.hi);
}
gt32(lo: i32, hi: i32 = 0): bool {
return this.comp32(lo, hi) > 0;
}
gte(other: I64): bool {
return this.gte32(other.lo, other.hi);
}
gte32(lo: i32, hi: i32 = 0): bool {
return this.comp32(lo, hi) >= 0;
}
mul(other: I64): void {
this.mul32(other.lo, other.hi);
}
mul32(lo: i32, hi: i32 = 0): void {
if (this.lo == 0 && this.hi == 0)
return;
if (lo == 0 && hi == 0) {
this.lo = 0;
this.hi = 0;
return;
}
// this == MIN
if (this.lo == I64_MIN_LO && this.hi == I64_MIN_HI) {
this.lo = 0; // == MIN_LO
this.hi = lo & 1 ? I64_MIN_HI : 0; // other.isOdd ? this = MIN : this = ZERO
return;
}
// other == MIN
if (lo == I64_MIN_LO && hi == I64_MIN_HI) {
this.hi = this.lo & 1 ? I64_MIN_HI : 0; // this.isOdd ? this = MIN : this = ZERO
this.lo = 0; // == MIN_LO
return;
}
if (this.hi < 0) {
this.neg();
// both negative: negate both and multiply
if (hi < 0) {
i64_add_internal(~lo, ~hi, 1, 0);
i64_mul_internal(this.lo, this.hi, i64_lo, i64_hi);
this.lo = i64_lo;
this.hi = i64_hi;
// this negative: negate this, multiply and negate result
} else {
i64_mul_internal(this.lo, this.hi, lo, hi);
this.lo = i64_lo;
this.hi = i64_hi;
this.neg();
}
return;
// other negative: negate other, multiply and negate result
} else if (hi < 0) {
i64_add_internal(~lo, ~hi, 1, 0);
i64_mul_internal(this.lo, this.hi, i64_lo, i64_hi);
this.lo = i64_lo;
this.hi = i64_hi;
this.neg();
return;
}
// both positive
i64_mul_internal(this.lo, this.hi, lo, hi);
this.lo = i64_lo;
this.hi = i64_hi;
}
div(other: I64): void {
this.div32(other.lo, other.hi);
}
div32(lo: i32, hi: i32 = 0): void {
// other == 0
if (lo == 0 && hi == 0)
throw new Error("division by zero");
// this == 0
if (this.lo == 0 && this.hi == 0)
return;
// this == MIN
if (this.lo == I64_MIN_LO && this.hi == I64_MIN_HI) {
// other == 1 or -1
if (lo == 1 && hi == 0 || lo == -1 && hi == -1) // -MIN == MIN
return;
// both == MIN
if (lo == I64_MIN_LO && hi == I64_MIN_HI) {
this.lo = 1;
this.hi = 0;
return;
}
// |other| >= 2, so |this/other| < |MIN_VALUE|
let tempLo: i32 = this.lo;
let tempHi: i32 = this.hi;
this.shr32(1, 0);
this.div32(lo, hi);
this.shl32(1, 0);
if (this.lo == 0 && this.hi == 0) {
if (hi < 0) {
this.lo = 1;
this.hi = 0;
} else {
this.lo = -1;
this.hi = -1;
}
return;
}
i64_mul_internal(lo, hi, this.lo, this.hi);
this.lo = tempLo;
this.hi = tempHi;
tempLo = i64_lo;
tempHi = i64_hi;
this.div32(lo, hi);
this.sub32(i64_lo, i64_hi);
i64_add_internal(tempLo, tempHi, this.lo, this.hi);
this.lo = i64_lo;
this.hi = i64_hi;
return;
}
if (this.hi < 0) {
this.neg();
// both negative: negate both and divide
if (hi < 0) {
i64_add_internal(~lo, ~hi, 1, 0);
i64_div_internal(this.lo, this.hi, i64_lo, i64_hi);
this.lo = i64_lo;
this.hi = i64_hi;
// this negative: negate this, divide and negate result
} else {
i64_div_internal(this.lo, this.hi, lo, hi);
this.lo = i64_lo;
this.hi = i64_hi;
this.neg();
}
return;
// other negative: negate other, divide and negate result
} else if (hi < 0) {
i64_add_internal(~lo, ~hi, 1, 0);
i64_div_internal(this.lo, this.hi, i64_lo, i64_hi);
this.lo = i64_lo;
this.hi = i64_hi;
this.neg();
return;
}
// both positive
i64_div_internal(this.lo, this.hi, lo, hi);
this.lo = i64_lo;
this.hi = i64_hi;
}
mod(other: I64): void {
this.mod32(other.lo, other.hi);
}
mod32(lo: i32, hi: i32 = 0): void {
const thisLo: i32 = this.lo;
const thisHi: i32 = this.hi;
this.div32(lo, hi);
this.mul32(lo, hi);
const resLo: i32 = this.lo;
const resHi: i32 = this.hi;
this.lo = thisLo;
this.hi = thisHi;
this.sub32(resLo, resHi);
}
not(): void {
this.lo = ~this.lo;
this.hi = ~this.hi;
}
and(other: I64): void {
this.and32(other.lo, other.hi);
}
and32(lo: i32, hi: i32 = 0): void {
this.lo &= lo;
this.hi &= hi;
}
or(other: I64): void {
this.or32(other.lo, other.hi);
}
or32(lo: i32, hi: i32 = 0): void {
this.lo |= lo;
this.hi |= hi;
}
xor(other: I64): void {
this.xor32(other.lo, other.hi);
}
xor32(lo: i32, hi: i32 = 0): void {
this.lo ^= lo;
this.hi ^= hi;
}
shl(other: I64): void {
this.shl32(other.lo, other.hi);
}
shl32(lo: i32, hi: i32 = 0): void {
if ((lo &= 63) == 0)
return;
if (lo < 32) {
this.hi = (this.hi << lo) | (this.lo >>> (32 - lo));
this.lo = this.lo << lo;
} else {
this.hi = this.lo << (lo - 32);
this.lo = 0;
}
}
shr(other: I64): void {
this.shr32(other.lo, other.hi);
}
shr32(lo: i32, hi: i32 = 0): void {
if ((lo &= 63) == 0)
return;
if (lo < 32) {
this.lo = (this.lo >>> lo) | (this.hi << (32 - lo));
this.hi = this.hi >> lo;
} else {
this.lo = this.hi >> (lo - 32);
this.hi = this.hi >= 0 ? 0 : -1;
}
}
shru(other: I64): void {
this.shru32(other.lo, other.hi);
}
shru32(lo: i32, hi: i32 = 0): void {
if ((lo &= 63) == 0)
return;
if (lo < 32) {
this.lo = (this.lo >>> lo) | (this.hi << (32 - lo));
this.hi = (this.hi >>> lo) | 0;
} else if (lo == 32) {
this.lo = this.hi;
this.hi = 0;
} else {
this.lo = (this.hi >>> (lo - 32)) | 0;
this.hi = 0;
}
}
clone(): I64 {
return new I64(this.lo, this.hi);
}
toString(): string {
let negative: bool = false;
if (this.hi < 0) {
i64_add_internal(~this.lo, ~this.hi, 1, 0);
negative = true;
} else {
i64_lo = this.lo;
i64_hi = this.hi;
}
if (i64_hi) {
let lo: string = (i64_lo as u32 >>> 0).toString(16);
while (lo.length < 8)
lo = "0" + lo;
return (negative ? "-0x" : "0x") + (i64_hi as u32 >>> 0).toString(16) + lo;
}
return negative ? "-" + i64_lo.toString() : i64_lo.toString();
}
}
let i64_lo: i32 = 0;
let i64_hi: i32 = 0;
function i64_add_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
let a48: i32 = hi >>> 16;
let a32: i32 = hi & 0xFFFF;
let a16: i32 = lo >>> 16;
let a00: i32 = lo & 0xFFFF;
let b48: i32 = otherHi >>> 16;
let b32: i32 = otherHi & 0xFFFF;
let b16: i32 = otherLo >>> 16;
let b00: i32 = otherLo & 0xFFFF;
let c48: i32 = 0, c32: i32 = 0, c16: i32 = 0, c00: i32 = 0;
c00 += a00 + b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 + b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 + b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 + b48;
c48 &= 0xFFFF;
i64_lo = (c16 << 16) | c00;
i64_hi = (c48 << 16) | c32;
}
function i64_mul_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
let a48: i32 = hi >>> 16;
let a32: i32 = hi & 0xFFFF;
let a16: i32 = lo >>> 16;
let a00: i32 = lo & 0xFFFF;
let b48: i32 = otherHi >>> 16;
let b32: i32 = otherHi & 0xFFFF;
let b16: i32 = otherLo >>> 16;
let b00: i32 = otherLo & 0xFFFF;
var c48 = 0, c32 = 0, c16 = 0, c00 = 0;
c00 += a00 * b00;
c16 += c00 >>> 16;
c00 &= 0xFFFF;
c16 += a16 * b00;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c16 += a00 * b16;
c32 += c16 >>> 16;
c16 &= 0xFFFF;
c32 += a32 * b00;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a16 * b16;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c32 += a00 * b32;
c48 += c32 >>> 16;
c32 &= 0xFFFF;
c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
c48 &= 0xFFFF;
i64_lo = (c16 << 16) | c00;
i64_hi = (c48 << 16) | c32;
}
function i64_div_internal(lo: i32, hi: i32, otherLo: i32, otherHi: i32): void {
throw new Error("not implemented");
}
export class U64 extends I64 {
static fromI32(n: i32): U64 {
return new U64(n, 0);
}
get isPositive(): bool {
return true;
}
get isNegative(): bool {
return false;
}
comp32(lo: i32, hi: i32): i32 {
// uses both a cast and a js-like shift for portability
return ((hi as u32 >>> 0) > (this.hi as u32 >>> 0)) || (hi == this.hi && (lo as u32 >>> 0) > (this.lo as u32 >>> 0)) ? -1 : 1;
}
neg(): void {
this.lo = ~this.lo;
this.hi = ~this.hi;
this.add32(1, 0);
}
clone(): U64 {
return new U64(this.lo, this.hi);
}
}

56
tests/binaryen.ts Normal file
View File

@ -0,0 +1,56 @@
/// <reference path="../src/binaryen.d.ts" />
import "../src/glue/js";
import { Type, Module, MemorySegment, BinaryOp } from "../src/binaryen";
import { Target } from "../src/compiler";
import { U64 } from "../src/util";
const mod = Module.create();
mod.setMemory(1, Module.MAX_MEMORY_WASM32, [
MemorySegment.create(new Uint8Array(4), U64.fromI32(8)),
MemorySegment.create(new Uint8Array(4), U64.fromI32(12))
], Target.WASM32, "memory");
const add = mod.addFunctionType("iii", Type.I32, [ Type.I32, Type.I32 ]);
mod.addFunction("add", add, [], mod.createReturn(
mod.createBinary(BinaryOp.AddI32,
mod.createGetLocal(0, Type.I32),
mod.createGetLocal(1, Type.I32)
)
));
mod.addExport("add", "add");
const lit = mod.addFunctionType("I", Type.I64, []);
mod.addFunction("lit", lit, [], mod.createReturn(
mod.createI64(0, 0x80000000) // I64_MIN
));
mod.addExport("lit", "lit");
mod.addGlobal("42", Type.I32, false, mod.createI32(42));
const aSwitch = mod.addFunctionType("i", Type.I32, []);
mod.addFunction("aSwitch", aSwitch, [], mod.createBlock("", [
mod.createBlock("a", [
mod.createBlock("b", [
mod.createBlock("c", [
mod.createBlock("d", [
mod.createSwitch(
["a", "b", "c"],
"d",
mod.createI32(4)
)
]),
mod.createReturn(mod.createI32(3))
]),
mod.createReturn(mod.createI32(2))
]),
mod.createReturn(mod.createI32(1))
]),
mod.createReturn(mod.createI32(0))
]));
mod.addExport("aSwitch", "aSwitch");
// mod.optimize();
if (mod.validate())
_BinaryenModulePrint(mod.ref);
_BinaryenModuleDispose(mod.ref);

37
tests/i64.ts Normal file
View File

@ -0,0 +1,37 @@
import { I64 } from "../src/util/i64";
import * as Long from "long";
import * as assert from "assert";
function test(fn, lo, hi, otherLo, otherHi) {
let expected = Long.fromBits(lo, hi)[fn](Long.fromBits(otherLo, otherHi));
let actual = new I64(lo, hi); actual[fn + "32"](otherLo, otherHi);
assert.equal(actual.lo, expected.low, fn + " lo ");
assert.equal(actual.hi, expected.high, fn + " hi");
}
function rand() {
let r = Math.random();
// 10% edge cases
if (r < 0.05) return 0x80000000 | 0;
else if (r < 0.1) return 0;
return (Math.random() * 0xffffffff) | 0;
}
let i = 0;
while (i++ < 1000000) {
let lo = rand();
let hi = rand();
let otherLo = rand();
let otherHi = rand();
// console.log(lo, hi, otherLo, otherHi);
test("add", lo, hi, otherLo, otherHi);
test("sub", lo, hi, otherLo, otherHi);
test("mul", lo, hi, otherLo, otherHi);
test("shl", lo, hi, otherLo, otherHi);
test("shr", lo, hi, otherLo, otherHi);
test("shru", lo, hi, otherLo, otherHi);
test("and", lo, hi, otherLo, otherHi);
test("or", lo, hi, otherLo, otherHi);
test("xor", lo, hi, otherLo, otherHi);
}
console.log("done");

View File

@ -0,0 +1,12 @@
export class Test<T> {
instanceFunction(): void {
}
static staticFunction(): void {
}
get instanceGetter(): i32 {
}
static set staticSetter(v: i32): i32 {
}
instanceField: i32;
static staticField: i32;
}

View File

@ -0,0 +1,10 @@
export const enum A {
B = 1,
C,
D = 3
}
enum E {
F,
G = 1 + 2,
H = 3 * 4
}

View File

@ -0,0 +1,4 @@
function simple(): void {
}
function typeparams<T, V extends T>(a: V | null = null): void {
}

View File

@ -0,0 +1,171 @@
// incrementing precedence
a
=
b
?
X
:
c
||
d
&&
e
|
f
^
g
&
h
==
i
<
j
<<
k
+
l
/
++
m
++
;
// decrementing precedence
++
a
++
/
b
+
c
<<
d
<
e
==
f
&
g
^
h
|
i
&&
j
||
k
?
X
:
y
=
l
;
// assignment (right-to-left)
a
=
b
+=
c
-=
d
**=
e
*=
f
/=
g
%=
h
<<=
i
>>=
j
>>>=
k
&=
l
^=
m
|=
n
;
// equality (left-to-right)
a
==
b
!=
c
===
d
!==
e
;
// relational (left-to-right)
a
<
b
<=
c
>
d
>=
e
;
// bitwise shift (left-to-right)
a
<<
b
>>
c
>>>
d
;
// addition (left-to-right)
a
+
b
-
c
;
// exponentiation (right-to-left), multiplication (left-to-right)
a
**
b
*
c
/
d
%
e
**
f
*
g
;
// prefix (right-to-left)
delete
void
typeof
--
++
-
+
~
!
a
;
// parenthesized
(
a
+
(
b
/
c
)
-
d
)
*
e
;

46
tests/parser/index.ts Normal file
View File

@ -0,0 +1,46 @@
import * as fs from "fs";
import * as diff from "diff";
import * as chalk from "chalk";
import "../../src/glue/js";
import { NodeKind, ExpressionStatement } from "../../src/ast";
import { Parser } from "../../src/parser";
const files = fs.readdirSync(__dirname + "/fixtures");
files.forEach(filename => {
if (filename.charAt(0) == "_") return;
const isTree = filename.indexOf(".tree.") >= 0;
const parser = new Parser();
const text = fs.readFileSync(__dirname + "/fixtures/" + filename, { encoding: "utf8" }).replace(/\r?\n/g, "\n").replace(/^\/\/.*\r?\n/mg, "");
parser.parseFile(text, filename, true);
var sb: string[] = [];
if (isTree) {
const statements = parser.program.sources[0].statements;
statements.forEach(stmt => {
if (stmt.kind != NodeKind.EXPRESSION) return;
(<ExpressionStatement>stmt).expression.serializeAsTree(sb, 0);
sb.push(";\n");
});
} else
parser.program.sources[0].serialize(sb);
const actual = sb.join("");
const expected = isTree ? text : text.replace(/^\s+/mg, "");
const diffs = diff.diffLines(expected, actual);
let changed = false;
diffs.forEach(part => {
if (part.added || part.removed)
changed = true;
});
if (changed) { // print it
console.log("Differences in " + filename + ":");
diffs.forEach(part => {
if (part.added || part.removed)
changed = true;
process.stderr.write((part.added ? chalk.green : part.removed ? chalk.red : chalk.grey)(part.value));
});
} else {
console.log("No differences in " + filename + ".");
}
// parser.program.initialize();
// console.log(parser.program.names);
});

8
tests/path.ts Normal file
View File

@ -0,0 +1,8 @@
import { normalizePath, resolvePath } from "../src/util";
import * as path from "path";
let test = "./Y/./N/./N/../../././../Y/./.";
console.log(normalizePath(test));
console.log(path.posix.normalize(test));
console.log(resolvePath("../../..", "lib/util/i64.ts"));

23
tests/tokenizer.ts Normal file
View File

@ -0,0 +1,23 @@
import "../src/glue/js";
import { Tokenizer, Token } from "../src/tokenizer";
import { Source } from "../src/reflection";
import * as fs from "fs";
const text = fs.readFileSync(__dirname + "/../src/tokenizer.ts").toString();
const tn = new Tokenizer(new Source("tokenizer.ts", text));
let token;
do {
token = tn.next();
let range = tn.range();
console.log(Token[token] + " -> " + range.source.text.substring(range.start, range.end));
if (token == Token.IDENTIFIER)
console.log("> " + tn.readIdentifier());
else if (token == Token.INTEGERLITERAL)
console.log("> " + tn.readInteger());
else if (token == Token.FLOATLITERAL)
console.log("> " + tn.readFloat());
else if (token == Token.STRINGLITERAL)
console.log("> " + tn.readString());
} while (token != Token.ENDOFFILE);