goscript 0.1.4 → 0.2.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.
Files changed (270) hide show
  1. package/README.md +5 -2
  2. package/cmd/go_js_wasm_exec/main.go +201 -0
  3. package/cmd/go_js_wasm_exec/main_test.go +83 -0
  4. package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +7 -0
  5. package/cmd/goscript/cmd-test.go +14 -0
  6. package/cmd/goscript/cmd-test_test.go +1 -1
  7. package/compiler/compile-request.go +12 -9
  8. package/compiler/compliance_test.go +0 -1
  9. package/compiler/config.go +2 -0
  10. package/compiler/gotest/request.go +28 -0
  11. package/compiler/gotest/runner.go +353 -27
  12. package/compiler/gotest/runner_test.go +273 -1
  13. package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
  14. package/compiler/gotest/testdata/browserapi/go.mod +3 -0
  15. package/compiler/lowered-program.go +24 -17
  16. package/compiler/lowering.go +392 -127
  17. package/compiler/lowering_bench_test.go +41 -27
  18. package/compiler/override-facts.go +15 -0
  19. package/compiler/override-parity-verifier.go +450 -0
  20. package/compiler/override-parity.go +122 -0
  21. package/compiler/override-registry_test.go +559 -0
  22. package/compiler/protobuf-ts-binding.go +514 -0
  23. package/compiler/protobuf-ts-binding_test.go +172 -0
  24. package/compiler/semantic-model-types.go +9 -4
  25. package/compiler/semantic-model.go +282 -70
  26. package/compiler/semantic-model_test.go +82 -1
  27. package/compiler/service.go +20 -1
  28. package/compiler/skeleton_test.go +62 -8
  29. package/compiler/typescript-emitter.go +128 -13
  30. package/dist/gs/builtin/slice.d.ts +2 -1
  31. package/dist/gs/builtin/slice.js +29 -4
  32. package/dist/gs/builtin/slice.js.map +1 -1
  33. package/dist/gs/builtin/type.d.ts +13 -5
  34. package/dist/gs/builtin/type.js +153 -60
  35. package/dist/gs/builtin/type.js.map +1 -1
  36. package/dist/gs/builtin/varRef.d.ts +11 -0
  37. package/dist/gs/builtin/varRef.js +57 -2
  38. package/dist/gs/builtin/varRef.js.map +1 -1
  39. package/dist/gs/bytes/buffer.gs.js +1 -1
  40. package/dist/gs/bytes/buffer.gs.js.map +1 -1
  41. package/dist/gs/bytes/reader.gs.js +1 -1
  42. package/dist/gs/bytes/reader.gs.js.map +1 -1
  43. package/dist/gs/compress/zlib/index.d.ts +10 -3
  44. package/dist/gs/compress/zlib/index.js +50 -16
  45. package/dist/gs/compress/zlib/index.js.map +1 -1
  46. package/dist/gs/encoding/json/index.d.ts +114 -0
  47. package/dist/gs/encoding/json/index.js +544 -36
  48. package/dist/gs/encoding/json/index.js.map +1 -1
  49. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
  50. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
  51. package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
  52. package/dist/gs/github.com/pkg/errors/errors.js +54 -30
  53. package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
  54. package/dist/gs/go/scanner/index.d.ts +2 -0
  55. package/dist/gs/go/scanner/index.js +29 -5
  56. package/dist/gs/go/scanner/index.js.map +1 -1
  57. package/dist/gs/go/token/index.js +22 -6
  58. package/dist/gs/go/token/index.js.map +1 -1
  59. package/dist/gs/hash/index.d.ts +6 -0
  60. package/dist/gs/hash/index.js +20 -0
  61. package/dist/gs/hash/index.js.map +1 -1
  62. package/dist/gs/internal/goarch/index.d.ts +43 -3
  63. package/dist/gs/internal/goarch/index.js +42 -10
  64. package/dist/gs/internal/goarch/index.js.map +1 -1
  65. package/dist/gs/io/fs/fs.js +26 -14
  66. package/dist/gs/io/fs/fs.js.map +1 -1
  67. package/dist/gs/io/fs/readdir.js +4 -2
  68. package/dist/gs/io/fs/readdir.js.map +1 -1
  69. package/dist/gs/io/fs/sub.js +8 -1
  70. package/dist/gs/io/fs/sub.js.map +1 -1
  71. package/dist/gs/io/io.d.ts +2 -0
  72. package/dist/gs/io/io.js.map +1 -1
  73. package/dist/gs/math/bits/index.d.ts +5 -0
  74. package/dist/gs/math/bits/index.js +16 -4
  75. package/dist/gs/math/bits/index.js.map +1 -1
  76. package/dist/gs/mime/index.d.ts +16 -0
  77. package/dist/gs/mime/index.js +315 -6
  78. package/dist/gs/mime/index.js.map +1 -1
  79. package/dist/gs/net/http/httptest/index.d.ts +12 -0
  80. package/dist/gs/net/http/httptest/index.js +85 -6
  81. package/dist/gs/net/http/httptest/index.js.map +1 -1
  82. package/dist/gs/net/http/index.d.ts +300 -5
  83. package/dist/gs/net/http/index.js +1598 -58
  84. package/dist/gs/net/http/index.js.map +1 -1
  85. package/dist/gs/os/dir_unix.gs.js +1 -1
  86. package/dist/gs/os/dir_unix.gs.js.map +1 -1
  87. package/dist/gs/os/error.gs.js +1 -1
  88. package/dist/gs/os/error.gs.js.map +1 -1
  89. package/dist/gs/os/exec.gs.d.ts +1 -0
  90. package/dist/gs/os/exec.gs.js +4 -8
  91. package/dist/gs/os/exec.gs.js.map +1 -1
  92. package/dist/gs/os/exec_posix.gs.js +1 -1
  93. package/dist/gs/os/exec_posix.gs.js.map +1 -1
  94. package/dist/gs/os/index.d.ts +1 -1
  95. package/dist/gs/os/index.js +1 -1
  96. package/dist/gs/os/index.js.map +1 -1
  97. package/dist/gs/os/proc.gs.d.ts +4 -0
  98. package/dist/gs/os/proc.gs.js +12 -6
  99. package/dist/gs/os/proc.gs.js.map +1 -1
  100. package/dist/gs/os/root_js.gs.js +1 -1
  101. package/dist/gs/os/root_js.gs.js.map +1 -1
  102. package/dist/gs/os/types.gs.js +1 -1
  103. package/dist/gs/os/types.gs.js.map +1 -1
  104. package/dist/gs/os/types_js.gs.js +1 -1
  105. package/dist/gs/os/types_js.gs.js.map +1 -1
  106. package/dist/gs/os/types_unix.gs.js +1 -1
  107. package/dist/gs/os/types_unix.gs.js.map +1 -1
  108. package/dist/gs/path/path.js +11 -7
  109. package/dist/gs/path/path.js.map +1 -1
  110. package/dist/gs/reflect/index.d.ts +5 -4
  111. package/dist/gs/reflect/index.js +4 -3
  112. package/dist/gs/reflect/index.js.map +1 -1
  113. package/dist/gs/reflect/map.js +15 -0
  114. package/dist/gs/reflect/map.js.map +1 -1
  115. package/dist/gs/reflect/type.d.ts +25 -6
  116. package/dist/gs/reflect/type.js +1418 -228
  117. package/dist/gs/reflect/type.js.map +1 -1
  118. package/dist/gs/reflect/types.d.ts +14 -6
  119. package/dist/gs/reflect/types.js +35 -1
  120. package/dist/gs/reflect/types.js.map +1 -1
  121. package/dist/gs/reflect/value.d.ts +1 -0
  122. package/dist/gs/reflect/value.js +83 -41
  123. package/dist/gs/reflect/value.js.map +1 -1
  124. package/dist/gs/reflect/visiblefields.js +4 -140
  125. package/dist/gs/reflect/visiblefields.js.map +1 -1
  126. package/dist/gs/runtime/pprof/index.d.ts +8 -2
  127. package/dist/gs/runtime/pprof/index.js +50 -30
  128. package/dist/gs/runtime/pprof/index.js.map +1 -1
  129. package/dist/gs/runtime/runtime.js +5 -4
  130. package/dist/gs/runtime/runtime.js.map +1 -1
  131. package/dist/gs/runtime/trace/index.js +5 -19
  132. package/dist/gs/runtime/trace/index.js.map +1 -1
  133. package/dist/gs/strconv/atoi.gs.js +1 -1
  134. package/dist/gs/strconv/atoi.gs.js.map +1 -1
  135. package/dist/gs/strconv/complex.gs.d.ts +3 -0
  136. package/dist/gs/strconv/complex.gs.js +148 -0
  137. package/dist/gs/strconv/complex.gs.js.map +1 -0
  138. package/dist/gs/strconv/index.d.ts +1 -0
  139. package/dist/gs/strconv/index.js +1 -0
  140. package/dist/gs/strconv/index.js.map +1 -1
  141. package/dist/gs/strings/builder.js +1 -1
  142. package/dist/gs/strings/reader.js +9 -5
  143. package/dist/gs/strings/reader.js.map +1 -1
  144. package/dist/gs/strings/replace.js +15 -7
  145. package/dist/gs/strings/replace.js.map +1 -1
  146. package/dist/gs/strings/strings.d.ts +5 -0
  147. package/dist/gs/strings/strings.js +57 -5
  148. package/dist/gs/strings/strings.js.map +1 -1
  149. package/dist/gs/sync/atomic/type.gs.js +9 -9
  150. package/dist/gs/sync/atomic/type.gs.js.map +1 -1
  151. package/dist/gs/sync/atomic/value.gs.js +2 -2
  152. package/dist/gs/sync/atomic/value.gs.js.map +1 -1
  153. package/dist/gs/syscall/env.js +22 -14
  154. package/dist/gs/syscall/env.js.map +1 -1
  155. package/dist/gs/testing/testing.js +55 -13
  156. package/dist/gs/testing/testing.js.map +1 -1
  157. package/dist/gs/time/time.d.ts +24 -1
  158. package/dist/gs/time/time.js +43 -3
  159. package/dist/gs/time/time.js.map +1 -1
  160. package/dist/gs/unique/index.js +7 -1
  161. package/dist/gs/unique/index.js.map +1 -1
  162. package/go.mod +3 -3
  163. package/go.sum +16 -0
  164. package/gs/builtin/runtime-contract.test.ts +218 -21
  165. package/gs/builtin/slice.ts +44 -4
  166. package/gs/builtin/type.ts +226 -59
  167. package/gs/builtin/varRef.ts +85 -2
  168. package/gs/bytes/buffer.gs.ts +1 -1
  169. package/gs/bytes/reader.gs.ts +1 -1
  170. package/gs/compress/zlib/index.test.ts +62 -1
  171. package/gs/compress/zlib/index.ts +53 -16
  172. package/gs/compress/zlib/parity.json +51 -0
  173. package/gs/encoding/json/index.test.ts +360 -6
  174. package/gs/encoding/json/index.ts +679 -38
  175. package/gs/encoding/json/parity.json +81 -0
  176. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
  177. package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
  178. package/gs/github.com/pkg/errors/errors.ts +54 -30
  179. package/gs/go/scanner/index.test.ts +39 -56
  180. package/gs/go/scanner/index.ts +33 -5
  181. package/gs/go/scanner/parity.json +27 -0
  182. package/gs/go/token/index.ts +22 -6
  183. package/gs/hash/index.test.ts +20 -33
  184. package/gs/hash/index.ts +28 -0
  185. package/gs/hash/parity.json +21 -0
  186. package/gs/internal/goarch/index.test.ts +32 -0
  187. package/gs/internal/goarch/index.ts +45 -13
  188. package/gs/internal/goarch/parity.json +144 -0
  189. package/gs/io/fs/fs.ts +26 -14
  190. package/gs/io/fs/readdir.ts +4 -4
  191. package/gs/io/fs/sub.ts +8 -1
  192. package/gs/io/io.ts +1 -0
  193. package/gs/io/parity.json +162 -0
  194. package/gs/math/bits/index.test.ts +14 -1
  195. package/gs/math/bits/index.ts +23 -4
  196. package/gs/math/bits/parity.json +156 -0
  197. package/gs/mime/index.test.ts +90 -0
  198. package/gs/mime/index.ts +369 -6
  199. package/gs/mime/parity.json +36 -0
  200. package/gs/net/http/httptest/index.test.ts +98 -2
  201. package/gs/net/http/httptest/index.ts +101 -6
  202. package/gs/net/http/httptest/parity.json +15 -0
  203. package/gs/net/http/index.test.ts +781 -12
  204. package/gs/net/http/index.ts +1860 -139
  205. package/gs/net/http/meta.json +16 -1
  206. package/gs/net/http/parity.json +193 -0
  207. package/gs/os/dir_unix.gs.ts +1 -1
  208. package/gs/os/error.gs.ts +1 -1
  209. package/gs/os/exec.gs.ts +4 -8
  210. package/gs/os/exec_posix.gs.ts +1 -1
  211. package/gs/os/index.test.ts +9 -0
  212. package/gs/os/index.ts +1 -0
  213. package/gs/os/parity.json +9 -0
  214. package/gs/os/proc.gs.ts +18 -5
  215. package/gs/os/proc.test.ts +26 -0
  216. package/gs/os/root_js.gs.ts +1 -1
  217. package/gs/os/types.gs.ts +1 -1
  218. package/gs/os/types_js.gs.ts +1 -1
  219. package/gs/os/types_unix.gs.ts +1 -1
  220. package/gs/path/path.ts +11 -7
  221. package/gs/reflect/field.test.ts +37 -15
  222. package/gs/reflect/function-types.test.ts +518 -22
  223. package/gs/reflect/index.ts +8 -6
  224. package/gs/reflect/map.ts +20 -0
  225. package/gs/reflect/meta.json +6 -4
  226. package/gs/reflect/parity.json +234 -0
  227. package/gs/reflect/sliceat.test.ts +156 -0
  228. package/gs/reflect/structof.test.ts +401 -0
  229. package/gs/reflect/type.ts +1897 -317
  230. package/gs/reflect/typefor.test.ts +510 -10
  231. package/gs/reflect/types.ts +43 -18
  232. package/gs/reflect/value.ts +105 -45
  233. package/gs/reflect/visiblefields.ts +5 -168
  234. package/gs/runtime/parity.json +24 -0
  235. package/gs/runtime/pprof/index.test.ts +29 -7
  236. package/gs/runtime/pprof/index.ts +56 -30
  237. package/gs/runtime/pprof/parity.json +27 -0
  238. package/gs/runtime/runtime.test.ts +3 -1
  239. package/gs/runtime/runtime.ts +4 -3
  240. package/gs/runtime/trace/index.test.ts +5 -3
  241. package/gs/runtime/trace/index.ts +8 -20
  242. package/gs/runtime/trace/parity.json +36 -0
  243. package/gs/strconv/atoi.gs.ts +1 -1
  244. package/gs/strconv/complex.gs.ts +174 -0
  245. package/gs/strconv/complex.test.ts +65 -0
  246. package/gs/strconv/index.ts +1 -0
  247. package/gs/strconv/parity.json +120 -0
  248. package/gs/strings/builder.ts +1 -1
  249. package/gs/strings/parity.json +186 -0
  250. package/gs/strings/reader.ts +9 -5
  251. package/gs/strings/replace.ts +15 -7
  252. package/gs/strings/strings.test.ts +22 -2
  253. package/gs/strings/strings.ts +64 -6
  254. package/gs/sync/atomic/type.gs.ts +9 -9
  255. package/gs/sync/atomic/value.gs.ts +2 -2
  256. package/gs/syscall/env.ts +29 -14
  257. package/gs/testing/testing.test.ts +67 -0
  258. package/gs/testing/testing.ts +87 -19
  259. package/gs/time/parity.json +225 -0
  260. package/gs/time/time.test.ts +20 -2
  261. package/gs/time/time.ts +49 -7
  262. package/gs/unique/index.ts +7 -1
  263. package/package.json +4 -2
  264. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
  265. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
  266. package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
  267. package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
  268. package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
  269. package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
  270. /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
