Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
*.lib
*.pdb
*.log
__jit/
__jit/
__fs_test/
27 changes: 25 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ A test that reads standard input (`console.readLine`) gets it from `tests/<name>
## Docs

- [fetch / Headers / Response](docs/fetch.md) — built-in HTTP client
- [process / fs](docs/process-fs.md) — the command line, the environment and the file system

## Implemented classes & functions

Expand All @@ -145,7 +146,7 @@ Globals (`src/lib.ts`):
- `Date`
- `RegExp`, `MatchResults`, `MatchIndicesResults`
- `String`: `String.fromCharCode` (each code written as UTF-8, a surrogate pair as one
character), plus the `string` prototype methods it wraps: `at`, `charAt`, `charCodeAt`,
character), `String.fromCodePoint` (each code point as UTF-8; outside 0 to 0x10FFFF a `RangeError`), plus the `string` prototype methods it wraps: `at`, `charAt`, `charCodeAt`,
`codePointAt`, `concat`, `endsWith`, `includes`, `indexOf`, `lastIndexOf`,
`localeCompare`, `match`, `matchAll`, `normalize`, `padEnd`, `padStart`, `repeat`,
`replace`, `replaceAll`, `search`, `slice`, `split`, `startsWith`, `substring`,
Expand Down Expand Up @@ -192,7 +193,10 @@ Generics (`src/generics/lib.generics.ts`):
`Int16Array`, `Uint16Array`, `Int32Array`, `Uint32Array`, `BigInt64Array`,
`BigUint64Array`, `Float32Array`, `Float64Array`). A typed array is made from an array of
its elements (`new Uint8Array([1, 2, 3])`) or a length (`new Uint8Array(4096)`, each element 0)
- `Map<K, V>`
- `T[]` and `Array<T>` also have `flatMap`, `toSpliced` (`splice` on a copy), and `T[][]` has
`flat()`, which takes away one level only: a depth would make the result's type depend on a number
- `Map<K, V>`, and `Map.groupBy(items, keySelector)`, a `Map` of each key to its items, the keys in
the order they first come (not under `-mm=own`, whose Map values cannot be arrays)
- `Set<V>`
- `Atomics`: `add`, `sub`, `and`, `or`, `xor`, `exchange`, `compareExchange`, `load`,
`store`, `isLockFree` over the integer typed arrays, and `wait`/`notify` over an
Expand Down Expand Up @@ -290,6 +294,25 @@ Generics (`src/generics/lib.generics.ts`):
const text = new TextDecoder().decode(Stdio.readAll());
Stdio.write(new TextEncoder().encode(text.toUpperCase()));
```
- `process`, as Node's, with functions where Node has properties: `argv()`, `getEnv(name)` (or
`undefined`), `setEnv`, `unsetEnv`, `env()` (`[name, value]` pairs), `cwd()`, `chdir`, `pid()`, `platform()`,
`exit(code)`. See [docs/process-fs.md](docs/process-fs.md).
- `fs`, synchronous, by Node's names: `existsSync`, `statSync`, `readFileSync` (a `Uint8Array`),
`readTextFileSync` (a string), `writeFileSync`, `writeTextFileSync`, `appendFileSync`,
`appendTextFileSync`, `readdirSync` (sorted), `mkdirSync` (`{ recursive }`), `rmSync`
(`{ recursive, force }`), `rmdirSync`, `renameSync`, `copyFileSync`. An error is an `Error` with
Node's message, `"ENOENT: no such file or directory, open 'a.txt'"`. See
[docs/process-fs.md](docs/process-fs.md).

```ts
const args = process.argv();
const name = args[args.length - 1];
if (!fs.existsSync(name)) {
console.error(`${name}: not found`);
process.exit(1);
}
fs.writeTextFileSync(name + ".upper", fs.readTextFileSync(name).toUpperCase());
```

Errors (`src/core/core.d.ts`):

Expand Down
103 changes: 103 additions & 0 deletions docs/process-fs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# process / fs

