mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-12 22:41:27 +00:00
Initial commit
This commit is contained in:
1785
src/ast.ts
Normal file
1785
src/ast.ts
Normal file
File diff suppressed because it is too large
Load Diff
252
src/binaryen.d.ts
vendored
Normal file
252
src/binaryen.d.ts
vendored
Normal 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
585
src/binaryen.ts
Normal 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
653
src/compiler.ts
Normal 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");
|
||||
}
|
||||
}
|
100
src/diagnosticMessages.generated.ts
Normal file
100
src/diagnosticMessages.generated.ts
Normal 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 "";
|
||||
}
|
||||
}
|
49
src/diagnosticMessages.json
Normal file
49
src/diagnosticMessages.json
Normal 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
167
src/diagnostics.ts
Normal 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
16
src/glue/js.d.ts
vendored
Normal 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
16
src/glue/js.ts
Normal 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
0
src/glue/wasm.ts
Normal file
58
src/index.ts
Normal file
58
src/index.ts
Normal 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
1543
src/parser.ts
Normal file
File diff suppressed because it is too large
Load Diff
325
src/program.ts
Normal file
325
src/program.ts
Normal 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
310
src/reflection.ts
Normal 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
1158
src/tokenizer.ts
Normal file
File diff suppressed because one or more lines are too long
33
src/tsconfig.json
Normal file
33
src/tsconfig.json
Normal 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
228
src/util.ts
Normal 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
520
src/util/i64.ts
Normal 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user