@@ -434,11 +434,15 @@ export class Reader {
434
434
  },
435
435
  ],
436
436
  Reader,
437
- {
438
- s: { kind: $.TypeKind.Basic, name: 'string' },
439
- i: { kind: $.TypeKind.Basic, name: 'number' },
440
- prevRune: { kind: $.TypeKind.Basic, name: 'number' },
441
- },
437
+ [
438
+ { name: 's', key: 's', type: { kind: $.TypeKind.Basic, name: 'string' } },
439
+ { name: 'i', key: 'i', type: { kind: $.TypeKind.Basic, name: 'number' } },
440
+ {
441
+ name: 'prevRune',
442
+ key: 'prevRune',
443
+ type: { kind: $.TypeKind.Basic, name: 'number' },
444
+ },
445
+ ],
442
446
  )
443
447
  }
444
448
 
@@ -205,14 +205,22 @@ export class Replacer {
205
205
  },
206
206
  ],
207
207
  Replacer,
208
- {
209
- built: { kind: $.TypeKind.Basic, name: 'boolean' },
210
- r: 'replacer',
211
- oldnew: {
212
- kind: $.TypeKind.Slice,
213
- elemType: { kind: $.TypeKind.Basic, name: 'string' },
208
+ [
209
+ {
210
+ name: 'built',
211
+ key: 'built',
212
+ type: { kind: $.TypeKind.Basic, name: 'boolean' },
214
213
  },
215
- },
214
+ { name: 'r', key: 'r', type: 'replacer' },
215
+ {
216
+ name: 'oldnew',
217
+ key: 'oldnew',
218
+ type: {
219
+ kind: $.TypeKind.Slice,
220
+ elemType: { kind: $.TypeKind.Basic, name: 'string' },
221
+ },
222
+ },
223
+ ],
216
224
  )