The command line, the environment, the working directory and the file system, synchronously. Both
are namespaces in `src/generics/lib.generics.ts` over the C++ wrapper `src/wrappers/os.cpp`.

## process

Node's `process`, with functions where Node has properties (a namespace in the library holds no
state of its own, so `argv` and `env` are read when they are asked for):

```ts
namespace process {
function argv(): string[]; // Node: process.argv
function getEnv(name: string): string | undefined; // Node: process.env[name]
function setEnv(name: string, value: string): void;
function unsetEnv(name: string): void; // Node: delete process.env[name]
function env(): [string, string][]; // every variable, [name, value]
function cwd(): string;
function chdir(directory: string): void;
function pid(): int;
function platform(): string; // "win32", "linux", "android", "darwin"
function exit(code?: int): void;
}
```

- `argv()[0]` is the program. A compiled program's arguments follow it. Under the JIT the program
is `tslang`, so its options come next, then the script, then the script's own arguments - the way
`node script.js a b` gives `["node", "script.js", "a", "b"]`. On Linux and Android the arguments
are read from `/proc/self/cmdline`, on Windows from `GetCommandLineW`.
- `setEnv` and `unsetEnv` change the environment of this process and of the processes it starts.
A name that is empty or has an `=` in it is an `Error` (`EINVAL`). Unsetting a variable that is
not set is no error. On Windows the variables are read and written through the Win32 API, which
every C runtime in the process shares (under the JIT this library has a C runtime of its own),
and names are case-insensitive there, as always on Windows.
- `exit(code)` flushes every C stream and ends the process: nothing after it runs, no `finally`
block either. Under the JIT on Windows it flushes this library's streams (`console.log`,
`Stdio`), not the host's, so text from `print()` not yet flushed may be lost there.

## fs

```ts
namespace fs {
function existsSync(path: string): boolean;
function statSync(path: string): FileStats; // { isFile, isDirectory, size, mtimeMs }
function readFileSync(path: string): Uint8Array;
function readTextFileSync(path: string): string;
function writeFileSync(path: string, data: Uint8Array): void;
function writeTextFileSync(path: string, text: string): void;
function appendFileSync(path: string, data: Uint8Array): void;
function appendTextFileSync(path: string, text: string): void;
function readdirSync(path: string): string[];
function mkdirSync(path: string, options?: { recursive?: boolean }): void;
function rmSync(path: string, options?: { recursive?: boolean, force?: boolean }): void;
function rmdirSync(path: string): void;
function renameSync(oldPath: string, newPath: string): void;
function copyFileSync(src: string, dest: string): void;
}
```

