TypeScript Native Compiler
Build
What's new
- CPU supported list:
Added
ARM
andAArch64
- Literal Type Widening
function main()
{
let a: 10 = 11; // error
let b = 11; // ok
b = 10; // ok
print("done.");
}
- Shared libraries:
shared.ts
- shared library file:
export const val_num = 2.5;
export const val_str = "Hello World! - val";
export function test1()
{
print("Hello World! test 1");
}
export function test2()
{
print("Hello World! test 2");
}
- Build option
--emit=exe
intsc
to generate executable file
tsc --opt --emit=exe <file>.ts
Planning
- Migrating to LLVM 16.0.6
- Shared libraries
- JavaScript Built-in classes library
Demo
Try it
Chat Room
Want to chat with other members of the TypeScriptCompiler community?
Example
abstract class Department {
constructor(public name: string) {}
printName(): void {
print("Department name: " + this.name);
}
abstract printMeeting(): void; // must be implemented in derived classes
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // constructors in derived classes must call super()
}
printMeeting(): void {
print("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
print("Generating accounting reports...");
}
}
function main() {
let department: Department; // ok to create a reference to an abstract type
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
//department.generateReports(); // error: department is not of type AccountingDepartment, cannot access generateReports
}
Run
tsc --emit=jit --opt --shared-libs=TypeScriptRuntime.dll example.ts
Result
Department name: Accounting and Auditing
The Accounting Department meets each Monday at 10am.
Run as JIT
- with Garbage collection
tsc --emit=jit --opt --shared-libs=TypeScriptRuntime.dll hello.ts
- without Garbage collection
tsc --emit=jit --nogc hello.ts
File hello.ts
function main() {
print("Hello World!");
}
Result
Hello World!
Compile as Binary Executable
On Windows
File tsc-compile.bat
set FILENAME=%1
set GC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\gc\msbuild\x64\release\Release
set LLVM_LIB_PATH=C:\dev\TypeScriptCompiler\__build\llvm\msbuild\x64\release\Release\lib
set TSC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\lib
set TSCEXEPATH=C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\bin
%TSCEXEPATH%\tsc.exe --opt --emit=exe %FILENAME%.ts
Compile
tsc-compile.bat hello
Run
hello.exe
Result
Hello World!
On Linux (Ubuntu 20.04 and 22.04)
File tsc-compile.sh
FILENAME=$1
export TSC_LIB_PATH=~/dev/TypeScriptCompiler/__build/tsc/linux-ninja-gcc-release/lib
export LLVM_LIB_PATH=~/dev/TypeScriptCompiler/3rdParty/llvm/release/lib
export GC_LIB_PATH=~/dev/TypeScriptCompiler/3rdParty/gc/release
TSCEXEPATH=~/dev/TypeScriptCompiler/__build/tsc/linux-ninja-gcc-release/bin
$TSCEXEPATH/tsc --emit=exe $FILENAME.ts --relocation-model=pic
Compile
sh -f tsc-compile.sh hello
Run
./hello
Result
Hello World!
Compiling as WASM
On Windows
File tsc-compile-wasm.bat
set FILENAME=%1
set GC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\gc\msbuild\x64\release\Release
set LLVM_LIB_PATH=C:\dev\TypeScriptCompiler\__build\llvm\msbuild\x64\release\Release\lib
set TSC_LIB_PATH=C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\lib
C:\dev\TypeScriptCompiler\__build\tsc\windows-msbuild-release\bin\tsc.exe --emit=exe --nogc -mtriple=wasm32-unknown-unknown %FILENAME%.ts
Compile
tsc-compile-wasm.bat hello
Run run.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<script type="module">
let buffer;
let buffer32;
let buffer64;
let bufferF64;
let heap;
let heap_base, heap_end, stack_low, stack_high;
const allocated = [];
const allocatedSize = (addr) => {
return allocated["" + addr];
};
const setAllocatedSize = (addr, newSize) => {
allocated["" + addr] = newSize;
};
const expand = (addr, newSize) => {
const end = addr + allocatedSize(addr);
const newEnd = addr + newSize;
for (const allocatedAddr in allocated)
{
const beginAllocatedAddr = parseInt(allocatedAddr);
const endAllocatedAddr = beginAllocatedAddr + allocated[allocatedAddr];
if (beginAllocatedAddr != addr && addr < endAllocatedAddr && newEnd > beginAllocatedAddr)
{
return false;
}
}
setAllocatedSize(addr, newSize);
if (addr + newSize > heap) heap = addr + newSize;
return true;
};
const endOf = (addr) => { while (buffer[addr] != 0) { addr++; if (addr > heap_end) throw "out of memory boundary"; }; return addr; };
const strOf = (addr) => String.fromCharCode(...buffer.slice(addr, endOf(addr)));
const copyStr = (dst, src) => { while (buffer[src] != 0) buffer[dst++] = buffer[src++]; buffer[dst] = 0; return dst; };
const ncopy = (dst, src, count) => { while (count-- > 0) buffer[dst++] = buffer[src++]; return dst; };
const append = (dst, src) => copyStr(endOf(dst), src);
const cmp = (addrL, addrR) => { while (buffer[addrL] != 0) { if (buffer[addrL] != buffer[addrR]) break; addrL++; addrR++; } return buffer[addrL] - buffer[addrR]; };
const prn = (str, addr) => { for (let i = 0; i < str.length; i++) buffer[addr++] = str.charCodeAt(i); buffer[addr] = 0; return addr; };
const clear = (addr, size, val) => { for (let i = 0; i < size; i++) buffer[addr++] = val; };
const alloc = (size) => { if ((heap + size) > heap_end) throw "out of memory"; setAllocatedSize(heap, size); const heapCurrent = heap; heap += size; return heapCurrent; };
const free = (addr) => delete allocated["" + addr];
const realloc = (addr, size) => {
if (!expand(addr, size)) {
const newAddr = alloc(size);
ncopy(newAddr, addr, allocatedSize(addr));
free(addr);
return newAddr;
}
return addr;
}
const envObj = {
memory: new WebAssembly.Memory({ initial: 256 }),
table: new WebAssembly.Table({
initial: 0,
element: 'anyfunc',
}),
fmod: (arg1, arg2) => arg1 % arg2,
sqrt: (arg1) => Math.sqrt(arg1),
floor: (arg1) => Math.floor(arg1),
pow: (arg1, arg2) => Math.pow(arg1, arg2),
fabs: (arg1) => Math.abs(arg1),
_assert: (msg, file, line) => console.assert(false, strOf(msg), "| file:", strOf(file), "| line:", line, " DBG:", path),
puts: (arg) => output += strOf(arg) + '\n',
strcpy: copyStr,
strcat: append,
strcmp: cmp,
strlen: (addr) => endOf(addr) - addr,
malloc: alloc,
realloc: realloc,
free: free,
memset: (addr, size, val) => clear(addr, size, val),
atoi: (addr, rdx) => parseInt(strOf(addr), rdx),
atof: (addr) => parseFloat(strOf(addr)),
sprintf_s: (addr, sizeOfBuffer, format, ...args) => {
const formatStr = strOf(format);
switch (formatStr) {
case "%d": prn(buffer32[args[0] >> 2].toString(), addr); break;
case "%g": prn(bufferF64[args[0] >> 3].toString(), addr); break;
case "%llu": prn(buffer64[args[0] >> 3].toString(), addr); break;
default: return 1;
}
return 0;
},
}
const config = {
env: envObj,
};
WebAssembly.instantiateStreaming(fetch("./hello.wasm"), config)
.then(results => {
const { main, __wasm_call_ctors, __heap_base, __heap_end, __stack_low, __stack_high } = results.instance.exports;
buffer = new Uint8Array(results.instance.exports.memory.buffer);
buffer32 = new Uint32Array(results.instance.exports.memory.buffer);
buffer64 = new BigUint64Array(results.instance.exports.memory.buffer);
bufferF64 = new Float64Array(results.instance.exports.memory.buffer);
heap = heap_base = __heap_base, heap_end = __heap_end, stack_low = __stack_low, stack_high = __stack_high;
try
{
if (__wasm_call_ctors) __wasm_call_ctors();
main();
}
catch (e)
{
console.error(e);
}
});
</script>
</body>
</html>
Build
On Windows
Requirements:
Visual Studio 2022
- 512GB of free space on disk
First, precompile dependencies
cd TypeScriptCompiler
prepare_3rdParty.bat
To build TSC
binaries:
cd TypeScriptCompiler\tsc
config_tsc_release.bat
build_tsc_release.bat
On Linux (Ubuntu 20.04 and 22.04)
Requirements:
GCC
orClang
- 512GB of free space on disk
- sudo apt-get install
libtinfo-dev
First, precompile dependencies
chmod +x *.sh
cd ~/TypeScriptCompiler
./prepare_3rdParty.sh
To build TSC
binaries:
cd ~/TypeScriptCompiler/tsc
chmod +x *.sh
./config_tsc_release.sh
./build_tsc_release.sh