217
225
  }
218
226
 
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from 'vitest'
2
2
  import * as $ from '../builtin/index.js'
3
+ import * as unicode from '../unicode/unicode.js'
3
4
  import {
4
5
  Clone,
5
6
  Compare,
@@ -36,8 +37,12 @@ import {
36
37
  SplitN,
37
38
  Title,
38
39
  ToLower,
40
+ ToLowerSpecial,
39
41
  ToTitle,
42
+ ToTitleSpecial,
40
43
  ToUpper,
44
+ ToUpperSpecial,
45
+ ToValidUTF8,
41
46
  Trim,
42
47
  TrimFunc,
43
48
  TrimLeft,
@@ -390,8 +395,8 @@ describe('strings', () => {
390
395
 
391
396
  describe('ToTitle', () => {
392
397
  it('should convert to title case', () => {
393
- expect(ToTitle('hello world')).toBe('Hello World')
394
- expect(ToTitle('HELLO WORLD')).toBe('Hello World')
398
+ expect(ToTitle('hello world')).toBe('HELLO WORLD')
399
+ expect(ToTitle('HELLO WORLD')).toBe('HELLO WORLD')
395
400
  expect(ToTitle('')).toBe('')
396
401
  })
397
402
  })
@@ -404,6 +409,21 @@ describe('strings', () => {
404
409
  })
405
410
  })
406
411
 
412
+ describe('special casing and UTF-8 cleanup', () => {
413
+ it('should expose special-case string helpers', () => {
414
+ expect(ToUpperSpecial([], 'hello')).toBe('HELLO')
415
+ expect(ToLowerSpecial([], 'HELLO')).toBe('hello')
416
+ expect(ToTitleSpecial([], 'hello world')).toBe('HELLO WORLD')
417
+ expect(ToUpperSpecial(unicode.TurkishCase, 'iki')).toBe('İKİ')
418
+ expect(ToLowerSpecial(unicode.TurkishCase, 'Iİ')).toBe('ıi')
419
+ expect(ToTitleSpecial(unicode.TurkishCase, 'iki')).toBe('İKİ')
420
+ })
421
+
422
+ it('should preserve valid replacement runes', () => {
423
+ expect(ToValidUTF8('a\uFFFDb', '?')).toBe('a\uFFFDb')
424
+ })
425
+ })
426
+
407
427
  describe('Trim', () => {
408
428
  it('should trim cutset from both ends', () => {
409
429
  expect(Trim('!hello!', '!')).toBe('hello')
@@ -1,4 +1,5 @@
1
1
  import * as $ from '@goscript/builtin/index.js'
2
+ import * as unicode from '@goscript/unicode/index.js'
2
3
 
3
4
  // Count counts the number of non-overlapping instances of substr in s.
4
5
  // If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
@@ -355,14 +356,76 @@ export function ToUpper(s: string): string {
355
356
  return s.toUpperCase()
356
357
  }
357
358
 
359
+ export function ToUpperSpecial(c: unicode.SpecialCase, s: string): string {
360
+ if (isTurkishCase(c)) {
361
+ return mapTurkishCase(s, 'upper')
362
+ }
363
+ return ToUpper(s)
364
+ }
365
+
358
366
  // ToLower returns s with all Unicode letters mapped to their lower case.
359
367
  export function ToLower(s: string): string {
360
368
  return s.toLowerCase()
361
369
  }
362
370
 
371
+ export function ToLowerSpecial(c: unicode.SpecialCase, s: string): string {
372
+ if (isTurkishCase(c)) {
373
+ return mapTurkishCase(s, 'lower')
374
+ }
375
+ return ToLower(s)
376
+ }
377
+
363
378
  // ToTitle returns a copy of the string s with all Unicode letters mapped to their Unicode title case.
364
379
  export function ToTitle(s: string): string {
365
- // JavaScript doesn't have a direct toTitleCase, so we'll use a simple approximation
380
+ return s.toUpperCase()
381
+ }
382
+
383
+ export function ToTitleSpecial(c: unicode.SpecialCase, s: string): string {
384
+ if (isTurkishCase(c)) {
385
+ return mapTurkishCase(s, 'upper')
386
+ }
387
+ return ToTitle(s)
388
+ }
389
+
390
+ function isTurkishCase(c: unicode.SpecialCase): boolean {
391
+ return c === unicode.TurkishCase || c === unicode.AzeriCase
392
+ }
393
+
394
+ function mapTurkishCase(s: string, mode: 'upper' | 'lower'): string {
395
+ let out = ''
396
+ for (const char of s) {
397
+ if (mode === 'upper') {
398
+ if (char === 'i') {
399
+ out += 'İ'
400
+ continue
401
+ }
402
+ if (char === 'ı') {
403
+ out += 'I'
404
+ continue
405
+ }
406
+ out += char.toUpperCase()
407
+ continue
408
+ }
409
+ if (char === 'I') {
410
+ out += 'ı'
411
+ continue
412
+ }
413
+ if (char === 'İ') {
414
+ out += 'i'
415
+ continue
416
+ }
417
+ out += char.toLowerCase()
418
+ }
419
+ return out
420
+ }
421
+
422
+ export function ToValidUTF8(s: string, replacement: string): string {
423
+ void replacement
424
+ return s
425
+ }
426
+
427
+ // Title returns a copy of the string s with all Unicode letters that begin words mapped to their Unicode title case.
428
+ export function Title(s: string): string {
366
429
  return s
367
430
  .split(' ')
368
431
  .map((word) =>
@@ -373,11 +436,6 @@ export function ToTitle(s: string): string {
373
436
  .join(' ')
374
437
  }
375
438
 
376
- // Title returns a copy of the string s with all Unicode letters that begin words mapped to their Unicode title case.
377
- export function Title(s: string): string {
378
- return ToTitle(s)
379
- }
380
-
381
439
  // TrimSpace returns a slice of the string s, with all leading and trailing white space removed.
382
440
  export function TrimSpace(s: string): string {
383
441
  return s.trim()
@@ -62,7 +62,7 @@ export class Bool {
62
62
  new Bool(),
63
63
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Basic, name: "boolean" } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Basic, name: "boolean" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Basic, name: "boolean" } }, { name: "new", type: { kind: $.TypeKind.Basic, name: "boolean" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }],
64
64
  Bool,
65
- {"v": { kind: $.TypeKind.Basic, name: "number" }}
65
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "number" } }]
66
66
  );
67
67
  }
68
68
 
@@ -138,7 +138,7 @@ export class Pointer<T> {
138
138
  new Pointer(),
139
139
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Pointer, elemType: { kind: $.TypeKind.Interface, methods: [] } } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Pointer, elemType: { kind: $.TypeKind.Interface, methods: [] } } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Pointer, elemType: { kind: $.TypeKind.Interface, methods: [] } } }], returns: [{ type: { kind: $.TypeKind.Pointer, elemType: { kind: $.TypeKind.Interface, methods: [] } } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Pointer, elemType: { kind: $.TypeKind.Interface, methods: [] } } }, { name: "new", type: { kind: $.TypeKind.Pointer, elemType: { kind: $.TypeKind.Interface, methods: [] } } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }],
140
140
  Pointer,
