Compare commits
No commits in common. "main" and "v1.5.8" have entirely different histories.
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,16 +1,5 @@
|
|||||||
# CHANGELOG - go/js
|
# CHANGELOG - go/js
|
||||||
|
|
||||||
## v1.5.11 (2026-08-24)
|
|
||||||
- **定义隔离**: `Define` 在发布到共享 VM Pool 前编译并在一次性隔离 VM 中验证脚本,拒绝语法错误、定义阶段异常、非函数结果和超过 1 秒的定义执行。
|
|
||||||
- **故障边界**: 无效定义不再增加 Pool 版本或进入共享脚本列表,避免单个低代码脚本阻塞其他 VM 和服务。
|
|
||||||
- **依赖对齐**: 更新 `log`、`config`、`encoding`、`file`、`id`、`rand`、`safe`、`shell` 至 monorepo 当前版本。
|
|
||||||
|
|
||||||
## v1.5.10 (2026-08-17)
|
|
||||||
- **嵌套回调桥接**: 可变参数中的 lower-camel 配置对象现可包含 JavaScript 回调;普通字段继续使用 `cast` 转换,函数字段由 goja 原生导出,支持 API 流式回调。
|
|
||||||
|
|
||||||
## v1.5.9 (2026-07-18)
|
|
||||||
- **依赖校验修复**: 重新整理模块 checksum,并对齐 `x/crypto v0.52.0`、`x/sys v0.45.0`。
|
|
||||||
|
|
||||||
## v1.5.8 (2026-07-13)
|
## v1.5.8 (2026-07-13)
|
||||||
- **桥接一致性修复**: Go 函数返回的 API 门面对象,其导出方法也统一经由桥接层调用。
|
- **桥接一致性修复**: Go 函数返回的 API 门面对象,其导出方法也统一经由桥接层调用。
|
||||||
JavaScript 的 lower-camel 对象字段会与顶层 API 一样自动转换为 Go struct 字段。
|
JavaScript 的 lower-camel 对象字段会与顶层 API 一样自动转换为 Go struct 字段。
|
||||||
|
|||||||
@ -9,10 +9,8 @@ A lightweight, frictionless, and AI-friendly JavaScript engine for Go applicatio
|
|||||||
- **Host Object Fidelity**: Go pointers and structs are preserved when passed back and forth between Go and JS.
|
- **Host Object Fidelity**: Go pointers and structs are preserved when passed back and forth between Go and JS.
|
||||||
- **Context Injection**: Automatic `context.Context` propagation from `js.Call`.
|
- **Context Injection**: Automatic `context.Context` propagation from `js.Call`.
|
||||||
- **Versioned Pool**: Thread-safe VM pool with incremental code synchronization and version checking (`CheckVersion`).
|
- **Versioned Pool**: Thread-safe VM pool with incremental code synchronization and version checking (`CheckVersion`).
|
||||||
- **Transactional Definitions**: `Define` validates each function in an isolated VM before publishing it, so an invalid low-code script cannot poison the shared VM pool.
|
|
||||||
- **Function Discovery**: List all defined functions via `FuncList()`.
|
- **Function Discovery**: List all defined functions via `FuncList()`.
|
||||||
- **Context Interruption**: Safe execution with `context.Context` cancellation support.
|
- **Context Interruption**: Safe execution with `context.Context` cancellation support.
|
||||||
- **Nested Callback Bridging**: JavaScript callbacks inside lower-camel option objects are preserved when converted to Go structs, including variadic streaming options.
|
|
||||||
- **AI-Ready**: Generates TypeScript definitions (`.d.ts`) for AI to understand available capabilities.
|
- **AI-Ready**: Generates TypeScript definitions (`.d.ts`) for AI to understand available capabilities.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -46,8 +44,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`Define` accepts an anonymous function expression. Before registration it rejects syntax errors, definition-time exceptions, non-function results, and definitions that do not finish within one second. Rejected definitions do not change the Pool version or become visible to any runtime VM.
|
|
||||||
|
|
||||||
### 3. Discover Functions
|
### 3. Discover Functions
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|||||||
19
TEST.md
19
TEST.md
@ -1,23 +1,15 @@
|
|||||||
# Test Report - go/js
|
# Test Report - go/js
|
||||||
|
|
||||||
## v1.5.11 验证
|
|
||||||
- 独立模块解析、全量测试与基准测试均通过。
|
|
||||||
- `TestDefineValidation` 覆盖语法错误、定义阶段异常、非函数结果、定义超时,以及失败定义不改变共享注册表。
|
|
||||||
|
|
||||||
## v1.5.10 验证
|
|
||||||
- 独立模块解析、全量测试与基准测试均通过。
|
|
||||||
- `TestBridgeVariadicNestedCallback` 验证可变 options 中 lower-camel 数据与嵌套 JavaScript 回调同时正确转换。
|
|
||||||
|
|
||||||
## Performance (Benchmark)
|
## Performance (Benchmark)
|
||||||
Date: 2026-08-24
|
Date: 2026-06-28
|
||||||
OS: darwin
|
OS: darwin
|
||||||
Arch: arm64
|
Arch: amd64
|
||||||
CPU: Apple M3 Max
|
CPU: Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
|
||||||
|
|
||||||
| Benchmark | Iterations | Time/op |
|
| Benchmark | Iterations | Time/op |
|
||||||
|-----------|------------|---------|
|
|-----------|------------|---------|
|
||||||
| BenchmarkCall | 1578303 | 742.8 ns/op |
|
| BenchmarkCall | 766462 | 1331 ns/op |
|
||||||
| BenchmarkSync | 13456 | 136636 ns/op |
|
| BenchmarkSync | 31066 | 52789 ns/op |
|
||||||
|
|
||||||
*Note: BenchmarkCall covers the hot path of executing a JS function from the pool. BenchmarkSync covers the cost of defining new code (including VM sync).*
|
*Note: BenchmarkCall covers the hot path of executing a JS function from the pool. BenchmarkSync covers the cost of defining new code (including VM sync).*
|
||||||
|
|
||||||
@ -70,7 +62,6 @@ ok apigo.cc/go/js 0.800s
|
|||||||
- [x] Context and Logger injection.
|
- [x] Context and Logger injection.
|
||||||
- [x] Concurrent execution and script versioning.
|
- [x] Concurrent execution and script versioning.
|
||||||
- [x] Script version checking (`CheckVersion`).
|
- [x] Script version checking (`CheckVersion`).
|
||||||
- [x] Transactional definition validation and shared Pool fault isolation.
|
|
||||||
- [x] Function discovery (`FuncList`).
|
- [x] Function discovery (`FuncList`).
|
||||||
- [x] Context cancellation interruption.
|
- [x] Context cancellation interruption.
|
||||||
- [x] Graceful shutdown.
|
- [x] Graceful shutdown.
|
||||||
|
|||||||
48
bridge.go
48
bridge.go
@ -89,13 +89,11 @@ func wrapGoFunc(vm *goja.Runtime, fn any, isUnsafe bool) goja.Value {
|
|||||||
if isVariadic && i == numIn-1 {
|
if isVariadic && i == numIn-1 {
|
||||||
elemType := argType.Elem()
|
elemType := argType.Elem()
|
||||||
for jsArgIdx < len(jsArgs) {
|
for jsArgIdx < len(jsArgs) {
|
||||||
jsValue := jsArgs[jsArgIdx]
|
exported := jsArgs[jsArgIdx].Export()
|
||||||
exported := jsValue.Export()
|
|
||||||
expV := reflect.ValueOf(exported)
|
expV := reflect.ValueOf(exported)
|
||||||
if !expV.IsValid() || !expV.Type().AssignableTo(elemType) {
|
if !expV.IsValid() || !expV.Type().AssignableTo(elemType) {
|
||||||
elem := reflect.New(elemType).Elem()
|
elem := reflect.New(elemType).Elem()
|
||||||
cast.Convert(elem.Addr().Interface(), exported)
|
cast.Convert(elem.Addr().Interface(), exported)
|
||||||
exportJSCallbacks(vm, jsValue, elem)
|
|
||||||
expV = elem
|
expV = elem
|
||||||
}
|
}
|
||||||
goArgs = append(goArgs, expV)
|
goArgs = append(goArgs, expV)
|
||||||
@ -177,50 +175,6 @@ func wrapGoFunc(vm *goja.Runtime, fn any, isUnsafe bool) goja.Value {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// exportJSCallbacks overlays callable fields after cast.Convert has applied the
|
|
||||||
// project's lower-camel struct mapping. goja's native ExportTo preserves JS
|
|
||||||
// functions, while cast remains authoritative for ordinary data conversion.
|
|
||||||
func exportJSCallbacks(vm *goja.Runtime, source goja.Value, target reflect.Value) {
|
|
||||||
if source == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for target.Kind() == reflect.Ptr {
|
|
||||||
if target.IsNil() {
|
|
||||||
if goja.IsUndefined(source) || goja.IsNull(source) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
target.Set(reflect.New(target.Type().Elem()))
|
|
||||||
}
|
|
||||||
target = target.Elem()
|
|
||||||
}
|
|
||||||
if target.Kind() != reflect.Struct || goja.IsUndefined(source) || goja.IsNull(source) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
object := source.ToObject(vm)
|
|
||||||
targetType := target.Type()
|
|
||||||
for i := 0; i < target.NumField(); i++ {
|
|
||||||
field := target.Field(i)
|
|
||||||
if !field.CanSet() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := cast.GetLowerName(targetType.Field(i).Name)
|
|
||||||
value := object.Get(name)
|
|
||||||
if goja.IsUndefined(value) {
|
|
||||||
value = object.Get(targetType.Field(i).Name)
|
|
||||||
}
|
|
||||||
if value == nil || goja.IsUndefined(value) || goja.IsNull(value) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if field.Kind() == reflect.Func {
|
|
||||||
_ = vm.ExportTo(value, field.Addr().Interface())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if field.Kind() == reflect.Struct || field.Kind() == reflect.Ptr {
|
|
||||||
exportJSCallbacks(vm, value, field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// toJSValue exposes Go API objects through bridge-wrapped methods rather than
|
// toJSValue exposes Go API objects through bridge-wrapped methods rather than
|
||||||
// goja's direct reflection. Directly reflected methods bypass cast.Convert, so
|
// goja's direct reflection. Directly reflected methods bypass cast.Convert, so
|
||||||
// nested lower-camel JavaScript objects would not be converted to Go structs.
|
// nested lower-camel JavaScript objects would not be converted to Go structs.
|
||||||
|
|||||||
@ -24,15 +24,6 @@ type bridgeFacadeRequest struct {
|
|||||||
Limit int
|
Limit int
|
||||||
}
|
}
|
||||||
|
|
||||||
type bridgeCallbackGroup struct {
|
|
||||||
OnData func([]byte) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type bridgeCallbackOptions struct {
|
|
||||||
Token string
|
|
||||||
Stream *bridgeCallbackGroup
|
|
||||||
}
|
|
||||||
|
|
||||||
// bridgeFacade intentionally follows the shape used by Go APIs exposed to JS:
|
// bridgeFacade intentionally follows the shape used by Go APIs exposed to JS:
|
||||||
// private state and exported methods.
|
// private state and exported methods.
|
||||||
type bridgeFacade struct {
|
type bridgeFacade struct {
|
||||||
@ -176,26 +167,6 @@ func TestBridgeVariadic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBridgeVariadicNestedCallback(t *testing.T) {
|
|
||||||
vm := goja.New()
|
|
||||||
call := func(options ...*bridgeCallbackOptions) error {
|
|
||||||
if len(options) != 1 || options[0] == nil || options[0].Token != "internal" || options[0].Stream == nil || options[0].Stream.OnData == nil {
|
|
||||||
return fmt.Errorf("options were not converted: %#v", options)
|
|
||||||
}
|
|
||||||
return options[0].Stream.OnData([]byte("chunk"))
|
|
||||||
}
|
|
||||||
if err := vm.Set("call", wrapGoFunc(vm, call, false)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
value, err := vm.RunString(`let received = ''; call({token: 'internal', stream: {onData(chunk) { received = String.fromCharCode(...chunk); return null }}}); received`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if value.String() != "chunk" {
|
|
||||||
t.Fatalf("callback received %q", value.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBridgeMapsLowerCamelCaseForReturnedObjectMethods(t *testing.T) {
|
func TestBridgeMapsLowerCamelCaseForReturnedObjectMethods(t *testing.T) {
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
facade := &bridgeFacade{}
|
facade := &bridgeFacade{}
|
||||||
|
|||||||
22
go.mod
22
go.mod
@ -3,25 +3,25 @@ module apigo.cc/go/js
|
|||||||
go 1.25.0
|
go 1.25.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/cast v1.5.5
|
apigo.cc/go/cast v1.5.3
|
||||||
apigo.cc/go/jsmod v1.5.3
|
apigo.cc/go/jsmod v1.5.3
|
||||||
apigo.cc/go/log v1.5.11
|
apigo.cc/go/log v1.5.8
|
||||||
github.com/dop251/goja v0.0.0-20260311135729-065cd970411c
|
github.com/dop251/goja v0.0.0-20260311135729-065cd970411c
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
apigo.cc/go/config v1.5.4 // indirect
|
apigo.cc/go/config v1.5.3 // indirect
|
||||||
apigo.cc/go/encoding v1.5.6 // indirect
|
apigo.cc/go/encoding v1.5.5 // indirect
|
||||||
apigo.cc/go/file v1.5.6 // indirect
|
apigo.cc/go/file v1.5.5 // indirect
|
||||||
apigo.cc/go/id v1.5.7 // indirect
|
apigo.cc/go/id v1.5.6 // indirect
|
||||||
apigo.cc/go/rand v1.5.4 // indirect
|
apigo.cc/go/rand v1.5.3 // indirect
|
||||||
apigo.cc/go/safe v1.5.3 // indirect
|
apigo.cc/go/safe v1.5.2 // indirect
|
||||||
apigo.cc/go/shell v1.5.6 // indirect
|
apigo.cc/go/shell v1.5.4 // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||||
golang.org/x/crypto v0.52.0 // indirect
|
golang.org/x/crypto v0.51.0 // indirect
|
||||||
golang.org/x/sys v0.45.0 // indirect
|
golang.org/x/sys v0.44.0 // indirect
|
||||||
golang.org/x/text v0.37.0 // indirect
|
golang.org/x/text v0.37.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
48
go.sum
48
go.sum
@ -1,23 +1,23 @@
|
|||||||
apigo.cc/go/cast v1.5.5 h1:DMbfK3uPhPjRaXutj3StIZIkfjFIATSXfuAOeNOd4Fw=
|
apigo.cc/go/cast v1.5.0 h1:UBGJtFQ8eJPMQXs37cUgqd7YQo1zI9opuSDBDmn2/pE=
|
||||||
apigo.cc/go/cast v1.5.5/go.mod h1:GMjjrYn93tWat1U409G7h1jR3ejfLLI7r0efBo9Sbd4=
|
apigo.cc/go/cast v1.5.0/go.mod h1:z2GW5p5WCZGEqVVIJUdhl232vRbLf2Qu4EDlEakX/D8=
|
||||||
apigo.cc/go/config v1.5.4 h1:1c/OarGwbz3+6ikurE+a6LJLjtzXvGvbxw3HV/Nk54M=
|
apigo.cc/go/config v1.5.0 h1:Yuz9QEb11XXG4XkhDi/ueT2M1T3Q9PElE5tiakvjehs=
|
||||||
apigo.cc/go/config v1.5.4/go.mod h1:oN+D2F8ETIyqKp+Yu8R4PRQlUoqR44o35jAHwLrrAq0=
|
apigo.cc/go/config v1.5.0/go.mod h1:jdMiDLPa9gzB8/FFZvm9jOopUqdxb7XSX+0OeWcZZUM=
|
||||||
apigo.cc/go/encoding v1.5.6 h1:v02swVfbFGidD4QcX2ktuHHbCjdSbOB85fhzAXay+7M=
|
apigo.cc/go/encoding v1.5.0 h1:EJNdRVDOMoI2DAvZwQNQTbYuqB/6zsEzvg7lS5pQI+I=
|
||||||
apigo.cc/go/encoding v1.5.6/go.mod h1:Big9q1Zwy4071dXtnrQ3SJDzfa/G7/A60KE/5+M//P8=
|
apigo.cc/go/encoding v1.5.0/go.mod h1:8++NfZj3hWig0qh2g7GQRw/4LpSvCYMWUZ+8J+x58cA=
|
||||||
apigo.cc/go/file v1.5.6 h1:Y7w3Tyu4e16VuED7rF2pzba+dzGE+hjDnBlIPVHIfzA=
|
apigo.cc/go/file v1.5.0 h1:Fh1NSDBqaxjuXYJ71yPHPXVJ8BFEv/AGS3l+jkLi5uw=
|
||||||
apigo.cc/go/file v1.5.6/go.mod h1:9sdW4ylSOA0HWc8Yt8qdnmMf6nn5SUEmjoPKyXpYXIQ=
|
apigo.cc/go/file v1.5.0/go.mod h1:4YhOGgBINTpmmmgws3H8LAyXQQBGzBp44hYUoCS+kr0=
|
||||||
apigo.cc/go/id v1.5.7 h1:Y5Sx6sQBCAdYMCQPTjODZyGMMd1+WRWCy2dWHVq11XQ=
|
apigo.cc/go/id v1.5.0 h1:MjNWPhBhDsoXaLeJDv/0wfJmVMU9EvOs8pWYfsTQ6e8=
|
||||||
apigo.cc/go/id v1.5.7/go.mod h1:fugudFBqfVNakfm91zZzuzU0P4PULzo6sylB8hRMqxA=
|
apigo.cc/go/id v1.5.0/go.mod h1:qhu4a1/KLc/XcBpcsRu+mXZt7U7Wvd9zMcPs4VspuPA=
|
||||||
apigo.cc/go/jsmod v1.5.3 h1:S3W317bH0QV2NMeRO1E0v6ySIBOfMWYv/NuQJbvqKWU=
|
apigo.cc/go/jsmod v1.5.0 h1:JgQtJNiJWy1NOP9AzE8NX5VXJkpO/x3GqLsCCSny5Ec=
|
||||||
apigo.cc/go/jsmod v1.5.3/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
|
apigo.cc/go/jsmod v1.5.0/go.mod h1:bmyeZtOAP/j5am+YRnaiM89smysK24K7ebk0koFtsSw=
|
||||||
apigo.cc/go/log v1.5.11 h1:r7vHkzpdelggNguZZK4e3O9bcaxfQWjXkxsKYw4A8Bo=
|
apigo.cc/go/log v1.5.5 h1:AFU7d7AQxkpgDHl7SnlEwd6yzGSFAlnrrjbrNDQnQHI=
|
||||||
apigo.cc/go/log v1.5.11/go.mod h1:C6qtOn09miyCK7FXEAAwEZASnBow76K7GZFoBaq9eiU=
|
apigo.cc/go/log v1.5.5/go.mod h1:Djy+I5aLhGB/EjwRz4KHqkVEz584IAD55FAFiIfInuo=
|
||||||
apigo.cc/go/rand v1.5.4 h1:eessFBsKQuoOYdzrStldOGw9f4HqbeO87X95bI4jIBQ=
|
apigo.cc/go/rand v1.5.0 h1:1o8hh8fhdBuk1/h02IvugvamuT3dkWbVJrqEJVQKB2E=
|
||||||
apigo.cc/go/rand v1.5.4/go.mod h1:q1BTFkY/cXE229dDD5Q22lF7T0DoKPV6xAu+6bCrDH4=
|
apigo.cc/go/rand v1.5.0/go.mod h1:Lh98S2dm9UY0X+M+kNQQEKyXHG5pcCKSFPyXN0QCGdk=
|
||||||
apigo.cc/go/safe v1.5.3 h1:9p/BmdlVWLbekpKByZIFC09Qn8Wdhik2eINiwunBxPs=
|
apigo.cc/go/safe v1.5.0 h1:W1NblmcU8cex1f9Y5z8mNLUJOzZTE1s6fszb3FbhGnk=
|
||||||
apigo.cc/go/safe v1.5.3/go.mod h1:Ay8kEPL76DeXH4ifsVTc/3/sfGHlWLQjAp4vi7GA9AI=
|
apigo.cc/go/safe v1.5.0/go.mod h1:OfQ5d6COePSGEuPvMeOk6KagX2sezw7nvKh7exj9SeM=
|
||||||
apigo.cc/go/shell v1.5.6 h1:2i93lNJ0oy/D5kOmlmOjwVjLPsW7vQBo0trLOF9AAAI=
|
apigo.cc/go/shell v1.5.0 h1:WLDMMqUU0INeaBDmQsTPr0h/NfB2RknAtiJ5NL467+Q=
|
||||||
apigo.cc/go/shell v1.5.6/go.mod h1:Bp73DGKESOISWSIGUtL1dsFo5g4SI10GhgsnLJCTpsw=
|
apigo.cc/go/shell v1.5.0/go.mod h1:rYHA77d5hEsQHcJrbAWf1pHy0sxayeJ0gU55LA/JWQk=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||||
@ -34,10 +34,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
|
||||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
|
||||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
|
||||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
17
pool.go
17
pool.go
@ -127,21 +127,7 @@ func (p *Pool) Define(name string, code string, version int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapped := fmt.Sprintf("globalThis[%q] = (%s);", name, code)
|
wrapped := fmt.Sprintf("globalThis['%s'] = (%s);", name, code)
|
||||||
program, err := goja.Compile(name, wrapped, false)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("js.Define [%s]: invalid JavaScript: %w", name, err)
|
|
||||||
}
|
|
||||||
validationVM := goja.New()
|
|
||||||
validationTimer := time.AfterFunc(time.Second, func() { validationVM.Interrupt("definition validation timed out") })
|
|
||||||
if _, err = validationVM.RunProgram(program); err != nil {
|
|
||||||
validationTimer.Stop()
|
|
||||||
return fmt.Errorf("js.Define [%s]: definition failed: %w", name, err)
|
|
||||||
}
|
|
||||||
validationTimer.Stop()
|
|
||||||
if _, ok := goja.AssertFunction(validationVM.Get(name)); !ok {
|
|
||||||
return fmt.Errorf("js.Define [%s]: code must evaluate to a function", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
@ -178,7 +164,6 @@ func CheckVersion(name string, version int64) bool {
|
|||||||
|
|
||||||
// parseJSFrame parses a single stack trace line from Goja.
|
// parseJSFrame parses a single stack trace line from Goja.
|
||||||
// Format is:
|
// Format is:
|
||||||
//
|
|
||||||
// named: \tat funcName (src:line:col) (optionalPC)
|
// named: \tat funcName (src:line:col) (optionalPC)
|
||||||
// anon: \tat src:line:col (optionalPC)
|
// anon: \tat src:line:col (optionalPC)
|
||||||
func parseJSFrame(line string) (src, lineNum, col string, ok bool) {
|
func parseJSFrame(line string) (src, lineNum, col string, ok bool) {
|
||||||
|
|||||||
29
pool_test.go
29
pool_test.go
@ -161,35 +161,6 @@ func TestDefineValidation(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error for arrow function: %v", err)
|
t.Errorf("unexpected error for arrow function: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid JavaScript must not enter the shared registry or poison VMs.
|
|
||||||
version := p.version
|
|
||||||
err = p.Define("badSyntax", `({ args }) => { return args })`, 0)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "invalid JavaScript") {
|
|
||||||
t.Fatalf("expected syntax validation error, got %v", err)
|
|
||||||
}
|
|
||||||
if p.version != version || p.CheckVersion("badSyntax", 0) {
|
|
||||||
t.Fatal("invalid JavaScript changed the shared script registry")
|
|
||||||
}
|
|
||||||
err = p.Define("throwsWhileDefining", `(() => { throw new Error("definition failed") })()`, 0)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "definition failed") {
|
|
||||||
t.Fatalf("expected definition error, got %v", err)
|
|
||||||
}
|
|
||||||
err = p.Define("notAFunction", `({ value: 1 })`, 0)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "must evaluate to a function") {
|
|
||||||
t.Fatalf("expected function validation error, got %v", err)
|
|
||||||
}
|
|
||||||
err = p.Define("neverFinishes", `(() => { for (;;) {} })()`, 0)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), "definition validation timed out") {
|
|
||||||
t.Fatalf("expected definition timeout, got %v", err)
|
|
||||||
}
|
|
||||||
if p.version != version || p.CheckVersion("throwsWhileDefining", 0) || p.CheckVersion("notAFunction", 0) || p.CheckVersion("neverFinishes", 0) {
|
|
||||||
t.Fatal("failed definitions changed the shared script registry")
|
|
||||||
}
|
|
||||||
result, callErr := p.Call("good2", 0, nil, 2, 3)
|
|
||||||
if callErr != nil || cast.To[int64](result) != 5 {
|
|
||||||
t.Fatalf("valid function failed after rejected script: result=%v error=%v", result, callErr)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestJSErrorStackTrace(t *testing.T) {
|
func TestJSErrorStackTrace(t *testing.T) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user