- The names are Node's. Node's `readFileSync(path, "utf8")` and its `writeFileSync` that takes a
string are `readTextFileSync` and `writeTextFileSync` here (Deno's names), since a function here
has one result type: `readFileSync` gives bytes, `writeFileSync` takes them.
- Text is UTF-8, and a string ends at its first 0, so a file that is not text is read with
`readFileSync`. Paths are UTF-8 on every platform; on Windows they go through the wide API, so a
name outside the ANSI code page works.
- `statSync` follows symbolic links. `size` is in bytes; `mtimeMs` is the last modification in
milliseconds since 1970, as `new Date(ms)` takes it (to the second on Windows).
- `readdirSync` gives the names in the directory, without `.` and `..`, sorted: the operating system
gives them in no set order.
- `mkdirSync` without `recursive` needs the parent, and a directory already there is `EEXIST`; with
it every missing parent is made too and a directory already there is no error.
- `rmSync` removes a file; a directory only with `recursive`, and then all it holds (`EISDIR`
without). With `force` a path that is not there is no error. `rmdirSync` removes an empty
directory.
- `renameSync` and `copyFileSync` replace a file already at the destination.
- Files of 2 GB or more are an `EFBIG` error: lengths are an `int`, as `Stdio`'s are.

An error is an `Error` whose message is Node's: the code, what it means, the call and the path.

```ts
try {
fs.readTextFileSync("settings.json");
} catch (e: Error) {
console.log(e.message); // ENOENT: no such file or directory, open 'settings.json'
}
```

The code is one of `ENOENT`, `EEXIST`, `EACCES`, `EPERM`, `ENOTDIR`, `EISDIR`, `ENOTEMPTY`, `EBUSY`,
`EINVAL`, `ENAMETOOLONG`, `EXDEV`, `EFBIG`, `ENOSPC`, `EROFS`, `EMFILE`, or `EIO` for any other; what
it means is the C runtime's wording, so its letter case differs between platforms.

## Example

```ts
function main() {
const args = process.argv();
const dir = args.length > 1 ? args[args.length - 1] : process.cwd();
for (const name of fs.readdirSync(dir)) {
const stats = fs.statSync(dir + "/" + name);
console.log(stats.isDirectory ? `${name}/` : `${name} ${stats.size}`);
}
}
```
5 changes: 3 additions & 2 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ $TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/regex.cpp -o $OUTPUT/$LIB_OUT/reg
$TOOL $DBG_GCC $CPP_FLAGS $WRAPPER_MM_DEF -c $SRC/src/wrappers/thread.cpp -o $OUTPUT/$LIB_OUT/thread.o
$TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/http_linux.cpp -o $OUTPUT/$LIB_OUT/http_linux.o
$TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/memory.cpp -o $OUTPUT/$LIB_OUT/memory.o
$TOOL $DBG_GCC $CPP_FLAGS -c $SRC/src/wrappers/os.cpp -o $OUTPUT/$LIB_OUT/os.o

$BIN_PATH/$TOOL_NAME $DBG_OPTS $MM_OPT --emit=obj --export=none --nowarn --no-default-lib $SRC/src/lib.linux.ts $PIC -o $OUTPUT/$LIB_OUT/lib.linux.o

# Build Lib
$BIN_PATH/$TOOL_NAME $DBG_OPTS $MM_OPT --emit=obj --export=none --nowarn --no-default-lib $SRC/src/lib.ts $PIC -o $OUTPUT/$LIB_OUT/lib.o
$ARC rcs $OUTPUT/$LIB_OUT/libTypeScriptDefaultLibCore.a $OUTPUT/$LIB_OUT/lib.o $OUTPUT/$LIB_OUT/lib.linux.o $OUTPUT/$LIB_OUT/io.o $OUTPUT/$LIB_OUT/datetime.o $OUTPUT/$LIB_OUT/regex.o $OUTPUT/$LIB_OUT/thread.o $OUTPUT/$LIB_OUT/http_linux.o $OUTPUT/$LIB_OUT/memory.o
$ARC rcs $OUTPUT/$LIB_OUT/libTypeScriptDefaultLibCore.a $OUTPUT/$LIB_OUT/lib.o $OUTPUT/$LIB_OUT/lib.linux.o $OUTPUT/$LIB_OUT/io.o $OUTPUT/$LIB_OUT/datetime.o $OUTPUT/$LIB_OUT/regex.o $OUTPUT/$LIB_OUT/thread.o $OUTPUT/$LIB_OUT/http_linux.o $OUTPUT/$LIB_OUT/memory.o $OUTPUT/$LIB_OUT/os.o

# A static archive cannot record its own dependencies on ELF (there is no ld.bfd equivalent of
# the `#pragma comment(lib, ...)` http.cpp uses on Windows), so programs linking
Expand All @@ -110,7 +111,7 @@ EOF
# undefined on purpose: they must bind to the one collector already in the process
# (libTypeScriptRuntime.so under the JIT, libgc.a in an exe); linking libgc.a in here would
# give the library a second, separate heap.
g++ -shared $DBG_GCC $OUTPUT/$LIB_OUT/lib.o $OUTPUT/$LIB_OUT/lib.linux.o $OUTPUT/$LIB_OUT/io.o $OUTPUT/$LIB_OUT/datetime.o $OUTPUT/$LIB_OUT/regex.o $OUTPUT/$LIB_OUT/thread.o $OUTPUT/$LIB_OUT/http_linux.o $OUTPUT/$LIB_OUT/memory.o -lcurl -lm -o $OUTPUT/$DLL_OUT/libTypeScriptDefaultLib.so
g++ -shared $DBG_GCC $OUTPUT/$LIB_OUT/lib.o $OUTPUT/$LIB_OUT/lib.linux.o $OUTPUT/$LIB_OUT/io.o $OUTPUT/$LIB_OUT/datetime.o $OUTPUT/$LIB_OUT/regex.o $OUTPUT/$LIB_OUT/thread.o $OUTPUT/$LIB_OUT/http_linux.o $OUTPUT/$LIB_OUT/memory.o $OUTPUT/$LIB_OUT/os.o -lcurl -lm -o $OUTPUT/$DLL_OUT/libTypeScriptDefaultLib.so

# Copy
# Stage into a single shared defaultlib tree with per-target, per-build and per-model subfolders
Expand Down
4 changes: 2 additions & 2 deletions scripts/build_android.bat
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ md "%OBJ%" "%LIB_OUT%"
rem thread.cpp registers the threads it starts with the collector, which only the gc build links
set WRAPPER_MM_DEF=
if "%MM%"=="gc" set WRAPPER_MM_DEF=-DTSLANG_MM_GC
for %%w in (io datetime regex thread http_stub memory) do (
for %%w in (io datetime regex thread http_stub memory os) do (
%CXX% %WRAPPER_MM_DEF% -c "%ROOT%\src\wrappers\%%w.cpp" -o "%OBJ%\%%w.o" || exit /b 1
)

%TSC% "%ROOT%\src\lib.linux.ts" -o "%OBJ%\lib.linux.o" || exit /b 1
%TSC% "%ROOT%\src\lib.ts" -o "%OBJ%\lib.o" || exit /b 1

set OBJS="%OBJ%\lib.o" "%OBJ%\lib.linux.o" "%OBJ%\io.o" "%OBJ%\datetime.o" "%OBJ%\regex.o" "%OBJ%\thread.o" "%OBJ%\http_stub.o" "%OBJ%\memory.o"
set OBJS="%OBJ%\lib.o" "%OBJ%\lib.linux.o" "%OBJ%\io.o" "%OBJ%\datetime.o" "%OBJ%\regex.o" "%OBJ%\thread.o" "%OBJ%\http_stub.o" "%OBJ%\memory.o" "%OBJ%\os.o"
%AR% rcs "%LIB_OUT%\libTypeScriptDefaultLib.a" %OBJS% || exit /b 1

rd /s /q "%OBJ%"
Expand Down
6 changes: 4 additions & 2 deletions scripts/build_core.bat
Original file line number Diff line number Diff line change
Expand Up @@ -225,22 +225,23 @@ rem Build native wrappers for C++ code
%TSLANG_CC% %DBG_CL% /EHsc /Wall %WRAPPER_MM_DEF% /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\thread.cpp
%TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\http.cpp
%TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\memory.cpp
%TSLANG_CC% %DBG_CL% /EHsc /Wall /c /Fo%OUTPUT%\%LIB_OUT%\ %SRC%\src\wrappers\os.cpp

rem Build OS-specific Lib
echo Build OS-specific Lib
%TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=obj --export=none --nowarn --no-default-lib %SRC%\src\lib.win32.ts -o %OUTPUT%\%LIB_OUT%\lib.win32.obj

rem Build DLL
echo Build DLL
%TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=dll --gc-lib-path=%GC_SHARED_LIB_PATH% %TSLANG_RUNTIME_OPT% --embed-declarations=false --nowarn --no-default-lib %SRC%\src\lib.ts --obj=%OUTPUT%\%LIB_OUT%\lib.win32.obj --obj=%OUTPUT%\%LIB_OUT%\io.obj --obj=%OUTPUT%\%LIB_OUT%\datetime.obj --obj=%OUTPUT%\%LIB_OUT%\regex.obj --obj=%OUTPUT%\%LIB_OUT%\thread.obj --obj=%OUTPUT%\%LIB_OUT%\http.obj --obj=%OUTPUT%\%LIB_OUT%\memory.obj -o %OUTPUT%\%DLL_OUT%\TypeScriptDefaultLib.dll
%TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=dll --gc-lib-path=%GC_SHARED_LIB_PATH% %TSLANG_RUNTIME_OPT% --embed-declarations=false --nowarn --no-default-lib %SRC%\src\lib.ts --obj=%OUTPUT%\%LIB_OUT%\lib.win32.obj --obj=%OUTPUT%\%LIB_OUT%\io.obj --obj=%OUTPUT%\%LIB_OUT%\datetime.obj --obj=%OUTPUT%\%LIB_OUT%\regex.obj --obj=%OUTPUT%\%LIB_OUT%\thread.obj --obj=%OUTPUT%\%LIB_OUT%\http.obj --obj=%OUTPUT%\%LIB_OUT%\memory.obj --obj=%OUTPUT%\%LIB_OUT%\os.obj -o %OUTPUT%\%DLL_OUT%\TypeScriptDefaultLib.dll

rem Build Lib
echo Build Lib
%TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=obj --export=none --nowarn --no-default-lib %SRC%\src\lib.ts -o %OUTPUT%\%LIB_OUT%\lib.obj
rem %TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=llvm --export=none %SRC%\src\lib.ts -o %OUTPUT%\%LIB_OUT%\lib.ll
rem %TOOL_PATH%\%TOOL_NAME%.exe %TRIPLE_OPT% %DBG% %MM_OPT% --emit=mlir --export=none %SRC%\src\lib.ts 2> %OUTPUT%\%LIB_OUT%\lib.mlir

%TSLANG_AR% /out:%OUTPUT%\%LIB_OUT%\TypeScriptDefaultLib.lib %OUTPUT%\%LIB_OUT%\lib.obj %OUTPUT%\%LIB_OUT%\lib.win32.obj %OUTPUT%\%LIB_OUT%\io.obj %OUTPUT%\%LIB_OUT%\datetime.obj %OUTPUT%\%LIB_OUT%\regex.obj %OUTPUT%\%LIB_OUT%\thread.obj %OUTPUT%\%LIB_OUT%\http.obj %OUTPUT%\%LIB_OUT%\memory.obj
%TSLANG_AR% /out:%OUTPUT%\%LIB_OUT%\TypeScriptDefaultLib.lib %OUTPUT%\%LIB_OUT%\lib.obj %OUTPUT%\%LIB_OUT%\lib.win32.obj %OUTPUT%\%LIB_OUT%\io.obj %OUTPUT%\%LIB_OUT%\datetime.obj %OUTPUT%\%LIB_OUT%\regex.obj %OUTPUT%\%LIB_OUT%\thread.obj %OUTPUT%\%LIB_OUT%\http.obj %OUTPUT%\%LIB_OUT%\memory.obj %OUTPUT%\%LIB_OUT%\os.obj

del %OUTPUT%\%LIB_OUT%\lib.obj
del %OUTPUT%\%LIB_OUT%\lib.win32.obj
Expand All @@ -250,6 +251,7 @@ del %OUTPUT%\%LIB_OUT%\regex.obj
del %OUTPUT%\%LIB_OUT%\thread.obj
del %OUTPUT%\%LIB_OUT%\http.obj
del %OUTPUT%\%LIB_OUT%\memory.obj
del %OUTPUT%\%LIB_OUT%\os.obj

rem Stage into a single shared defaultlib tree with per-target, per-build, per-model subfolders
rem under dll\ and lib\. Only the current build's subfolders are refreshed so the other
Expand Down
Loading
Loading