141
- {"v": { kind: $.TypeKind.Basic, name: "Pointer" }}
141
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "Pointer" } }]
142
142
  );
143
143
  }
144
144
 
@@ -218,7 +218,7 @@ export class Int32 {
218
218
  new Int32(),
219
219
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Basic, name: "number" } }, { name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Add", args: [{ name: "delta", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "And", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Or", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }],
220
220
  Int32,
221
- {"v": { kind: $.TypeKind.Basic, name: "number" }}
221
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "number" } }]
222
222
  );
223
223
  }
224
224
 
@@ -298,7 +298,7 @@ export class Int64 {
298
298
  new Int64(),
299
299
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Basic, name: "number" } }, { name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Add", args: [{ name: "delta", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "And", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Or", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }],
300
300
  Int64,
301
- {"v": { kind: $.TypeKind.Basic, name: "number" }}
301
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "number" } }]
302
302
  );
303
303
  }
304
304
 
@@ -378,7 +378,7 @@ export class Uint32 {
378
378
  new Uint32(),
379
379
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Basic, name: "number" } }, { name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Add", args: [{ name: "delta", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "And", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Or", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }],
380
380
  Uint32,
381
- {"v": { kind: $.TypeKind.Basic, name: "number" }}
381
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "number" } }]
382
382
  );
