goscript 0.0.84 → 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 +23 -0
- 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 +15 -1
- package/dist/gs/builtin/hostio.js +134 -49
- package/dist/gs/builtin/hostio.js.map +1 -1
- 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/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.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.js +9 -9
- 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 +27 -0
- package/gs/builtin/hostio.test.ts +177 -0
- package/gs/builtin/hostio.ts +171 -56
- package/gs/builtin/index.ts +1 -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/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 +50 -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 +9 -9
- 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,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { CompareEqualVT } from './index.js'
|
|
4
|
+
|
|
5
|
+
class TestValue {
|
|
6
|
+
constructor(private readonly value: string) {}
|
|
7
|
+
|
|
8
|
+
EqualVT(other: TestValue): boolean {
|
|
9
|
+
return this.value == other.value
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('protobuf-go-lite EqualVT helpers', () => {
|
|
14
|
+
it('accepts compiler-emitted runtime type arguments', () => {
|
|
15
|
+
const equal = CompareEqualVT<TestValue>({
|
|
16
|
+
T: { zero: () => null },
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
expect(equal(new TestValue('a'), new TestValue('a'))).toBe(true)
|
|
20
|
+
expect(equal(new TestValue('a'), new TestValue('b'))).toBe(false)
|
|
21
|
+
expect(equal(null, null)).toBe(true)
|
|
22
|
+
})
|
|
23
|
+
})
|
|
@@ -9,6 +9,8 @@ export function IsEqualVT<T extends EqualVT<T>>(t1: T | null, t2: T | null): boo
|
|
|
9
9
|
return t1.EqualVT(t2)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function CompareEqualVT<T extends EqualVT<T>>(
|
|
12
|
+
export function CompareEqualVT<T extends EqualVT<T>>(
|
|
13
|
+
_typeArgs?: unknown,
|
|
14
|
+
): (t1: T | null, t2: T | null) => boolean {
|
|
13
15
|
return (t1, t2) => IsEqualVT(t1, t2)
|
|
14
16
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
import * as token from '@goscript/go/token/index.js'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
ErrorList_Add,
|
|
8
|
+
ErrorList_Error,
|
|
9
|
+
ErrorList_Len,
|
|
10
|
+
ErrorList_RemoveMultiples,
|
|
11
|
+
type ErrorList,
|
|
12
|
+
} from './index.js'
|
|
13
|
+
|
|
14
|
+
describe('go/scanner override', () => {
|
|
15
|
+
it('adds and formats scanner errors', () => {
|
|
16
|
+
const list: $.VarRef<ErrorList> = $.varRef(null)
|
|
17
|
+
|
|
18
|
+
ErrorList_Add(
|
|
19
|
+
list,
|
|
20
|
+
$.markAsStructValue(
|
|
21
|
+
new token.Position({
|
|
22
|
+
Filename: 'test.go',
|
|
23
|
+
Line: 1,
|
|
24
|
+
Column: 1,
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
'test error',
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
expect(ErrorList_Len(list.value)).toBe(1)
|
|
31
|
+
expect(ErrorList_Error(list.value)).toBe('test.go:1:1: test error')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('removes repeated line errors', () => {
|
|
35
|
+
const list: $.VarRef<ErrorList> = $.varRef(null)
|
|
36
|
+
const pos = $.markAsStructValue(
|
|
37
|
+
new token.Position({
|
|
38
|
+
Filename: 'test.go',
|
|
39
|
+
Line: 1,
|
|
40
|
+
Column: 1,
|
|
41
|
+
}),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
ErrorList_Add(list, pos, 'first')
|
|
45
|
+
ErrorList_Add(list, pos, 'second')
|
|
46
|
+
ErrorList_RemoveMultiples(list)
|
|
47
|
+
|
|
48
|
+
expect(ErrorList_Len(list.value)).toBe(1)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
+
import * as token from '@goscript/go/token/index.js'
|
|
3
|
+
|
|
4
|
+
export class Error {
|
|
5
|
+
public get Pos(): token.Position {
|
|
6
|
+
return this._fields.Pos.value
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public set Pos(value: token.Position) {
|
|
10
|
+
this._fields.Pos.value = value
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public get Msg(): string {
|
|
14
|
+
return this._fields.Msg.value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public set Msg(value: string) {
|
|
18
|
+
this._fields.Msg.value = value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public _fields: {
|
|
22
|
+
Pos: $.VarRef<token.Position>
|
|
23
|
+
Msg: $.VarRef<string>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
constructor(init?: Partial<{ Pos: token.Position; Msg: string }>) {
|
|
27
|
+
this._fields = {
|
|
28
|
+
Pos: $.varRef(init?.Pos ?? new token.Position()),
|
|
29
|
+
Msg: $.varRef(init?.Msg ?? ''),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public clone(): Error {
|
|
34
|
+
return $.markAsStructValue(
|
|
35
|
+
new Error({
|
|
36
|
+
Pos: this.Pos.clone(),
|
|
37
|
+
Msg: this.Msg,
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public Error(): string {
|
|
43
|
+
return Error_Error(this)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static __typeInfo = $.registerStructType(
|
|
47
|
+
'go/scanner.Error',
|
|
48
|
+
new Error(),
|
|
49
|
+
[
|
|
50
|
+
{
|
|
51
|
+
name: 'Error',
|
|
52
|
+
args: [],
|
|
53
|
+
returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
Error,
|
|
57
|
+
{
|
|
58
|
+
Pos: { type: 'go/token.Position' },
|
|
59
|
+
Msg: { kind: $.TypeKind.Basic, name: 'string' },
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type ErrorList = $.Slice<Error | null>
|
|
65
|
+
|
|
66
|
+
export const ScanComments = 1
|
|
67
|
+
|
|
68
|
+
export function Error_Error(err: Error): string {
|
|
69
|
+
if (!err.Pos.IsValid()) {
|
|
70
|
+
return err.Msg
|
|
71
|
+
}
|
|
72
|
+
return `${err.Pos.String()}: ${err.Msg}`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function ErrorList_Add(
|
|
76
|
+
list: $.VarRef<ErrorList>,
|
|
77
|
+
pos: token.Position,
|
|
78
|
+
msg: string,
|
|
79
|
+
): void {
|
|
80
|
+
list.value = $.append(
|
|
81
|
+
list.value,
|
|
82
|
+
$.markAsStructValue(new Error({ Pos: pos, Msg: msg })),
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function ErrorList_Len(list: ErrorList): number {
|
|
87
|
+
return $.len(list)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function ErrorList_Less(list: ErrorList, i: number, j: number): boolean {
|
|
91
|
+
const left = list![i]!
|
|
92
|
+
const right = list![j]!
|
|
93
|
+
if (left.Pos.Filename !== right.Pos.Filename) {
|
|
94
|
+
return left.Pos.Filename < right.Pos.Filename
|
|
95
|
+
}
|
|
96
|
+
if (left.Pos.Line !== right.Pos.Line) {
|
|
97
|
+
return left.Pos.Line < right.Pos.Line
|
|
98
|
+
}
|
|
99
|
+
if (left.Pos.Column !== right.Pos.Column) {
|
|
100
|
+
return left.Pos.Column < right.Pos.Column
|
|
101
|
+
}
|
|
102
|
+
return left.Msg < right.Msg
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function ErrorList_Swap(list: ErrorList, i: number, j: number): void {
|
|
106
|
+
const first = list![i]
|
|
107
|
+
list![i] = list![j]
|
|
108
|
+
list![j] = first
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function ErrorList_Sort(list: ErrorList): void {
|
|
112
|
+
list?.sort((left, right) => {
|
|
113
|
+
if (left === null || right === null) {
|
|
114
|
+
if (left === right) {
|
|
115
|
+
return 0
|
|
116
|
+
}
|
|
117
|
+
if (left === null) {
|
|
118
|
+
return -1
|
|
119
|
+
}
|
|
120
|
+
return 1
|
|
121
|
+
}
|
|
122
|
+
if (ErrorList_Less([left, right], 0, 1)) {
|
|
123
|
+
return -1
|
|
124
|
+
}
|
|
125
|
+
if (ErrorList_Less([right, left], 0, 1)) {
|
|
126
|
+
return 1
|
|
127
|
+
}
|
|
128
|
+
return 0
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function ErrorList_RemoveMultiples(list: $.VarRef<ErrorList>): void {
|
|
133
|
+
ErrorList_Sort(list.value)
|
|
134
|
+
const seen = new Set<string>()
|
|
135
|
+
list.value = $.asArray(list.value).filter((err) => {
|
|
136
|
+
if (err === null) {
|
|
137
|
+
return false
|
|
138
|
+
}
|
|
139
|
+
const key = `${err.Pos.Filename}:${err.Pos.Line}`
|
|
140
|
+
if (seen.has(key)) {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
seen.add(key)
|
|
144
|
+
return true
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function ErrorList_Error(list: ErrorList): string {
|
|
149
|
+
const errors = $.asArray(list).filter((err) => err !== null)
|
|
150
|
+
if (errors.length === 0) {
|
|
151
|
+
return 'no errors'
|
|
152
|
+
}
|
|
153
|
+
if (errors.length === 1) {
|
|
154
|
+
return errors[0]!.Error()
|
|
155
|
+
}
|
|
156
|
+
return `${errors[0]!.Error()} (and ${errors.length - 1} more errors)`
|
|
157
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
4
|
+
|
|
5
|
+
import { Position, Position_IsValid, Position_String } from './index.js'
|
|
6
|
+
|
|
7
|
+
describe('go/token override', () => {
|
|
8
|
+
it('models Position values', () => {
|
|
9
|
+
const pos = $.markAsStructValue(
|
|
10
|
+
new Position({
|
|
11
|
+
Filename: 'test.go',
|
|
12
|
+
Line: 3,
|
|
13
|
+
Column: 9,
|
|
14
|
+
}),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
expect(Position_IsValid(pos)).toBe(true)
|
|
18
|
+
expect(Position_String(pos)).toBe('test.go:3:9')
|
|
19
|
+
expect(pos.clone().String()).toBe('test.go:3:9')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as $ from '@goscript/builtin/index.js'
|
|
2
|
+
|
|
3
|
+
export type Pos = number
|
|
4
|
+
|
|
5
|
+
export const NoPos: Pos = 0
|
|
6
|
+
|
|
7
|
+
export class Position {
|
|
8
|
+
public get Filename(): string {
|
|
9
|
+
return this._fields.Filename.value
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public set Filename(value: string) {
|
|
13
|
+
this._fields.Filename.value = value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public get Offset(): number {
|
|
17
|
+
return this._fields.Offset.value
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public set Offset(value: number) {
|
|
21
|
+
this._fields.Offset.value = value
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public get Line(): number {
|
|
25
|
+
return this._fields.Line.value
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public set Line(value: number) {
|
|
29
|
+
this._fields.Line.value = value
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public get Column(): number {
|
|
33
|
+
return this._fields.Column.value
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public set Column(value: number) {
|
|
37
|
+
this._fields.Column.value = value
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public _fields: {
|
|
41
|
+
Filename: $.VarRef<string>
|
|
42
|
+
Offset: $.VarRef<number>
|
|
43
|
+
Line: $.VarRef<number>
|
|
44
|
+
Column: $.VarRef<number>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
constructor(
|
|
48
|
+
init?: Partial<{
|
|
49
|
+
Filename: string
|
|
50
|
+
Offset: number
|
|
51
|
+
Line: number
|
|
52
|
+
Column: number
|
|
53
|
+
}>,
|
|
54
|
+
) {
|
|
55
|
+
this._fields = {
|
|
56
|
+
Filename: $.varRef(init?.Filename ?? ''),
|
|
57
|
+
Offset: $.varRef(init?.Offset ?? 0),
|
|
58
|
+
Line: $.varRef(init?.Line ?? 0),
|
|
59
|
+
Column: $.varRef(init?.Column ?? 0),
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
public clone(): Position {
|
|
64
|
+
return $.markAsStructValue(
|
|
65
|
+
new Position({
|
|
66
|
+
Filename: this.Filename,
|
|
67
|
+
Offset: this.Offset,
|
|
68
|
+
Line: this.Line,
|
|
69
|
+
Column: this.Column,
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public IsValid(): boolean {
|
|
75
|
+
return Position_IsValid(this)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public String(): string {
|
|
79
|
+
return Position_String(this)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static __typeInfo = $.registerStructType(
|
|
83
|
+
'go/token.Position',
|
|
84
|
+
new Position(),
|
|
85
|
+
[
|
|
86
|
+
{
|
|
87
|
+
name: 'IsValid',
|
|
88
|
+
args: [],
|
|
89
|
+
returns: [{ type: { kind: $.TypeKind.Basic, name: 'bool' } }],
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'String',
|
|
93
|
+
args: [],
|
|
94
|
+
returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
Position,
|
|
98
|
+
{
|
|
99
|
+
Filename: { kind: $.TypeKind.Basic, name: 'string' },
|
|
100
|
+
Offset: { kind: $.TypeKind.Basic, name: 'int' },
|
|
101
|
+
Line: { kind: $.TypeKind.Basic, name: 'int' },
|
|
102
|
+
Column: { kind: $.TypeKind.Basic, name: 'int' },
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function Position_IsValid(pos: Position): boolean {
|
|
108
|
+
return pos.Line > 0
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function Position_String(pos: Position): string {
|
|
112
|
+
if (!Position_IsValid(pos)) {
|
|
113
|
+
return '-'
|
|
114
|
+
}
|
|
115
|
+
const filename = pos.Filename === '' ? '<input>' : pos.Filename
|
|
116
|
+
if (pos.Column > 0) {
|
|
117
|
+
return `${filename}:${pos.Line}:${pos.Column}`
|
|
118
|
+
}
|
|
119
|
+
return `${filename}:${pos.Line}`
|
|
120
|
+
}
|
|
@@ -115,6 +115,56 @@ describe('os stdio', () => {
|
|
|
115
115
|
expect(writeSync).toHaveBeenCalledTimes(1)
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
+
it('falls back to process builtin fs when Deno lacks sync stdio', () => {
|
|
119
|
+
const readSync = vi.fn(
|
|
120
|
+
(
|
|
121
|
+
_fd: number,
|
|
122
|
+
buffer: Uint8Array,
|
|
123
|
+
_offset?: number,
|
|
124
|
+
_length?: number,
|
|
125
|
+
_position?: number | null,
|
|
126
|
+
) => {
|
|
127
|
+
buffer.set([5, 6], 0)
|
|
128
|
+
return 2
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
const writeSync = vi.fn(
|
|
132
|
+
(
|
|
133
|
+
_fd: number,
|
|
134
|
+
_buffer: Uint8Array,
|
|
135
|
+
_offset?: number,
|
|
136
|
+
length?: number,
|
|
137
|
+
_position?: number | null,
|
|
138
|
+
) => length ?? 0,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
;(globalThis as any).Deno = {
|
|
142
|
+
stdin: {},
|
|
143
|
+
stdout: {},
|
|
144
|
+
}
|
|
145
|
+
;(globalThis as any).process = {
|
|
146
|
+
getBuiltinModule: vi.fn(() => ({
|
|
147
|
+
readSync,
|
|
148
|
+
writeSync,
|
|
149
|
+
})),
|
|
150
|
+
}
|
|
151
|
+
resetHostRuntimeForTests()
|
|
152
|
+
|
|
153
|
+
const input = NewFile(0, 'stdin')
|
|
154
|
+
const output = NewFile(1, 'stdout')
|
|
155
|
+
const readBuf = new Uint8Array(2)
|
|
156
|
+
|
|
157
|
+
const [readN, readErr] = input!.Read(readBuf)
|
|
158
|
+
expect(readN).toBe(2)
|
|
159
|
+
expect(readErr).toBeNull()
|
|
160
|
+
expect(Array.from(readBuf)).toEqual([5, 6])
|
|
161
|
+
|
|
162
|
+
const [writeN, writeErr] = output!.Write(new Uint8Array([7, 8, 9]))
|
|
163
|
+
expect(writeN).toBe(3)
|
|
164
|
+
expect(writeErr).toBeNull()
|
|
165
|
+
expect(writeSync).toHaveBeenCalledTimes(1)
|
|
166
|
+
})
|
|
167
|
+
|
|
118
168
|
it('returns EOF on zero-byte reads and ErrClosed after Close', () => {
|
|
119
169
|
const stdinReadSync = vi.fn(() => 0)
|
|
120
170
|
|
package/gs/os/meta.json
CHANGED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { basename, join } from 'node:path'
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { tmpdir } from 'node:os'
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
5
|
+
|
|
6
|
+
import { ErrUnimplemented } from './error.gs.js'
|
|
7
|
+
import { CreateTemp, MkdirTemp } from './tempfile.gs.js'
|
|
8
|
+
|
|
9
|
+
const tempRoots: string[] = []
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
vi.restoreAllMocks()
|
|
13
|
+
vi.unstubAllGlobals()
|
|
14
|
+
for (const root of tempRoots.splice(0)) {
|
|
15
|
+
rmSync(root, { force: true, recursive: true })
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
function makeTempRoot(): string {
|
|
20
|
+
const root = mkdtempSync(join(tmpdir(), 'goscript-os-temp-'))
|
|
21
|
+
tempRoots.push(root)
|
|
22
|
+
return root
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function stubRandomBytes(bytes: number[]): void {
|
|
26
|
+
vi.stubGlobal('crypto', {
|
|
27
|
+
getRandomValues: vi.fn((dst: Uint8Array) => {
|
|
28
|
+
for (let i = 0; i < dst.length; i++) {
|
|
29
|
+
dst[i] = bytes[i] ?? 0
|
|
30
|
+
}
|
|
31
|
+
return dst
|
|
32
|
+
}),
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function forbidMathRandom(): void {
|
|
37
|
+
vi.spyOn(Math, 'random').mockImplementation(() => {
|
|
38
|
+
throw new Error('os temp names must not use Math.random')
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('os temp files', () => {
|
|
43
|
+
it('uses Web Crypto bytes for temp names', () => {
|
|
44
|
+
const root = makeTempRoot()
|
|
45
|
+
stubRandomBytes([0x78, 0x56, 0x34, 0x12])
|
|
46
|
+
forbidMathRandom()
|
|
47
|
+
|
|
48
|
+
const [dir, dirErr] = MkdirTemp(root, 'dir-*.tmp')
|
|
49
|
+
expect(dirErr).toBeNull()
|
|
50
|
+
expect(basename(dir)).toBe('dir-305419896.tmp')
|
|
51
|
+
expect(existsSync(dir)).toBe(true)
|
|
52
|
+
|
|
53
|
+
const [file, fileErr] = CreateTemp(root, 'file-*.tmp')
|
|
54
|
+
expect(fileErr).toBeNull()
|
|
55
|
+
expect(file).not.toBeNull()
|
|
56
|
+
expect(basename(file!.Name())).toBe('file-305419896.tmp')
|
|
57
|
+
expect(existsSync(file!.Name())).toBe(true)
|
|
58
|
+
file!.Close()
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('does not truncate an existing path after a random collision', () => {
|
|
62
|
+
const root = makeTempRoot()
|
|
63
|
+
const existing = join(root, 'file-305419896.tmp')
|
|
64
|
+
writeFileSync(existing, 'keep')
|
|
65
|
+
stubRandomBytes([0x78, 0x56, 0x34, 0x12])
|
|
66
|
+
forbidMathRandom()
|
|
67
|
+
|
|
68
|
+
const [file, err] = CreateTemp(root, 'file-*.tmp')
|
|
69
|
+
|
|
70
|
+
expect(file).toBeNull()
|
|
71
|
+
expect(err).not.toBeNull()
|
|
72
|
+
expect(readFileSync(existing, 'utf8')).toBe('keep')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('reports ErrUnimplemented without a secure random source', () => {
|
|
76
|
+
const root = makeTempRoot()
|
|
77
|
+
vi.stubGlobal('crypto', {})
|
|
78
|
+
forbidMathRandom()
|
|
79
|
+
|
|
80
|
+
const [dir, err] = MkdirTemp(root, 'dir-*')
|
|
81
|
+
|
|
82
|
+
expect(dir).toBe('')
|
|
83
|
+
expect(err).toBe(ErrUnimplemented)
|
|
84
|
+
})
|
|
85
|
+
})
|
package/gs/os/tempfile.gs.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as $ from "@goscript/builtin/index.js";
|
|
2
2
|
import { ErrUnimplemented } from "./error.gs.js";
|
|
3
3
|
import { TempDir } from "./file_constants_js.gs.js";
|
|
4
|
-
import {
|
|
5
|
-
import { File } from "./types_js.gs.js";
|
|
4
|
+
import { Mkdir } from "./file_js.gs.js";
|
|
5
|
+
import { createHostFile, File, getDeno, getNodeFS, newHostError } from "./types_js.gs.js";
|
|
6
6
|
|
|
7
7
|
export function joinPath(dir: string, file: string): string {
|
|
8
8
|
if (dir === "" || dir === ".") {
|
|
@@ -20,33 +20,93 @@ export function joinPath(dir: string, file: string): string {
|
|
|
20
20
|
return dir + "/" + file
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function nextTempPath(dir: string, pattern: string): string {
|
|
23
|
+
function nextTempPath(dir: string, pattern: string): [string, $.GoError] {
|
|
24
24
|
const baseDir = dir === "" ? TempDir() : dir
|
|
25
|
-
const suffix
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const [suffix, err] = nextRandom()
|
|
26
|
+
if (err !== null) {
|
|
27
|
+
return ["", err]
|
|
28
|
+
}
|
|
29
|
+
const star = pattern.lastIndexOf("*")
|
|
30
|
+
const name =
|
|
31
|
+
star >= 0
|
|
32
|
+
? pattern.slice(0, star) + suffix + pattern.slice(star + 1)
|
|
33
|
+
: pattern + suffix
|
|
34
|
+
return [joinPath(baseDir, name), null]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function nextRandom(): [string, $.GoError] {
|
|
38
|
+
const crypto = globalThis.crypto
|
|
39
|
+
if (!crypto || typeof crypto.getRandomValues !== "function") {
|
|
40
|
+
return ["", ErrUnimplemented]
|
|
41
|
+
}
|
|
42
|
+
const bytes = new Uint8Array(4)
|
|
43
|
+
crypto.getRandomValues(bytes)
|
|
44
|
+
const value =
|
|
45
|
+
bytes[0] |
|
|
46
|
+
(bytes[1] << 8) |
|
|
47
|
+
(bytes[2] << 16) |
|
|
48
|
+
(bytes[3] << 24)
|
|
49
|
+
return [(value >>> 0).toString(10), null]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createTempFile(path: string): [File | null, $.GoError] {
|
|
53
|
+
const denoObj = getDeno()
|
|
54
|
+
if (denoObj?.openSync) {
|
|
55
|
+
try {
|
|
56
|
+
const handle = denoObj.openSync(path, {
|
|
57
|
+
createNew: true,
|
|
58
|
+
mode: 0o600,
|
|
59
|
+
read: true,
|
|
60
|
+
write: true,
|
|
61
|
+
})
|
|
62
|
+
return [createHostFile(path, handle?.rid ?? -1, handle), null]
|
|
63
|
+
} catch (err) {
|
|
64
|
+
return [null, newHostError(err)]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const nodeFS = getNodeFS()
|
|
69
|
+
if (nodeFS?.openSync) {
|
|
70
|
+
try {
|
|
71
|
+
return [createHostFile(path, nodeFS.openSync(path, "wx+", 0o600)), null]
|
|
72
|
+
} catch (err) {
|
|
73
|
+
return [null, newHostError(err)]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return [null, ErrUnimplemented]
|
|
28
78
|
}
|
|
29
79
|
|
|
30
80
|
export function CreateTemp(dir: string, pattern: string): [File | null, $.GoError] {
|
|
31
81
|
const template = pattern === "" ? "tmp-*" : pattern
|
|
82
|
+
let lastErr: $.GoError = ErrUnimplemented
|
|
32
83
|
for (let i = 0; i < 16; i++) {
|
|
33
|
-
const path = nextTempPath(dir, template)
|
|
34
|
-
|
|
84
|
+
const [path, randErr] = nextTempPath(dir, template)
|
|
85
|
+
if (randErr !== null) {
|
|
86
|
+
return [null, randErr]
|
|
87
|
+
}
|
|
88
|
+
const [file, err] = createTempFile(path)
|
|
35
89
|
if (err === null) {
|
|
36
90
|
return [file, null]
|
|
37
91
|
}
|
|
92
|
+
lastErr = err
|
|
38
93
|
}
|
|
39
|
-
return [null,
|
|
94
|
+
return [null, lastErr]
|
|
40
95
|
}
|
|
41
96
|
|
|
42
97
|
export function MkdirTemp(dir: string, pattern: string): [string, $.GoError] {
|
|
43
98
|
const template = pattern === "" ? "tmp-*" : pattern
|
|
99
|
+
let lastErr: $.GoError = ErrUnimplemented
|
|
44
100
|
for (let i = 0; i < 16; i++) {
|
|
45
|
-
const path = nextTempPath(dir, template)
|
|
101
|
+
const [path, randErr] = nextTempPath(dir, template)
|
|
102
|
+
if (randErr !== null) {
|
|
103
|
+
return ["", randErr]
|
|
104
|
+
}
|
|
46
105
|
const err = Mkdir(path, 0o700)
|
|
47
106
|
if (err === null) {
|
|
48
107
|
return [path, null]
|
|
49
108
|
}
|
|
109
|
+
lastErr = err
|
|
50
110
|
}
|
|
51
|
-
return ["",
|
|
111
|
+
return ["", lastErr]
|
|
52
112
|
}
|