goscript 0.0.83 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/cmd/goscript/cmd_compile.go +70 -69
- package/cmd/goscript/cmd_compile_test.go +79 -0
- package/cmd/goscript/main.go +10 -5
- package/compiler/compile-request.go +218 -0
- package/compiler/compiler.go +16 -1336
- package/compiler/compliance_test.go +196 -0
- package/compiler/config.go +6 -13
- package/compiler/diagnostic.go +70 -0
- package/compiler/index.test.ts +28 -28
- package/compiler/index.ts +40 -72
- package/compiler/lowered-program.go +132 -0
- package/compiler/lowering.go +3576 -0
- package/compiler/override-registry.go +422 -0
- package/compiler/override-registry_test.go +207 -0
- package/compiler/package-graph.go +231 -0
- package/compiler/package-graph_test.go +281 -0
- package/compiler/result.go +13 -0
- package/compiler/runtime-contract.go +279 -0
- package/compiler/runtime-contract_test.go +90 -0
- package/compiler/semantic-model-types.go +110 -0
- package/compiler/semantic-model.go +922 -0
- package/compiler/semantic-model_test.go +416 -0
- package/compiler/service.go +133 -0
- package/compiler/skeleton_test.go +1145 -0
- package/compiler/typescript-emitter.go +663 -0
- package/compiler/wasm/compile.go +2 -3
- package/compiler/wasm/compile_test.go +29 -0
- package/compiler/wasm_api.go +10 -159
- package/dist/compiler/index.d.ts +1 -3
- package/dist/compiler/index.js +31 -55
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/builtin.d.ts +13 -0
- package/dist/gs/builtin/builtin.js +27 -7
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/channel.d.ts +3 -3
- package/dist/gs/builtin/channel.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +86 -0
- package/dist/gs/builtin/hostio.js +266 -0
- package/dist/gs/builtin/hostio.js.map +1 -0
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +1 -0
- package/dist/gs/builtin/index.js.map +1 -1
- package/dist/gs/builtin/print.d.ts +8 -0
- package/dist/gs/builtin/print.js +111 -0
- package/dist/gs/builtin/print.js.map +1 -0
- package/dist/gs/builtin/slice.d.ts +1 -1
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +11 -0
- package/dist/gs/builtin/type.js +55 -1
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/crypto/rand/index.d.ts +5 -0
- package/dist/gs/crypto/rand/index.js +77 -0
- package/dist/gs/crypto/rand/index.js.map +1 -0
- package/dist/gs/encoding/json/index.d.ts +3 -0
- package/dist/gs/encoding/json/index.js +160 -0
- package/dist/gs/encoding/json/index.js.map +1 -0
- package/dist/gs/fmt/fmt.js +2 -22
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/browser.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/stack.js.map +1 -1
- package/dist/gs/go/scanner/index.d.ts +29 -0
- package/dist/gs/go/scanner/index.js +120 -0
- package/dist/gs/go/scanner/index.js.map +1 -0
- package/dist/gs/go/token/index.d.ts +31 -0
- package/dist/gs/go/token/index.js +82 -0
- package/dist/gs/go/token/index.js.map +1 -0
- package/dist/gs/internal/abi/index.js.map +1 -1
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/readfile.js.map +1 -1
- package/dist/gs/io/fs/stat.js.map +1 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +2 -4
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/rawconn_js.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/tempfile.gs.js +66 -9
- package/dist/gs/os/tempfile.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +2 -51
- package/dist/gs/os/types_js.gs.js +67 -105
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +2 -2
- package/dist/gs/reflect/index.js +1 -1
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +2 -1
- package/dist/gs/reflect/type.js +85 -14
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/sort/sort.gs.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/quote.gs.js.map +1 -1
- package/dist/gs/strings/builder.js.map +1 -1
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/sync/sync.d.ts +1 -0
- package/dist/gs/sync/sync.js +12 -0
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/go.mod +2 -2
- package/gs/builtin/builtin.ts +31 -6
- package/gs/builtin/hostio.test.ts +246 -0
- package/gs/builtin/hostio.ts +413 -0
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/print.test.ts +48 -0
- package/gs/builtin/print.ts +154 -0
- package/gs/builtin/runtime-contract.test.ts +230 -0
- package/gs/builtin/type.ts +84 -1
- package/gs/crypto/rand/index.test.ts +32 -0
- package/gs/crypto/rand/index.ts +90 -0
- package/gs/crypto/rand/meta.json +5 -0
- package/gs/encoding/json/index.test.ts +65 -0
- package/gs/encoding/json/index.ts +186 -0
- package/gs/fmt/fmt.test.ts +41 -30
- package/gs/fmt/fmt.ts +2 -22
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +23 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +3 -1
- package/gs/github.com/aperturerobotics/wasivm/wazero/kernel/runtime/browser/meta.json +3 -1
- package/gs/go/scanner/index.test.ts +50 -0
- package/gs/go/scanner/index.ts +157 -0
- package/gs/go/token/index.test.ts +21 -0
- package/gs/go/token/index.ts +120 -0
- package/gs/os/file_unix_js.test.ts +103 -0
- package/gs/os/meta.json +1 -2
- package/gs/os/tempfile.gs.test.ts +85 -0
- package/gs/os/tempfile.gs.ts +71 -11
- package/gs/os/types_js.gs.ts +74 -153
- package/gs/reflect/index.ts +1 -1
- package/gs/reflect/type.ts +106 -17
- package/gs/reflect/typefor.test.ts +75 -0
- package/gs/sync/sync.test.ts +24 -0
- package/gs/sync/sync.ts +12 -0
- package/package.json +13 -13
- package/compiler/analysis.go +0 -3475
- package/compiler/analysis_test.go +0 -338
- package/compiler/assignment.go +0 -580
- package/compiler/builtin_test.go +0 -92
- package/compiler/code-writer.go +0 -115
- package/compiler/compiler_test.go +0 -149
- package/compiler/composite-lit.go +0 -779
- package/compiler/config_test.go +0 -62
- package/compiler/constraint.go +0 -86
- package/compiler/decl.go +0 -801
- package/compiler/expr-call-async.go +0 -188
- package/compiler/expr-call-builtins.go +0 -208
- package/compiler/expr-call-helpers.go +0 -382
- package/compiler/expr-call-make.go +0 -318
- package/compiler/expr-call-type-conversion.go +0 -520
- package/compiler/expr-call.go +0 -413
- package/compiler/expr-selector.go +0 -343
- package/compiler/expr-star.go +0 -82
- package/compiler/expr-type.go +0 -442
- package/compiler/expr-value.go +0 -89
- package/compiler/expr.go +0 -773
- package/compiler/field.go +0 -183
- package/compiler/gs_dependencies_test.go +0 -298
- package/compiler/lit.go +0 -322
- package/compiler/output.go +0 -72
- package/compiler/primitive.go +0 -149
- package/compiler/protobuf.go +0 -697
- package/compiler/sanitize.go +0 -100
- package/compiler/spec-struct.go +0 -995
- package/compiler/spec-value.go +0 -540
- package/compiler/spec.go +0 -725
- package/compiler/stmt-assign.go +0 -664
- package/compiler/stmt-for.go +0 -266
- package/compiler/stmt-range.go +0 -475
- package/compiler/stmt-select.go +0 -262
- package/compiler/stmt-type-switch.go +0 -147
- package/compiler/stmt.go +0 -1308
- package/compiler/type-assert.go +0 -386
- package/compiler/type-info.go +0 -156
- package/compiler/type-utils.go +0 -207
- package/compiler/type.go +0 -892
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
export class HostUnsupportedError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super('operation not implemented in JavaScript environment')
|
|
4
|
+
}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type NodeFSModule = {
|
|
8
|
+
readSync(
|
|
9
|
+
fd: number,
|
|
10
|
+
buffer: Uint8Array,
|
|
11
|
+
offset?: number,
|
|
12
|
+
length?: number,
|
|
13
|
+
position?: number | null,
|
|
14
|
+
): number
|
|
15
|
+
writeSync(
|
|
16
|
+
fd: number,
|
|
17
|
+
buffer: Uint8Array,
|
|
18
|
+
offset?: number,
|
|
19
|
+
length?: number,
|
|
20
|
+
position?: number | null,
|
|
21
|
+
): number
|
|
22
|
+
closeSync?(fd: number): void
|
|
23
|
+
fstatSync?(fd: number): any
|
|
24
|
+
fsyncSync?(fd: number): void
|
|
25
|
+
ftruncateSync?(fd: number, len?: number): void
|
|
26
|
+
openSync?(path: string, flags: number | string, mode?: number): number
|
|
27
|
+
chmodSync?(path: string, mode: number): void
|
|
28
|
+
chownSync?(path: string, uid: number, gid: number): void
|
|
29
|
+
lchownSync?(path: string, uid: number, gid: number): void
|
|
30
|
+
linkSync?(existingPath: string, newPath: string): void
|
|
31
|
+
lstatSync?(path: string): any
|
|
32
|
+
mkdirSync?(
|
|
33
|
+
path: string,
|
|
34
|
+
options?: number | { mode?: number; recursive?: boolean },
|
|
35
|
+
): void
|
|
36
|
+
readFileSync?(path: string): Uint8Array
|
|
37
|
+
readdirSync?(path: string, options?: { withFileTypes?: boolean }): any[]
|
|
38
|
+
readlinkSync?(path: string): string
|
|
39
|
+
renameSync?(oldPath: string, newPath: string): void
|
|
40
|
+
rmSync?(
|
|
41
|
+
path: string,
|
|
42
|
+
options?: { force?: boolean; recursive?: boolean },
|
|
43
|
+
): void
|
|
44
|
+
rmdirSync?(path: string): void
|
|
45
|
+
statSync?(path: string): any
|
|
46
|
+
symlinkSync?(target: string, path: string): void
|
|
47
|
+
truncateSync?(path: string, len?: number): void
|
|
48
|
+
unlinkSync?(path: string): void
|
|
49
|
+
utimesSync?(path: string, atime: Date | number, mtime: Date | number): void
|
|
50
|
+
writeFileSync?(
|
|
51
|
+
path: string,
|
|
52
|
+
data: Uint8Array,
|
|
53
|
+
options?: { mode?: number },
|
|
54
|
+
): void
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type DenoStream = {
|
|
58
|
+
readSync?(buffer: Uint8Array): number | null
|
|
59
|
+
writeSync?(buffer: Uint8Array): number
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type DenoFileLike = DenoStream & {
|
|
63
|
+
close?(): void
|
|
64
|
+
rid?: number
|
|
65
|
+
seekSync?(offset: number, whence: number): number
|
|
66
|
+
syncSync?(): void
|
|
67
|
+
statSync?(): any
|
|
68
|
+
truncateSync?(len?: number): void
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type HostReadFD = (fd: number, buffer: Uint8Array) => number | null
|
|
72
|
+
type HostWriteFD = (fd: number, buffer: Uint8Array) => number
|
|
73
|
+
type HostTextWrite = (data: string) => void
|
|
74
|
+
|
|
75
|
+
export type HostRuntime = {
|
|
76
|
+
deno: any | null
|
|
77
|
+
nodeFS: NodeFSModule | null
|
|
78
|
+
platform: string
|
|
79
|
+
processObj: any | null
|
|
80
|
+
getEnv(name: string): string
|
|
81
|
+
getStdioHandle(fd: number): DenoFileLike | null
|
|
82
|
+
readFD: HostReadFD
|
|
83
|
+
writeFD: HostWriteFD
|
|
84
|
+
writeStderrText: HostTextWrite
|
|
85
|
+
writeStdoutText: HostTextWrite
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type HostRuntimeDetector = () => HostRuntime
|
|
89
|
+
|
|
90
|
+
export type MainScriptMeta = {
|
|
91
|
+
url: string
|
|
92
|
+
main?: boolean
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const encoder = new TextEncoder()
|
|
96
|
+
|
|
97
|
+
function getDynamicRequire(): ((specifier: string) => unknown) | null {
|
|
98
|
+
try {
|
|
99
|
+
return Function(
|
|
100
|
+
"return typeof require !== 'undefined' ? require : null",
|
|
101
|
+
)() as ((specifier: string) => unknown) | null
|
|
102
|
+
} catch {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function writeAllSync(
|
|
108
|
+
writeChunk: (chunk: Uint8Array) => number,
|
|
109
|
+
buffer: Uint8Array,
|
|
110
|
+
): number {
|
|
111
|
+
let offset = 0
|
|
112
|
+
while (offset < buffer.length) {
|
|
113
|
+
const n = writeChunk(buffer.subarray(offset))
|
|
114
|
+
if (!Number.isFinite(n) || n < 0) {
|
|
115
|
+
throw new Error(`invalid write result: ${n}`)
|
|
116
|
+
}
|
|
117
|
+
if (n === 0) {
|
|
118
|
+
throw new Error('short write')
|
|
119
|
+
}
|
|
120
|
+
offset += n
|
|
121
|
+
}
|
|
122
|
+
return buffer.length
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function writeAllText(
|
|
126
|
+
writeChunk: (chunk: Uint8Array) => number,
|
|
127
|
+
data: string,
|
|
128
|
+
): void {
|
|
129
|
+
const bytes = encoder.encode(data)
|
|
130
|
+
if (bytes.length === 0) {
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
writeAllSync(writeChunk, bytes)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function detectNodeFS(processObj: any | null): NodeFSModule | null {
|
|
137
|
+
if (processObj && typeof processObj.getBuiltinModule === 'function') {
|
|
138
|
+
const module = processObj.getBuiltinModule('fs')
|
|
139
|
+
if (
|
|
140
|
+
module &&
|
|
141
|
+
typeof module.readSync === 'function' &&
|
|
142
|
+
typeof module.writeSync === 'function'
|
|
143
|
+
) {
|
|
144
|
+
return module as NodeFSModule
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const requireFn = getDynamicRequire()
|
|
149
|
+
if (requireFn) {
|
|
150
|
+
for (const specifier of ['node:fs', 'fs']) {
|
|
151
|
+
try {
|
|
152
|
+
const module = requireFn(specifier) as NodeFSModule | null
|
|
153
|
+
if (
|
|
154
|
+
module &&
|
|
155
|
+
typeof module.readSync === 'function' &&
|
|
156
|
+
typeof module.writeSync === 'function'
|
|
157
|
+
) {
|
|
158
|
+
return module
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
// Try the next fallback.
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return null
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function hasURLScheme(path: string): boolean {
|
|
170
|
+
return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(path)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function isAbsoluteScriptPath(path: string): boolean {
|
|
174
|
+
return path.startsWith('/') || /^[A-Za-z]:[\\/]/.test(path)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizePath(path: string): string {
|
|
178
|
+
return path.replace(/\\/g, '/')
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function absolutePathToFileURL(path: string, isDirectory: boolean): string {
|
|
182
|
+
const normalized = normalizePath(path)
|
|
183
|
+
const suffix = isDirectory && !normalized.endsWith('/') ? '/' : ''
|
|
184
|
+
if (/^[A-Za-z]:\//.test(normalized)) {
|
|
185
|
+
return new URL(`file:///${normalized}${suffix}`).href
|
|
186
|
+
}
|
|
187
|
+
return new URL(`file://${normalized}${suffix}`).href
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getCurrentWorkingDirectory(): string | null {
|
|
191
|
+
const runtime = getHostRuntime()
|
|
192
|
+
try {
|
|
193
|
+
if (typeof runtime.processObj?.cwd === 'function') {
|
|
194
|
+
return runtime.processObj.cwd()
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// Fall through to Deno cwd.
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
if (typeof runtime.deno?.cwd === 'function') {
|
|
202
|
+
return runtime.deno.cwd()
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
// No cwd fallback available.
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return null
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function fileURLFromScriptPath(path: string): string | null {
|
|
212
|
+
try {
|
|
213
|
+
if (hasURLScheme(path)) {
|
|
214
|
+
return new URL(path).href
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (isAbsoluteScriptPath(path)) {
|
|
218
|
+
return absolutePathToFileURL(path, false)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const cwd = getCurrentWorkingDirectory()
|
|
222
|
+
if (!cwd) {
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
return new URL(normalizePath(path), absolutePathToFileURL(cwd, true)).href
|
|
226
|
+
} catch {
|
|
227
|
+
return null
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function fallbackConsoleWriter(method: 'error' | 'log'): HostTextWrite {
|
|
232
|
+
return (data: string) => {
|
|
233
|
+
const consoleMethod = (globalThis as any).console?.[method]
|
|
234
|
+
if (!consoleMethod) {
|
|
235
|
+
return
|
|
236
|
+
}
|
|
237
|
+
if (data.endsWith('\n')) {
|
|
238
|
+
consoleMethod.call((globalThis as any).console, data.slice(0, -1))
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
consoleMethod.call((globalThis as any).console, data)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function detectHostRuntime(): HostRuntime {
|
|
246
|
+
const globalObj = globalThis as any
|
|
247
|
+
const deno = globalObj.Deno ?? null
|
|
248
|
+
const processObj = globalObj.process ?? null
|
|
249
|
+
const nodeFS = detectNodeFS(processObj)
|
|
250
|
+
|
|
251
|
+
const getStdioHandle = (fd: number): DenoFileLike | null => {
|
|
252
|
+
if (!deno) {
|
|
253
|
+
return null
|
|
254
|
+
}
|
|
255
|
+
switch (fd) {
|
|
256
|
+
case 0:
|
|
257
|
+
return deno.stdin ?? null
|
|
258
|
+
case 1:
|
|
259
|
+
return deno.stdout ?? null
|
|
260
|
+
case 2:
|
|
261
|
+
return deno.stderr ?? null
|
|
262
|
+
default:
|
|
263
|
+
return null
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const platform = deno?.build?.os ?? processObj?.platform ?? 'unknown'
|
|
268
|
+
|
|
269
|
+
const getEnv = (name: string): string => {
|
|
270
|
+
if (deno?.env?.get) {
|
|
271
|
+
try {
|
|
272
|
+
return deno.env.get(name) ?? ''
|
|
273
|
+
} catch {
|
|
274
|
+
return ''
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return processObj?.env?.[name] ?? ''
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const readFD: HostReadFD = (
|
|
281
|
+
fd: number,
|
|
282
|
+
buffer: Uint8Array,
|
|
283
|
+
): number | null => {
|
|
284
|
+
const handle = getStdioHandle(fd)
|
|
285
|
+
if (handle && typeof handle.readSync === 'function') {
|
|
286
|
+
return handle.readSync(buffer)
|
|
287
|
+
}
|
|
288
|
+
if (nodeFS) {
|
|
289
|
+
return nodeFS.readSync(fd, buffer, 0, buffer.length, null)
|
|
290
|
+
}
|
|
291
|
+
throw new HostUnsupportedError()
|
|
292
|
+
}
|
|
293
|
+
const writeFD: HostWriteFD = (fd: number, buffer: Uint8Array): number => {
|
|
294
|
+
const handle = getStdioHandle(fd)
|
|
295
|
+
if (handle && typeof handle.writeSync === 'function') {
|
|
296
|
+
return writeAllSync(
|
|
297
|
+
(chunk: Uint8Array) => handle.writeSync!(chunk),
|
|
298
|
+
buffer,
|
|
299
|
+
)
|
|
300
|
+
}
|
|
301
|
+
if (nodeFS) {
|
|
302
|
+
return writeAllSync(
|
|
303
|
+
(chunk: Uint8Array) =>
|
|
304
|
+
nodeFS.writeSync(fd, chunk, 0, chunk.length, null),
|
|
305
|
+
buffer,
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
throw new HostUnsupportedError()
|
|
309
|
+
}
|
|
310
|
+
const writeStdoutText: HostTextWrite = (data: string) => {
|
|
311
|
+
const handle = getStdioHandle(1)
|
|
312
|
+
if (handle && typeof handle.writeSync === 'function') {
|
|
313
|
+
writeAllText((chunk: Uint8Array) => handle.writeSync!(chunk), data)
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
if (nodeFS) {
|
|
317
|
+
writeAllText(
|
|
318
|
+
(chunk: Uint8Array) =>
|
|
319
|
+
nodeFS.writeSync(1, chunk, 0, chunk.length, null),
|
|
320
|
+
data,
|
|
321
|
+
)
|
|
322
|
+
return
|
|
323
|
+
}
|
|
324
|
+
fallbackConsoleWriter('log')(data)
|
|
325
|
+
}
|
|
326
|
+
const writeStderrText: HostTextWrite = (data: string) => {
|
|
327
|
+
const handle = getStdioHandle(2)
|
|
328
|
+
if (handle && typeof handle.writeSync === 'function') {
|
|
329
|
+
writeAllText((chunk: Uint8Array) => handle.writeSync!(chunk), data)
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
if (nodeFS) {
|
|
333
|
+
writeAllText(
|
|
334
|
+
(chunk: Uint8Array) =>
|
|
335
|
+
nodeFS.writeSync(2, chunk, 0, chunk.length, null),
|
|
336
|
+
data,
|
|
337
|
+
)
|
|
338
|
+
return
|
|
339
|
+
}
|
|
340
|
+
fallbackConsoleWriter('error')(data)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
deno,
|
|
345
|
+
getEnv,
|
|
346
|
+
getStdioHandle,
|
|
347
|
+
nodeFS,
|
|
348
|
+
platform,
|
|
349
|
+
processObj,
|
|
350
|
+
readFD,
|
|
351
|
+
writeFD,
|
|
352
|
+
writeStderrText,
|
|
353
|
+
writeStdoutText,
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
export class HostRuntimeOwner {
|
|
358
|
+
private runtime: HostRuntime
|
|
359
|
+
|
|
360
|
+
constructor(
|
|
361
|
+
private readonly detector: HostRuntimeDetector = detectHostRuntime,
|
|
362
|
+
) {
|
|
363
|
+
this.runtime = detector()
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
current(): HostRuntime {
|
|
367
|
+
return this.runtime
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
reset(): void {
|
|
371
|
+
this.runtime = this.detector()
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export const hostRuntimeOwner = new HostRuntimeOwner()
|
|
376
|
+
|
|
377
|
+
export function getHostRuntime(): HostRuntime {
|
|
378
|
+
return hostRuntimeOwner.current()
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function resetHostRuntimeForTests(): void {
|
|
382
|
+
hostRuntimeOwner.reset()
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function writeHostStdoutText(data: string): void {
|
|
386
|
+
getHostRuntime().writeStdoutText(data)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function writeHostStderrText(data: string): void {
|
|
390
|
+
getHostRuntime().writeStderrText(data)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function isMainScript(meta: MainScriptMeta): boolean {
|
|
394
|
+
if (meta.main === true) {
|
|
395
|
+
return true
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const entryPath = getHostRuntime().processObj?.argv?.[1]
|
|
399
|
+
if (typeof entryPath !== 'string' || entryPath === '') {
|
|
400
|
+
return false
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const entryURL = fileURLFromScriptPath(entryPath)
|
|
404
|
+
if (!entryURL) {
|
|
405
|
+
return false
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
return new URL(meta.url).href === entryURL
|
|
410
|
+
} catch {
|
|
411
|
+
return meta.url === entryURL
|
|
412
|
+
}
|
|
413
|
+
}
|
package/gs/builtin/index.ts
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { formatPrintedArgs } from './print.js'
|
|
4
|
+
|
|
5
|
+
describe('builtin println formatting', () => {
|
|
6
|
+
it('formats Uint8Array values with a stable inspect-style representation', () => {
|
|
7
|
+
expect(
|
|
8
|
+
formatPrintedArgs(['b2:', new Uint8Array([72, 101, 108, 108, 111])]),
|
|
9
|
+
).toBe('b2: Uint8Array(5) [ 72, 101, 108, 108, 111 ]')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('quotes nested string array elements', () => {
|
|
13
|
+
expect(formatPrintedArgs(['strings.Split:', ['a', 'b', 'c']])).toBe(
|
|
14
|
+
'strings.Split: [ "a", "b", "c" ]',
|
|
15
|
+
)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('formats plain objects across multiple lines', () => {
|
|
19
|
+
expect(
|
|
20
|
+
formatPrintedArgs([
|
|
21
|
+
'out:',
|
|
22
|
+
{
|
|
23
|
+
exampleField: new Uint8Array([104, 101, 108, 108, 111]),
|
|
24
|
+
exampleText: 'world',
|
|
25
|
+
},
|
|
26
|
+
]),
|
|
27
|
+
).toBe(`out: {
|
|
28
|
+
exampleField: Uint8Array(5) [ 104, 101, 108, 108, 111 ],
|
|
29
|
+
exampleText: "world",
|
|
30
|
+
}`)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('formats goscript struct field bags using field values', () => {
|
|
34
|
+
expect(
|
|
35
|
+
formatPrintedArgs([
|
|
36
|
+
{
|
|
37
|
+
_fields: {
|
|
38
|
+
Name: { value: 'hello' },
|
|
39
|
+
Count: { value: 3 },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
]),
|
|
43
|
+
).toBe(`{
|
|
44
|
+
Name: "hello",
|
|
45
|
+
Count: 3,
|
|
46
|
+
}`)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { asArray, isSliceProxy, type Slice } from './slice.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* formatPrintedArgs formats builtin println arguments deterministically.
|
|
5
|
+
*/
|
|
6
|
+
export function formatPrintedArgs(args: readonly any[]): string {
|
|
7
|
+
return args.map((arg) => formatPrintedValue(arg)).join(' ')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* formatPrintedValue formats a single builtin println argument deterministically.
|
|
12
|
+
*/
|
|
13
|
+
export function formatPrintedValue(value: any): string {
|
|
14
|
+
return formatValue(value, 0, false, new WeakSet<object>())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function formatValue(
|
|
18
|
+
value: any,
|
|
19
|
+
depth: number,
|
|
20
|
+
nested: boolean,
|
|
21
|
+
seen: WeakSet<object>,
|
|
22
|
+
): string {
|
|
23
|
+
if (value === null) {
|
|
24
|
+
return 'null'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
return '<nil>'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof value === 'string') {
|
|
32
|
+
return nested ? JSON.stringify(value) : value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof value === 'boolean' || typeof value === 'number') {
|
|
36
|
+
return String(value)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof value === 'bigint') {
|
|
40
|
+
return value.toString()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (value instanceof Uint8Array) {
|
|
44
|
+
return formatUint8Array(value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
return formatArray(value, depth, seen)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (isSliceProxy(value as Slice<unknown>)) {
|
|
52
|
+
return formatArray(asArray(value as Slice<unknown>), depth, seen)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof value === 'function') {
|
|
56
|
+
return value.name ? `[Function: ${value.name}]` : '[Function]'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeof value !== 'object') {
|
|
60
|
+
return String(value)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (seen.has(value)) {
|
|
64
|
+
return '[Circular]'
|
|
65
|
+
}
|
|
66
|
+
seen.add(value)
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (value instanceof Map) {
|
|
70
|
+
return formatArray(
|
|
71
|
+
Array.from(value.entries()).map(([k, v]) => `${formatValue(k, depth + 1, true, seen)} => ${formatValue(v, depth + 1, true, seen)}`),
|
|
72
|
+
depth,
|
|
73
|
+
seen,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (value instanceof Set) {
|
|
78
|
+
return formatArray(Array.from(value.values()), depth, seen)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (value instanceof Error) {
|
|
82
|
+
return value.message || value.toString()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof value.GoString === 'function') {
|
|
86
|
+
return value.GoString()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof value.Error === 'function') {
|
|
90
|
+
return value.Error()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (typeof value.String === 'function') {
|
|
94
|
+
return value.String()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const entries = getObjectEntries(value)
|
|
98
|
+
if (entries.length === 0) {
|
|
99
|
+
return '{}'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return formatObject(entries, depth, seen)
|
|
103
|
+
} finally {
|
|
104
|
+
seen.delete(value)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function formatUint8Array(value: Uint8Array): string {
|
|
109
|
+
if (value.length === 0) {
|
|
110
|
+
return 'Uint8Array(0) []'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return `Uint8Array(${value.length}) [ ${Array.from(value).join(', ')} ]`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function formatArray(value: readonly any[], depth: number, seen: WeakSet<object>): string {
|
|
117
|
+
if (value.length === 0) {
|
|
118
|
+
return '[]'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return `[ ${value.map((item) => formatValue(item, depth + 1, true, seen)).join(', ')} ]`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function formatObject(
|
|
125
|
+
entries: readonly [string, any][],
|
|
126
|
+
depth: number,
|
|
127
|
+
seen: WeakSet<object>,
|
|
128
|
+
): string {
|
|
129
|
+
const pad = ' '.repeat(depth + 1)
|
|
130
|
+
const closePad = ' '.repeat(depth)
|
|
131
|
+
const body = entries
|
|
132
|
+
.map(
|
|
133
|
+
([key, value]) =>
|
|
134
|
+
`${pad}${key}: ${formatValue(value, depth + 1, true, seen)},`,
|
|
135
|
+
)
|
|
136
|
+
.join('\n')
|
|
137
|
+
|
|
138
|
+
return `{\n${body}\n${closePad}}`
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function getObjectEntries(value: Record<string, any>): [string, any][] {
|
|
142
|
+
const fields = value._fields
|
|
143
|
+
if (fields && typeof fields === 'object' && !Array.isArray(fields)) {
|
|
144
|
+
return Object.keys(fields).map((key) => {
|
|
145
|
+
const field = fields[key]
|
|
146
|
+
if (field && typeof field === 'object' && 'value' in field) {
|
|
147
|
+
return [key, field.value]
|
|
148
|
+
}
|
|
149
|
+
return [key, field]
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return Object.entries(value).filter(([, entry]) => typeof entry !== 'function')
|
|
154
|
+
}
|