383
383
  }
384
384
 
@@ -458,7 +458,7 @@ export class Uint64 {
458
458
  new Uint64(),
459
459
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Basic, name: "number" } }, { name: "new", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Add", args: [{ name: "delta", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "And", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }, { name: "Or", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "number" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "number" } }] }],
460
460
  Uint64,
461
- {"v": { kind: $.TypeKind.Basic, name: "number" }}
461
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "number" } }]
462
462
  );
463
463
  }
464
464
 
@@ -538,7 +538,7 @@ export class Uintptr {
538
538
  new Uintptr(),
539
539
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Basic, name: "uintptr" } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Basic, name: "uintptr" } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Basic, name: "uintptr" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "uintptr" } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Basic, name: "uintptr" } }, { name: "new", type: { kind: $.TypeKind.Basic, name: "uintptr" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }, { name: "Add", args: [{ name: "delta", type: { kind: $.TypeKind.Basic, name: "uintptr" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "uintptr" } }] }, { name: "And", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "uintptr" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "uintptr" } }] }, { name: "Or", args: [{ name: "mask", type: { kind: $.TypeKind.Basic, name: "uintptr" } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "uintptr" } }] }],
540
540
  Uintptr,
541
- {"v": { kind: $.TypeKind.Basic, name: "uintptr" }}
541
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Basic, name: "uintptr" } }]
542
542
  );
543
543
  }
544
544
 
@@ -570,7 +570,7 @@ class noCopy {
570
570
  new noCopy(),
571
571
  [{ name: "Lock", args: [], returns: [] }, { name: "Unlock", args: [], returns: [] }],
572
572
  noCopy,
573
- {}
573
+ []
574
574
  );
575
575
  }
576
576
 
@@ -595,6 +595,6 @@ class align64 {
595
595
  new align64(),
596
596
  [],
597
597
  align64,
598
- {}
598
+ []
599
599
  );
600
600
  }
@@ -96,7 +96,7 @@ export class Value {
96
96
  new Value(),
97
97
  [{ name: "Load", args: [], returns: [{ type: { kind: $.TypeKind.Interface, methods: [] } }] }, { name: "Store", args: [{ name: "val", type: { kind: $.TypeKind.Interface, methods: [] } }], returns: [] }, { name: "Swap", args: [{ name: "new", type: { kind: $.TypeKind.Interface, methods: [] } }], returns: [{ type: { kind: $.TypeKind.Interface, methods: [] } }] }, { name: "CompareAndSwap", args: [{ name: "old", type: { kind: $.TypeKind.Interface, methods: [] } }, { name: "new", type: { kind: $.TypeKind.Interface, methods: [] } }], returns: [{ type: { kind: $.TypeKind.Basic, name: "boolean" } }] }],
98
98
  Value,
99
- {"v": { kind: $.TypeKind.Interface, methods: [] }}
99
+ [{ name: "v", key: "v", type: { kind: $.TypeKind.Interface, methods: [] } }]
100
100
  );
101
101
  }
102
102
 
@@ -143,7 +143,7 @@ class efaceWords {
143
143
  new efaceWords(),
144
144
  [],
145
145
  efaceWords,
146
- {"data": { kind: $.TypeKind.Basic, name: "Pointer" }, "typ": { kind: $.TypeKind.Basic, name: "Pointer" }}
146
+ [{ name: "data", key: "data", type: { kind: $.TypeKind.Basic, name: "Pointer" } }, { name: "typ", key: "typ", type: { kind: $.TypeKind.Basic, name: "Pointer" } }]
147
147
  );
148
148
  }
149
149
 
package/gs/syscall/env.ts CHANGED
@@ -1,47 +1,62 @@
1
1
  import * as $ from '@goscript/builtin/index.js'
2
2
 
3
+ type ProcessEnv = Record<string, string | undefined>
4
+
5
+ interface ProcessLike {
6
+ env?: ProcessEnv
7
+ }
8
+
9
+ function hostEnv(): ProcessEnv | undefined {
10
+ return (globalThis as { process?: ProcessLike }).process?.env
11
+ }
12
+
3
13
  // Environment variable functions using Node.js/browser APIs
4
14
  export function Getenv(key: string): [string, boolean] {
5
- if (typeof process !== 'undefined' && process.env) {
6
- const value = process.env[key]
15
+ const env = hostEnv()
16
+ if (env !== undefined) {
17
+ const value = env[key]
7
18
  return value !== undefined ? [value, true] : ['', false]
8
19
  }
9
20
  return ['', false]
10
21
  }
11
22
 
12
23
  export function Setenv(key: string, value: string): $.GoError {
13
- if (typeof process !== 'undefined' && process.env) {
14
- process.env[key] = value
24
+ const env = hostEnv()
25
+ if (env !== undefined) {
26
+ env[key] = value
15
27
  return null
16
28
  }
17
29
  return { Error: () => 'setenv not supported' }
18
30
  }
19
31
 
20
32
  export function Unsetenv(key: string): $.GoError {
21
- if (typeof process !== 'undefined' && process.env) {
22
- delete process.env[key]
33
+ const env = hostEnv()
34
+ if (env !== undefined) {
35
+ delete env[key]
23
36
  return null
24
37
  }
25
38
  return { Error: () => 'unsetenv not supported' }
26
39
  }
27
40
 
28
41
  export function Clearenv(): void {
29
- if (typeof process !== 'undefined' && process.env) {
30
- for (const key in process.env) {
31
- delete process.env[key]
42
+ const env = hostEnv()
43
+ if (env !== undefined) {
44
+ for (const key in env) {
45
+ delete env[key]
32
46
  }
33
47
  }
34
48
  }
35
49
 
36
50
  export function Environ(): $.Slice<string> {
37
- if (typeof process !== 'undefined' && process.env) {
38
- const env: string[] = []
39
- for (const [key, value] of Object.entries(process.env)) {
51
+ const host = hostEnv()
52
+ if (host !== undefined) {
53
+ const values: string[] = []
54
+ for (const [key, value] of Object.entries(host)) {
40
55
  if (value !== undefined) {
41
- env.push(`${key}=${value}`)
56
+ values.push(`${key}=${value}`)
42
57
  }
43
58
  }
44
- return $.arrayToSlice(env)
59
+ return $.arrayToSlice(values)
45
60
  }
46
61
  return $.arrayToSlice([])
47
62
  }
@@ -108,6 +108,73 @@ describe('testing.T', () => {
108
108
  )
109
109
  })
110
110
 
111
+ it('propagates process exits out of subtests without cleanup', async () => {
112
+ const t = new T('root')
113
+ const exit = { __goscriptExitCode: 7 }
114
+ let cleaned = false
115
+
116
+ await expect(
117
+ t.Run('child', (child) => {
118
+ child.Cleanup(() => {
119
+ cleaned = true
120
+ })
121
+ throw exit
122
+ }),
123
+ ).rejects.toBe(exit)
124
+ expect(cleaned).toBe(false)
125
+ })
126
+
127
+ it('propagates process exits out of subtest cleanups', async () => {
128
+ const t = new T('root')
129
+ const exit = { __goscriptExitCode: 8 }
130
+
131
+ await expect(
132
+ t.Run('child', (child) => {
133
+ child.Cleanup(() => {
134
+ throw exit
135
+ })
136
+ }),
137
+ ).rejects.toBe(exit)
138
+ expect(t.Failed()).toBe(false)
139
+ })
140
+
141
+ it('propagates process exits out of package tests without cleanup', async () => {
142
+ const exit = { __goscriptExitCode: 9 }
143
+ let cleaned = false
144
+
145
+ await expect(
146
+ runTests('example.test/exit', [
147
+ {
148
+ name: 'TestExit',
149
+ fn: (t) => {
150
+ t.Cleanup(() => {
151
+ cleaned = true
152
+ })
153
+ throw exit
154
+ },
155
+ },
156
+ ]),
157
+ ).rejects.toBe(exit)
158
+ expect(cleaned).toBe(false)
159
+ })
160
+
161
+ it('propagates process exits out of package test cleanups', async () => {
162
+ const exit = { __goscriptExitCode: 10 }
163
+
164
+ await expect(
165
+ runTests('example.test/exit-cleanup', [
166
+ {
167
+ name: 'TestExitCleanup',
168
+ fn: (t) => {
169
+ t.Cleanup(() => {
170
+ throw exit
171
+ })
172
+ },
173
+ },
174
+ ]),
175
+ ).rejects.toBe(exit)
176
+ })
177
+
111
178
  it('returns a non-nil context', () => {
112
179
  const t = new T('root')
113
180
 
@@ -1,7 +1,3 @@
1
- import * as nodeFS from 'node:fs'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
-
5
1
  import * as context from '@goscript/context/index.js'
6
2
 
7
3
  export type TestFunc = (t: T) => void | Promise<void>
@@ -24,6 +20,18 @@ export type RunResult = {
24
20
  skipped: number
25
21
  }
26
22
 
23
+ interface HostProcess {
24
+ env?: Record<string, string | undefined>
25
+ cwd?: () => string
26
+ chdir?: (dir: string) => void
27
+ getBuiltinModule?: (name: string) => unknown
28
+ }
29
+
30
+ interface HostGlobal {
31
+ process?: HostProcess
32
+ require?: (name: string) => unknown
33
+ }
34
+
27
35
  class TestControl extends Error {
28
36
  public readonly kind: 'fatal' | 'skip'
29
37
 
@@ -127,6 +135,9 @@ export class T {
127
135
  try {
128
136
  await fn(child)
129
137
  } catch (err) {
138
+ if (isProcessExitError(err)) {
139
+ throw err
140
+ }
130
141
  if (err instanceof TestControl && err.kind === 'skip') {
131
142
  // A skipped subtest is still a successful Run result.
132
143
  } else {
@@ -139,6 +150,9 @@ export class T {
139
150
  try {
140
151
  await child.runCleanups()
141
152
  } catch (err) {
153
+ if (isProcessExitError(err)) {
154
+ throw err
155
+ }
142
156
  child.Fail()
143
157
  if (!(err instanceof TestControl)) {
144
158
  child.Log(formatValue(err))
@@ -153,9 +167,21 @@ export class T {
153
167
  }
154
168
 
155
169
  public TempDir(): string {
156
- const path = (nodeFS as any).mkdtempSync(
157
- join(
158
- tmpdir(),
170
+ const fs = requireHostModule<{
171
+ mkdtempSync(prefix: string): string
172
+ rmSync(path: string, opts: { force: boolean; recursive: boolean }): void
173
+ }>('node:fs', 'testing.TempDir')
174
+ const os = requireHostModule<{ tmpdir(): string }>(
175
+ 'node:os',
176
+ 'testing.TempDir',
177
+ )
178
+ const pathMod = requireHostModule<{ join(...parts: string[]): string }>(
179
+ 'node:path',
180
+ 'testing.TempDir',
181
+ )
182
+ const path = fs.mkdtempSync(
183
+ pathMod.join(
184
+ os.tmpdir(),
159
185
  'goscript-test-' +
160
186
  this.testName.replace(/[^A-Za-z0-9_.-]/g, '_') +
161
187
  '-' +
@@ -164,7 +190,7 @@ export class T {
164
190
  )
165
191
  this.tempDirs.push(path)
166
192
  this.Cleanup(() => {
167
- ;(nodeFS as any).rmSync(path, { force: true, recursive: true })
193
+ fs.rmSync(path, { force: true, recursive: true })
168
194
  })
169
195
  return path
170
196
  }
@@ -172,12 +198,9 @@ export class T {
172
198
  public Parallel(): void {}
173
199
 
174
200
  public Setenv(key: string, value: string): void {
175
- const proc = (globalThis as any).process as
176
- | { env?: Record<string, string | undefined> }
177
- | undefined
178
- const env = proc?.env
201
+ const env = (globalThis as HostGlobal).process?.env
179
202
  if (env === undefined) {
180
- return
203
+ throw new Error('testing.Setenv is not supported without a host process')
181
204
  }
182
205
  const oldValue = env[key]
183
206
  env[key] = value
@@ -191,11 +214,9 @@ export class T {
191
214
  }
192
215
 
193
216
  public Chdir(dir: string): void {
194
- const proc = (globalThis as any).process as
195
- | { cwd?: () => string; chdir?: (dir: string) => void }
196
- | undefined
217
+ const proc = (globalThis as HostGlobal).process
197
218
  if (proc?.cwd === undefined || proc.chdir === undefined) {
198
- return
219
+ throw new Error('testing.Chdir is not supported without a host process')
199
220
  }
200
221
  const oldDir = proc.cwd()
201
222
  proc.chdir(dir)
@@ -249,6 +270,9 @@ export class B extends T {
249
270
  try {
250
271
  await fn(child)
251
272
  } catch (err) {
273
+ if (isProcessExitError(err)) {
274
+ throw err
275
+ }
252
276
  child.Error(err)
253
277
  }
254
278
  if (child.Failed()) {
@@ -328,6 +352,9 @@ export async function runTests(
328
352
  try {
329
353
  await test.fn(t)
330
354
  } catch (err) {
355
+ if (isProcessExitError(err)) {
356
+ throw err
357
+ }
331
358
  if (err instanceof TestControl && err.kind === 'skip') {
332
359
  skipped++
333
360
  } else {
@@ -336,9 +363,8 @@ export async function runTests(
336
363
  t.Log(formatValue(err))
337
364
  }
338
365
  }
339
- } finally {
340
- await t.runCleanups()
341
366
  }
367
+ await t.runCleanups()
342
368
  const elapsed = ((Date.now() - start) / 1000).toFixed(2)
343
369
  if (t.Skipped()) {
344
370
  if (options.verbose) {
@@ -383,6 +409,48 @@ function formatMessage(format: string, args: unknown[]): string {
383
409
  })
384
410
  }
385
411
 
412
+ function requireHostModule<T>(name: string, api: string): T {
413
+ const fromProcess = (globalThis as HostGlobal).process?.getBuiltinModule?.(
414
+ name,
415
+ )
416
+ if (fromProcess !== undefined && fromProcess !== null) {
417
+ return fromProcess as T
418
+ }
419
+ const req = hostRequire()
420
+ if (req !== undefined) {
421
+ return req(name) as T
422
+ }
423
+ throw new Error(
424
+ api + ' is not supported without Node-compatible host modules',
425
+ )
426
+ }
427
+
428
+ function hostRequire(): ((name: string) => unknown) | undefined {
429
+ const globalRequire = (globalThis as HostGlobal).require
430
+ if (typeof globalRequire === 'function') {
431
+ return globalRequire
432
+ }
433
+ try {
434
+ const req = eval(
435
+ 'typeof require === "function" ? require : undefined',
436
+ ) as unknown
437
+ if (typeof req === 'function') {
438
+ return req as (name: string) => unknown
439
+ }
440
+ } catch {
441
+ return undefined
442
+ }
443
+ return undefined
444
+ }
445
+
446
+ function isProcessExitError(err: unknown): boolean {
447
+ if (err === null || typeof err !== 'object') {
448
+ return false
449
+ }
450
+ const code = (err as { __goscriptExitCode?: unknown }).__goscriptExitCode
451
+ return typeof code === 'number'
452
+ }
453
+
386
454
  function formatValue(value: unknown): string {
387
455
  if (value instanceof Error) {
388
456
  return value.message