diff --git a/go.mod b/go.mod index 4c4ebc9c36a..a5dc5d52095 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/PuerkitoBio/goquery v1.6.1 github.com/Soontao/goHttpDigestClient v0.0.0-20170320082612-6d28bb1415c5 github.com/andybalholm/brotli v1.0.3 - github.com/dop251/goja v0.0.0-20220110113543-261677941f3c + github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4 github.com/fatih/color v1.12.0 github.com/golang/protobuf v1.4.3 github.com/gorilla/websocket v1.4.2 diff --git a/go.sum b/go.sum index d9c3544e113..a853557d922 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E= github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dop251/goja v0.0.0-20220110113543-261677941f3c h1:1XnAlcjYBdO7xsa2rhNB/BTztiu4cFKOxE+3brXVtG4= -github.com/dop251/goja v0.0.0-20220110113543-261677941f3c/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= +github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4 h1:gUXabLfCUjaNl7kLxGdaZaw1c5x33SGL9PEo6p/hfuo= +github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= diff --git a/vendor/github.com/dop251/goja/.tc39_test262_checkout.sh b/vendor/github.com/dop251/goja/.tc39_test262_checkout.sh index 8fd8c4037ee..07fe7811054 100644 --- a/vendor/github.com/dop251/goja/.tc39_test262_checkout.sh +++ b/vendor/github.com/dop251/goja/.tc39_test262_checkout.sh @@ -1,5 +1,7 @@ #!/bin/sh -sha=26f1f4567ee7e33163d961c867d689173cbb9065 # this is just the commit it was last tested with +# this is just the commit it was last tested with +sha=e87b0048c402479df1d9cb391fb86620cf3200fd + mkdir -p testdata/test262 cd testdata/test262 git init diff --git a/vendor/github.com/dop251/goja/README.md b/vendor/github.com/dop251/goja/README.md index 6e27ed50c77..35a42442299 100644 --- a/vendor/github.com/dop251/goja/README.md +++ b/vendor/github.com/dop251/goja/README.md @@ -3,7 +3,7 @@ goja ECMAScript 5.1(+) implementation in Go. -[![GoDoc](https://godoc.org/github.com/dop251/goja?status.svg)](https://godoc.org/github.com/dop251/goja) +[![Go Reference](https://pkg.go.dev/badge/github.com/dop251/goja.svg)](https://pkg.go.dev/github.com/dop251/goja) Goja is an implementation of ECMAScript 5.1 in pure Go with emphasis on standard compliance and performance. @@ -17,7 +17,7 @@ Features * Full ECMAScript 5.1 support (including regex and strict mode). * Passes nearly all [tc39 tests](https://github.com/tc39/test262) for the features implemented so far. The goal is to - pass all of them. Note, the current working commit is https://github.com/tc39/test262/commit/26f1f4567ee7e33163d961c867d689173cbb9065. + pass all of them. See .tc39_test262_checkout.sh for the latest working commit id. * Capable of running Babel, Typescript compiler and pretty much anything written in ES5. * Sourcemaps. * Most of ES6 functionality, still work in progress, see https://github.com/dop251/goja/milestone/1?closed=1 @@ -153,13 +153,13 @@ if num := v.Export().(int64); num != 4 { Passing Values to JS -------------------- -Any Go value can be passed to JS using Runtime.ToValue() method. See the method's [documentation](https://godoc.org/github.com/dop251/goja#Runtime.ToValue) for more details. +Any Go value can be passed to JS using Runtime.ToValue() method. See the method's [documentation](https://pkg.go.dev/github.com/dop251/goja#Runtime.ToValue) for more details. Exporting Values from JS ------------------------ A JS value can be exported into its default Go representation using Value.Export() method. -Alternatively it can be exported into a specific Go variable using [Runtime.ExportTo()](https://godoc.org/github.com/dop251/goja#Runtime.ExportTo) method. +Alternatively it can be exported into a specific Go variable using [Runtime.ExportTo()](https://pkg.go.dev/github.com/dop251/goja#Runtime.ExportTo) method. Within a single export operation the same Object will be represented by the same Go value (either the same map, slice or a pointer to the same struct). This includes circular objects and makes it possible to export them. @@ -168,7 +168,7 @@ Calling JS functions from Go ---------------------------- There are 2 approaches: -- Using [AssertFunction()](https://godoc.org/github.com/dop251/goja#AssertFunction): +- Using [AssertFunction()](https://pkg.go.dev/github.com/dop251/goja#AssertFunction): ```go vm := New() _, err := vm.RunString(` @@ -191,7 +191,7 @@ if err != nil { fmt.Println(res) // Output: 42 ``` -- Using [Runtime.ExportTo()](https://godoc.org/github.com/dop251/goja#Runtime.ExportTo): +- Using [Runtime.ExportTo()](https://pkg.go.dev/github.com/dop251/goja#Runtime.ExportTo): ```go const SCRIPT = ` function f(param) { @@ -222,7 +222,7 @@ Mapping struct field and method names ------------------------------------- By default, the names are passed through as is which means they are capitalised. This does not match the standard JavaScript naming convention, so if you need to make your JS code look more natural or if you are -dealing with a 3rd party library, you can use a [FieldNameMapper](https://godoc.org/github.com/dop251/goja#FieldNameMapper): +dealing with a 3rd party library, you can use a [FieldNameMapper](https://pkg.go.dev/github.com/dop251/goja#FieldNameMapper): ```go vm := New() @@ -236,14 +236,14 @@ fmt.Println(res.Export()) // Output: 42 ``` -There are two standard mappers: [TagFieldNameMapper](https://godoc.org/github.com/dop251/goja#TagFieldNameMapper) and -[UncapFieldNameMapper](https://godoc.org/github.com/dop251/goja#UncapFieldNameMapper), or you can use your own implementation. +There are two standard mappers: [TagFieldNameMapper](https://pkg.go.dev/github.com/dop251/goja#TagFieldNameMapper) and +[UncapFieldNameMapper](https://pkg.go.dev/github.com/dop251/goja#UncapFieldNameMapper), or you can use your own implementation. Native Constructors ------------------- In order to implement a constructor function in Go use `func (goja.ConstructorCall) *goja.Object`. -See [Runtime.ToValue()](https://godoc.org/github.com/dop251/goja#Runtime.ToValue) documentation for more details. +See [Runtime.ToValue()](https://pkg.go.dev/github.com/dop251/goja#Runtime.ToValue) documentation for more details. Regular Expressions ------------------- diff --git a/vendor/github.com/dop251/goja/array.go b/vendor/github.com/dop251/goja/array.go index 57642007027..6cff33fe389 100644 --- a/vendor/github.com/dop251/goja/array.go +++ b/vendor/github.com/dop251/goja/array.go @@ -1,6 +1,7 @@ package goja import ( + "fmt" "math" "math/bits" "reflect" @@ -480,11 +481,11 @@ func (a *arrayObject) deleteIdx(idx valueInt, throw bool) bool { } func (a *arrayObject) export(ctx *objectExportCtx) interface{} { - if v, exists := ctx.get(a); exists { + if v, exists := ctx.get(a.val); exists { return v } arr := make([]interface{}, a.length) - ctx.put(a, arr) + ctx.put(a.val, arr) if a.propValueCount == 0 && a.length == uint32(len(a.values)) && uint32(a.objCount) == a.length { for i, v := range a.values { if v != nil { @@ -506,6 +507,36 @@ func (a *arrayObject) exportType() reflect.Type { return reflectTypeArray } +func (a *arrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + r := a.val.runtime + if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil { + l := toIntStrict(int64(a.length)) + if dst.Len() != l { + if typ.Kind() == reflect.Array { + return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len()) + } else { + dst.Set(reflect.MakeSlice(typ, l, l)) + } + } + ctx.putTyped(a.val, typ, dst.Interface()) + for i := 0; i < l; i++ { + if i >= len(a.values) { + break + } + val := a.values[i] + if p, ok := val.(*valueProperty); ok { + val = p.get(a.val) + } + err := r.toReflectValue(val, dst.Index(i), ctx) + if err != nil { + return fmt.Errorf("could not convert array element %v to %v at %d: %w", val, typ, i, err) + } + } + return nil + } + return a.baseObject.exportToArrayOrSlice(dst, typ, ctx) +} + func (a *arrayObject) setValuesFromSparse(items []sparseArrayItem, newMaxIdx int) { a.values = make([]Value, newMaxIdx+1) for _, item := range items { diff --git a/vendor/github.com/dop251/goja/array_sparse.go b/vendor/github.com/dop251/goja/array_sparse.go index 3c111b266a1..0c0917a8ba7 100644 --- a/vendor/github.com/dop251/goja/array_sparse.go +++ b/vendor/github.com/dop251/goja/array_sparse.go @@ -1,6 +1,7 @@ package goja import ( + "fmt" "math" "math/bits" "reflect" @@ -420,11 +421,11 @@ func (a *sparseArrayObject) sortLen() int64 { } func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} { - if v, exists := ctx.get(a); exists { + if v, exists := ctx.get(a.val); exists { return v } arr := make([]interface{}, a.length) - ctx.put(a, arr) + ctx.put(a.val, arr) var prevIdx uint32 for _, item := range a.items { idx := item.idx @@ -457,3 +458,34 @@ func (a *sparseArrayObject) export(ctx *objectExportCtx) interface{} { func (a *sparseArrayObject) exportType() reflect.Type { return reflectTypeArray } + +func (a *sparseArrayObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + r := a.val.runtime + if iter := a.getSym(SymIterator, nil); iter == r.global.arrayValues || iter == nil { + l := toIntStrict(int64(a.length)) + if dst.Len() != l { + if typ.Kind() == reflect.Array { + return fmt.Errorf("cannot convert an Array into an array, lengths mismatch (have %d, need %d)", l, dst.Len()) + } else { + dst.Set(reflect.MakeSlice(typ, l, l)) + } + } + ctx.putTyped(a.val, typ, dst.Interface()) + for _, item := range a.items { + val := item.value + if p, ok := val.(*valueProperty); ok { + val = p.get(a.val) + } + idx := toIntStrict(int64(item.idx)) + if idx >= l { + break + } + err := r.toReflectValue(val, dst.Index(idx), ctx) + if err != nil { + return fmt.Errorf("could not convert array element %v to %v at %d: %w", item.value, typ, idx, err) + } + } + return nil + } + return a.baseObject.exportToArrayOrSlice(dst, typ, ctx) +} diff --git a/vendor/github.com/dop251/goja/builtin_date.go b/vendor/github.com/dop251/goja/builtin_date.go index 97ffa679588..03c2228b0db 100644 --- a/vendor/github.com/dop251/goja/builtin_date.go +++ b/vendor/github.com/dop251/goja/builtin_date.go @@ -682,20 +682,20 @@ func _dateSetMilliseconds(year, mon, day, hours, min, sec int64, t time.Time, ca func (r *Runtime) dateproto_setMilliseconds(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + n := call.Argument(0).ToNumber() + if IsNaN(n) { + d.unset() + return _NaN + } + msec := n.ToInteger() + sec := d.msec / 1e3 + var ok bool + sec, msec, ok = _norm(sec, msec, 1e3) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - n := call.Argument(0).ToNumber() - if IsNaN(n) { - d.unset() - return _NaN - } - msec := n.ToInteger() - sec := d.msec / 1e3 - var ok bool - sec, msec, ok = _norm(sec, msec, 1e3) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(sec*1e3 + msec) } else { return _NaN @@ -707,20 +707,20 @@ func (r *Runtime) dateproto_setMilliseconds(call FunctionCall) Value { func (r *Runtime) dateproto_setUTCMilliseconds(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + n := call.Argument(0).ToNumber() + if IsNaN(n) { + d.unset() + return _NaN + } + msec := n.ToInteger() + sec := d.msec / 1e3 + var ok bool + sec, msec, ok = _norm(sec, msec, 1e3) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - n := call.Argument(0).ToNumber() - if IsNaN(n) { - d.unset() - return _NaN - } - msec := n.ToInteger() - sec := d.msec / 1e3 - var ok bool - sec, msec, ok = _norm(sec, msec, 1e3) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(sec*1e3 + msec) } else { return _NaN @@ -732,12 +732,12 @@ func (r *Runtime) dateproto_setUTCMilliseconds(call FunctionCall) Value { func (r *Runtime) dateproto_setSeconds(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.time(), call, -5, false) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.time(), call, -5, false) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -749,12 +749,12 @@ func (r *Runtime) dateproto_setSeconds(call FunctionCall) Value { func (r *Runtime) dateproto_setUTCSeconds(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.timeUTC(), call, -5, true) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.timeUTC(), call, -5, true) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -766,12 +766,12 @@ func (r *Runtime) dateproto_setUTCSeconds(call FunctionCall) Value { func (r *Runtime) dateproto_setMinutes(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.time(), call, -4, false) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.time(), call, -4, false) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -783,12 +783,12 @@ func (r *Runtime) dateproto_setMinutes(call FunctionCall) Value { func (r *Runtime) dateproto_setUTCMinutes(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.timeUTC(), call, -4, true) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.timeUTC(), call, -4, true) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -800,12 +800,12 @@ func (r *Runtime) dateproto_setUTCMinutes(call FunctionCall) Value { func (r *Runtime) dateproto_setHours(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.time(), call, -3, false) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.time(), call, -3, false) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -817,12 +817,12 @@ func (r *Runtime) dateproto_setHours(call FunctionCall) Value { func (r *Runtime) dateproto_setUTCHours(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.timeUTC(), call, -3, true) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.timeUTC(), call, -3, true) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -834,12 +834,12 @@ func (r *Runtime) dateproto_setUTCHours(call FunctionCall) Value { func (r *Runtime) dateproto_setDate(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.time(), limitCallArgs(call, 1), -2, false) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.time(), limitCallArgs(call, 1), -2, false) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -851,12 +851,12 @@ func (r *Runtime) dateproto_setDate(call FunctionCall) Value { func (r *Runtime) dateproto_setUTCDate(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.timeUTC(), limitCallArgs(call, 1), -2, true) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.timeUTC(), limitCallArgs(call, 1), -2, true) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -868,12 +868,12 @@ func (r *Runtime) dateproto_setUTCDate(call FunctionCall) Value { func (r *Runtime) dateproto_setMonth(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.time(), limitCallArgs(call, 2), -1, false) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.time(), limitCallArgs(call, 2), -1, false) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN @@ -885,12 +885,12 @@ func (r *Runtime) dateproto_setMonth(call FunctionCall) Value { func (r *Runtime) dateproto_setUTCMonth(call FunctionCall) Value { obj := r.toObject(call.This) if d, ok := obj.self.(*dateObject); ok { + t, ok := _dateSetFullYear(d.timeUTC(), limitCallArgs(call, 2), -1, true) + if !ok { + d.unset() + return _NaN + } if d.isSet() { - t, ok := _dateSetFullYear(d.timeUTC(), limitCallArgs(call, 2), -1, true) - if !ok { - d.unset() - return _NaN - } return d.setTimeMs(timeToMsec(t)) } else { return _NaN diff --git a/vendor/github.com/dop251/goja/builtin_map.go b/vendor/github.com/dop251/goja/builtin_map.go index a472746b0e8..2b84baf8223 100644 --- a/vendor/github.com/dop251/goja/builtin_map.go +++ b/vendor/github.com/dop251/goja/builtin_map.go @@ -1,5 +1,11 @@ package goja +import ( + "reflect" +) + +var mapExportType = reflect.TypeOf([][2]interface{}{}) + type mapObject struct { baseObject m *orderedMap @@ -40,6 +46,56 @@ func (mo *mapObject) init() { mo.m = newOrderedMap(mo.val.runtime.getHash()) } +func (mo *mapObject) exportType() reflect.Type { + return mapExportType +} + +func (mo *mapObject) export(ctx *objectExportCtx) interface{} { + m := make([][2]interface{}, mo.m.size) + ctx.put(mo.val, m) + + iter := mo.m.newIter() + for i := 0; i < len(m); i++ { + entry := iter.next() + if entry == nil { + break + } + m[i][0] = exportValue(entry.key, ctx) + m[i][1] = exportValue(entry.value, ctx) + } + + return m +} + +func (mo *mapObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + if dst.IsNil() { + dst.Set(reflect.MakeMap(typ)) + } + ctx.putTyped(mo.val, typ, dst.Interface()) + keyTyp := typ.Key() + elemTyp := typ.Elem() + iter := mo.m.newIter() + r := mo.val.runtime + for { + entry := iter.next() + if entry == nil { + break + } + keyVal := reflect.New(keyTyp).Elem() + err := r.toReflectValue(entry.key, keyVal, ctx) + if err != nil { + return err + } + elemVal := reflect.New(elemTyp).Elem() + err = r.toReflectValue(entry.value, elemVal, ctx) + if err != nil { + return err + } + dst.SetMapIndex(keyVal, elemVal) + } + return nil +} + func (r *Runtime) mapProto_clear(call FunctionCall) Value { thisObj := r.toObject(call.This) mo, ok := thisObj.self.(*mapObject) diff --git a/vendor/github.com/dop251/goja/builtin_set.go b/vendor/github.com/dop251/goja/builtin_set.go index ef602e8430b..72e9015fb60 100644 --- a/vendor/github.com/dop251/goja/builtin_set.go +++ b/vendor/github.com/dop251/goja/builtin_set.go @@ -1,5 +1,12 @@ package goja +import ( + "fmt" + "reflect" +) + +var setExportType = reflectTypeArray + type setObject struct { baseObject m *orderedMap @@ -38,6 +45,69 @@ func (so *setObject) init() { so.m = newOrderedMap(so.val.runtime.getHash()) } +func (so *setObject) exportType() reflect.Type { + return setExportType +} + +func (so *setObject) export(ctx *objectExportCtx) interface{} { + a := make([]interface{}, so.m.size) + ctx.put(so.val, a) + iter := so.m.newIter() + for i := 0; i < len(a); i++ { + entry := iter.next() + if entry == nil { + break + } + a[i] = exportValue(entry.key, ctx) + } + return a +} + +func (so *setObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + l := so.m.size + if dst.Len() != l { + if typ.Kind() == reflect.Array { + return fmt.Errorf("cannot convert a Set into an array, lengths mismatch: have %d, need %d)", l, dst.Len()) + } else { + dst.Set(reflect.MakeSlice(typ, l, l)) + } + } + ctx.putTyped(so.val, typ, dst.Interface()) + iter := so.m.newIter() + r := so.val.runtime + for i := 0; i < l; i++ { + entry := iter.next() + if entry == nil { + break + } + err := r.toReflectValue(entry.key, dst.Index(i), ctx) + if err != nil { + return err + } + } + return nil +} + +func (so *setObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + keyTyp := typ.Key() + elemTyp := typ.Elem() + iter := so.m.newIter() + r := so.val.runtime + for { + entry := iter.next() + if entry == nil { + break + } + keyVal := reflect.New(keyTyp).Elem() + err := r.toReflectValue(entry.key, keyVal, ctx) + if err != nil { + return err + } + dst.SetMapIndex(keyVal, reflect.Zero(elemTyp)) + } + return nil +} + func (r *Runtime) setProto_add(call FunctionCall) Value { thisObj := r.toObject(call.This) so, ok := thisObj.self.(*setObject) diff --git a/vendor/github.com/dop251/goja/destruct.go b/vendor/github.com/dop251/goja/destruct.go index a34e3a76079..a9ef1186015 100644 --- a/vendor/github.com/dop251/goja/destruct.go +++ b/vendor/github.com/dop251/goja/destruct.go @@ -241,6 +241,14 @@ func (d *destructKeyedSource) exportType() reflect.Type { return d.w().exportType() } +func (d *destructKeyedSource) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + return d.w().exportToMap(dst, typ, ctx) +} + +func (d *destructKeyedSource) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + return d.w().exportToArrayOrSlice(dst, typ, ctx) +} + func (d *destructKeyedSource) equal(impl objectImpl) bool { return d.w().equal(impl) } diff --git a/vendor/github.com/dop251/goja/object.go b/vendor/github.com/dop251/goja/object.go index 2042053877a..5426733c535 100644 --- a/vendor/github.com/dop251/goja/object.go +++ b/vendor/github.com/dop251/goja/object.go @@ -142,7 +142,7 @@ func (p *PropertyDescriptor) complete() { type objectExportCacheItem map[reflect.Type]interface{} type objectExportCtx struct { - cache map[objectImpl]interface{} + cache map[*Object]interface{} } type objectImpl interface { @@ -193,6 +193,8 @@ type objectImpl interface { export(ctx *objectExportCtx) interface{} exportType() reflect.Type + exportToMap(m reflect.Value, typ reflect.Type, ctx *objectExportCtx) error + exportToArrayOrSlice(s reflect.Value, typ reflect.Type, ctx *objectExportCtx) error equal(objectImpl) bool iterateStringKeys() iterNextFunc @@ -943,12 +945,12 @@ func (o *baseObject) swap(i, j int64) { } func (o *baseObject) export(ctx *objectExportCtx) interface{} { - if v, exists := ctx.get(o); exists { + if v, exists := ctx.get(o.val); exists { return v } keys := o.stringKeys(false, nil) m := make(map[string]interface{}, len(keys)) - ctx.put(o, m) + ctx.put(o.val, m) for _, itemName := range keys { itemNameStr := itemName.String() v := o.val.self.getStr(itemName.string(), nil) @@ -966,6 +968,114 @@ func (o *baseObject) exportType() reflect.Type { return reflectTypeMap } +func genericExportToMap(o *Object, dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + if dst.IsNil() { + dst.Set(reflect.MakeMap(typ)) + } + ctx.putTyped(o, typ, dst.Interface()) + keyTyp := typ.Key() + elemTyp := typ.Elem() + needConvertKeys := !reflectTypeString.AssignableTo(keyTyp) + iter := &enumerableIter{ + o: o, + wrapped: o.self.iterateStringKeys(), + } + r := o.runtime + for item, next := iter.next(); next != nil; item, next = next() { + var kv reflect.Value + var err error + if needConvertKeys { + kv = reflect.New(keyTyp).Elem() + err = r.toReflectValue(item.name, kv, ctx) + if err != nil { + return fmt.Errorf("could not convert map key %s to %v: %w", item.name.String(), typ, err) + } + } else { + kv = reflect.ValueOf(item.name.String()) + } + + ival := o.self.getStr(item.name.string(), nil) + if ival != nil { + vv := reflect.New(elemTyp).Elem() + err = r.toReflectValue(ival, vv, ctx) + if err != nil { + return fmt.Errorf("could not convert map value %v to %v at key %s: %w", ival, typ, item.name.String(), err) + } + dst.SetMapIndex(kv, vv) + } else { + dst.SetMapIndex(kv, reflect.Zero(elemTyp)) + } + } + + return nil +} + +func (o *baseObject) exportToMap(m reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + return genericExportToMap(o.val, m, typ, ctx) +} + +func genericExportToArrayOrSlice(o *Object, dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) (err error) { + r := o.runtime + + if method := toMethod(r.getV(o, SymIterator)); method != nil { + // iterable + + var values []Value + // cannot change (append to) the slice once it's been put into the cache, so we need to know its length beforehand + ex := r.try(func() { + values = r.iterableToList(o, method) + }) + if ex != nil { + return ex + } + if dst.Len() != len(values) { + if typ.Kind() == reflect.Array { + return fmt.Errorf("cannot convert an iterable into an array, lengths mismatch (have %d, need %d)", len(values), dst.Len()) + } else { + dst.Set(reflect.MakeSlice(typ, len(values), len(values))) + } + } + ctx.putTyped(o, typ, dst.Interface()) + for i, val := range values { + err = r.toReflectValue(val, dst.Index(i), ctx) + if err != nil { + return + } + } + } else { + // array-like + var lp Value + if _, ok := o.self.assertCallable(); !ok { + lp = o.self.getStr("length", nil) + } + if lp == nil { + return fmt.Errorf("cannot convert %v to %v: not an array or iterable", o, typ) + } + l := toIntStrict(toLength(lp)) + if dst.Len() != l { + if typ.Kind() == reflect.Array { + return fmt.Errorf("cannot convert an array-like object into an array, lengths mismatch (have %d, need %d)", l, dst.Len()) + } else { + dst.Set(reflect.MakeSlice(typ, l, l)) + } + } + ctx.putTyped(o, typ, dst.Interface()) + for i := 0; i < l; i++ { + val := nilSafe(o.self.getIdx(valueInt(i), nil)) + err = r.toReflectValue(val, dst.Index(i), ctx) + if err != nil { + return + } + } + } + + return +} + +func (o *baseObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + return genericExportToArrayOrSlice(o.val, dst, typ, ctx) +} + type enumerableFlag int const ( @@ -1561,10 +1671,10 @@ func (o *guardedObject) deleteStr(name unistring.String, throw bool) bool { return res } -func (ctx *objectExportCtx) get(key objectImpl) (interface{}, bool) { +func (ctx *objectExportCtx) get(key *Object) (interface{}, bool) { if v, exists := ctx.cache[key]; exists { if item, ok := v.(objectExportCacheItem); ok { - r, exists := item[key.exportType()] + r, exists := item[key.self.exportType()] return r, exists } else { return v, true @@ -1573,7 +1683,7 @@ func (ctx *objectExportCtx) get(key objectImpl) (interface{}, bool) { return nil, false } -func (ctx *objectExportCtx) getTyped(key objectImpl, typ reflect.Type) (interface{}, bool) { +func (ctx *objectExportCtx) getTyped(key *Object, typ reflect.Type) (interface{}, bool) { if v, exists := ctx.cache[key]; exists { if item, ok := v.(objectExportCacheItem); ok { r, exists := item[typ] @@ -1587,20 +1697,20 @@ func (ctx *objectExportCtx) getTyped(key objectImpl, typ reflect.Type) (interfac return nil, false } -func (ctx *objectExportCtx) put(key objectImpl, value interface{}) { +func (ctx *objectExportCtx) put(key *Object, value interface{}) { if ctx.cache == nil { - ctx.cache = make(map[objectImpl]interface{}) + ctx.cache = make(map[*Object]interface{}) } if item, ok := ctx.cache[key].(objectExportCacheItem); ok { - item[key.exportType()] = value + item[key.self.exportType()] = value } else { ctx.cache[key] = value } } -func (ctx *objectExportCtx) putTyped(key objectImpl, typ reflect.Type, value interface{}) { +func (ctx *objectExportCtx) putTyped(key *Object, typ reflect.Type, value interface{}) { if ctx.cache == nil { - ctx.cache = make(map[objectImpl]interface{}) + ctx.cache = make(map[*Object]interface{}) } v, exists := ctx.cache[key] if exists { @@ -1608,7 +1718,7 @@ func (ctx *objectExportCtx) putTyped(key objectImpl, typ reflect.Type, value int item[typ] = value } else { m := make(objectExportCacheItem, 2) - m[key.exportType()] = v + m[key.self.exportType()] = v m[typ] = value ctx.cache[key] = m } diff --git a/vendor/github.com/dop251/goja/object_args.go b/vendor/github.com/dop251/goja/object_args.go index fd65bc6e763..eb41d01ce04 100644 --- a/vendor/github.com/dop251/goja/object_args.go +++ b/vendor/github.com/dop251/goja/object_args.go @@ -124,11 +124,11 @@ func (a *argumentsObject) defineOwnPropertyStr(name unistring.String, descr Prop } func (a *argumentsObject) export(ctx *objectExportCtx) interface{} { - if v, exists := ctx.get(a); exists { + if v, exists := ctx.get(a.val); exists { return v } arr := make([]interface{}, a.length) - ctx.put(a, arr) + ctx.put(a.val, arr) for i := range arr { v := a.getIdx(valueInt(int64(i)), nil) if v != nil { diff --git a/vendor/github.com/dop251/goja/object_dynamic.go b/vendor/github.com/dop251/goja/object_dynamic.go index 2414579f241..e1ef97d5d62 100644 --- a/vendor/github.com/dop251/goja/object_dynamic.go +++ b/vendor/github.com/dop251/goja/object_dynamic.go @@ -483,6 +483,14 @@ func (o *dynamicObject) exportType() reflect.Type { return reflect.TypeOf(o.d) } +func (o *baseDynamicObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + return genericExportToMap(o.val, dst, typ, ctx) +} + +func (o *baseDynamicObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + return genericExportToArrayOrSlice(o.val, dst, typ, ctx) +} + func (o *dynamicObject) equal(impl objectImpl) bool { if other, ok := impl.(*dynamicObject); ok { return o.d == other.d diff --git a/vendor/github.com/dop251/goja/object_goarray_reflect.go b/vendor/github.com/dop251/goja/object_goarray_reflect.go new file mode 100644 index 00000000000..9b6998afc55 --- /dev/null +++ b/vendor/github.com/dop251/goja/object_goarray_reflect.go @@ -0,0 +1,288 @@ +package goja + +import ( + "reflect" + "strconv" + + "github.com/dop251/goja/unistring" +) + +type objectGoArrayReflect struct { + objectGoReflect + lengthProp valueProperty + + putIdx func(idx int, v Value, throw bool) bool +} + +func (o *objectGoArrayReflect) _init() { + o.objectGoReflect.init() + o.class = classArray + o.prototype = o.val.runtime.global.ArrayPrototype + o.updateLen() + o.baseObject._put("length", &o.lengthProp) +} + +func (o *objectGoArrayReflect) init() { + o._init() + o.putIdx = o._putIdx +} + +func (o *objectGoArrayReflect) updateLen() { + o.lengthProp.value = intToValue(int64(o.value.Len())) +} + +func (o *objectGoArrayReflect) _hasIdx(idx valueInt) bool { + if idx := int64(idx); idx >= 0 && idx < int64(o.value.Len()) { + return true + } + return false +} + +func (o *objectGoArrayReflect) _hasStr(name unistring.String) bool { + if idx := strToIdx64(name); idx >= 0 && idx < int64(o.value.Len()) { + return true + } + return false +} + +func (o *objectGoArrayReflect) _getIdx(idx int) Value { + v := o.value.Index(idx) + if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() { + return _null + } + return o.val.runtime.ToValue(v.Interface()) +} + +func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value { + if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.value.Len() { + return o._getIdx(idx) + } + return o.objectGoReflect.getStr(idx.string(), receiver) +} + +func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Value { + var ownProp Value + if idx := strToGoIdx(name); idx >= 0 && idx < o.value.Len() { + ownProp = o._getIdx(idx) + } else if name == "length" { + ownProp = &o.lengthProp + } else { + ownProp = o.objectGoReflect.getOwnPropStr(name) + } + return o.getStrWithOwnProp(ownProp, name, receiver) +} + +func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value { + if idx := strToGoIdx(name); idx >= 0 { + if idx < o.value.Len() { + return &valueProperty{ + value: o._getIdx(idx), + writable: true, + enumerable: true, + } + } + return nil + } + if name == "length" { + return &o.lengthProp + } + return o.objectGoReflect.getOwnPropStr(name) +} + +func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value { + if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.value.Len() { + return &valueProperty{ + value: o._getIdx(idx), + writable: true, + enumerable: true, + } + } + return nil +} + +func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool { + err := o.val.runtime.toReflectValue(v, o.value.Index(idx), &objectExportCtx{}) + if err != nil { + o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err) + return false + } + return true +} + +func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool { + if i := toIntStrict(int64(idx)); i >= 0 { + if i >= o.value.Len() { + if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok { + return res + } + } + return o.putIdx(i, val, throw) + } else { + name := idx.string() + if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { + o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) + return false + } else { + return res + } + } +} + +func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool { + if idx := strToGoIdx(name); idx >= 0 { + if idx >= o.value.Len() { + if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok { + return res + } + } + return o.putIdx(idx, val, throw) + } else { + if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { + o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) + return false + } else { + return res + } + } +} + +func (o *objectGoArrayReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) { + return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw) +} + +func (o *objectGoArrayReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { + return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw) +} + +func (o *objectGoArrayReflect) hasOwnPropertyIdx(idx valueInt) bool { + return o._hasIdx(idx) +} + +func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool { + if o._hasStr(name) || name == "length" { + return true + } + return o.objectGoReflect._has(name.String()) +} + +func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { + if i := toIntStrict(int64(idx)); i >= 0 { + if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) { + return false + } + val := descr.Value + if val == nil { + val = _undefined + } + return o.putIdx(i, val, throw) + } + o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx) + return false +} + +func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { + if idx := strToGoIdx(name); idx >= 0 { + if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) { + return false + } + val := descr.Value + if val == nil { + val = _undefined + } + return o.putIdx(idx, val, throw) + } + o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name) + return false +} + +func (o *objectGoArrayReflect) toPrimitiveNumber() Value { + return o.toPrimitiveString() +} + +func (o *objectGoArrayReflect) toPrimitiveString() Value { + return o.val.runtime.arrayproto_join(FunctionCall{ + This: o.val, + }) +} + +func (o *objectGoArrayReflect) toPrimitive() Value { + return o.toPrimitiveString() +} + +func (o *objectGoArrayReflect) _deleteIdx(idx int) { + if idx < o.value.Len() { + o.value.Index(idx).Set(reflect.Zero(o.value.Type().Elem())) + } +} + +func (o *objectGoArrayReflect) deleteStr(name unistring.String, throw bool) bool { + if idx := strToGoIdx(name); idx >= 0 { + o._deleteIdx(idx) + return true + } + + return o.objectGoReflect.deleteStr(name, throw) +} + +func (o *objectGoArrayReflect) deleteIdx(i valueInt, throw bool) bool { + idx := toIntStrict(int64(i)) + if idx >= 0 { + o._deleteIdx(idx) + } + return true +} + +type goArrayReflectPropIter struct { + o *objectGoArrayReflect + idx, limit int +} + +func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) { + if i.idx < i.limit && i.idx < i.o.value.Len() { + name := strconv.Itoa(i.idx) + i.idx++ + return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next + } + + return i.o.objectGoReflect.iterateStringKeys()() +} + +func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value { + for i := 0; i < o.value.Len(); i++ { + accum = append(accum, asciiString(strconv.Itoa(i))) + } + + return o.objectGoReflect.stringKeys(all, accum) +} + +func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc { + return (&goArrayReflectPropIter{ + o: o, + limit: o.value.Len(), + }).next +} + +func (o *objectGoArrayReflect) equal(other objectImpl) bool { + if other, ok := other.(*objectGoArrayReflect); ok { + return o.value.Interface() == other.value.Interface() + } + return false +} + +func (o *objectGoArrayReflect) sortLen() int64 { + return int64(o.value.Len()) +} + +func (o *objectGoArrayReflect) sortGet(i int64) Value { + return o.getIdx(valueInt(i), nil) +} + +func (o *objectGoArrayReflect) swap(i, j int64) { + ii := toIntStrict(i) + jj := toIntStrict(j) + x := o._getIdx(ii) + y := o._getIdx(jj) + + o._putIdx(ii, y, false) + o._putIdx(jj, x, false) +} diff --git a/vendor/github.com/dop251/goja/object_goreflect.go b/vendor/github.com/dop251/goja/object_goreflect.go index 7d06b2c6d55..86e8fb54b08 100644 --- a/vendor/github.com/dop251/goja/object_goreflect.go +++ b/vendor/github.com/dop251/goja/object_goreflect.go @@ -103,6 +103,12 @@ func (o *objectGoReflect) init() { default: o.class = classObject o.prototype = o.val.runtime.global.ObjectPrototype + if !o.value.CanAddr() { + value := reflect.Indirect(reflect.New(o.value.Type())) + value.Set(o.value) + o.origValue = value + o.value = value + } } o.extensible = true diff --git a/vendor/github.com/dop251/goja/object_goslice_reflect.go b/vendor/github.com/dop251/goja/object_goslice_reflect.go index 94284806fdd..2da5f8dcf8f 100644 --- a/vendor/github.com/dop251/goja/object_goslice_reflect.go +++ b/vendor/github.com/dop251/goja/object_goslice_reflect.go @@ -4,113 +4,25 @@ import ( "math" "math/bits" "reflect" - "strconv" "github.com/dop251/goja/unistring" ) type objectGoSliceReflect struct { - objectGoReflect - lengthProp valueProperty + objectGoArrayReflect } func (o *objectGoSliceReflect) init() { - o.objectGoReflect.init() - o.class = classArray - o.prototype = o.val.runtime.global.ArrayPrototype - if !o.value.CanSet() { - value := reflect.Indirect(reflect.New(o.value.Type())) - value.Set(o.value) - o.value = value - } + o.objectGoArrayReflect._init() o.lengthProp.writable = true - o.updateLen() - o.baseObject._put("length", &o.lengthProp) -} - -func (o *objectGoSliceReflect) updateLen() { - o.lengthProp.value = intToValue(int64(o.value.Len())) -} - -func (o *objectGoSliceReflect) _hasIdx(idx valueInt) bool { - if idx := int64(idx); idx >= 0 && idx < int64(o.value.Len()) { - return true - } - return false -} - -func (o *objectGoSliceReflect) _hasStr(name unistring.String) bool { - if idx := strToIdx64(name); idx >= 0 && idx < int64(o.value.Len()) { - return true - } - return false -} - -func (o *objectGoSliceReflect) _getIdx(idx int) Value { - v := o.value.Index(idx) - if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() { - return _null - } - return o.val.runtime.ToValue(v.Interface()) + o.putIdx = o._putIdx } -func (o *objectGoSliceReflect) getIdx(idx valueInt, receiver Value) Value { - if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.value.Len() { - return o._getIdx(idx) - } - return o.objectGoReflect.getStr(idx.string(), receiver) -} - -func (o *objectGoSliceReflect) getStr(name unistring.String, receiver Value) Value { - var ownProp Value - if idx := strToGoIdx(name); idx >= 0 && idx < o.value.Len() { - ownProp = o._getIdx(idx) - } else if name == "length" { - ownProp = &o.lengthProp - } else { - ownProp = o.objectGoReflect.getOwnPropStr(name) - } - return o.getStrWithOwnProp(ownProp, name, receiver) -} - -func (o *objectGoSliceReflect) getOwnPropStr(name unistring.String) Value { - if idx := strToGoIdx(name); idx >= 0 { - if idx < o.value.Len() { - return &valueProperty{ - value: o._getIdx(idx), - writable: true, - enumerable: true, - } - } - return nil - } - if name == "length" { - return &o.lengthProp - } - return o.objectGoReflect.getOwnPropStr(name) -} - -func (o *objectGoSliceReflect) getOwnPropIdx(idx valueInt) Value { - if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.value.Len() { - return &valueProperty{ - value: o._getIdx(idx), - writable: true, - enumerable: true, - } - } - return nil -} - -func (o *objectGoSliceReflect) putIdx(idx int, v Value, throw bool) bool { +func (o *objectGoSliceReflect) _putIdx(idx int, v Value, throw bool) bool { if idx >= o.value.Len() { o.grow(idx + 1) } - err := o.val.runtime.toReflectValue(v, o.value.Index(idx), &objectExportCtx{}) - if err != nil { - o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err) - return false - } - return true + return o.objectGoArrayReflect._putIdx(idx, v, throw) } func (o *objectGoSliceReflect) grow(size int) { @@ -154,167 +66,18 @@ func (o *objectGoSliceReflect) putLength(v uint32, throw bool) bool { return true } -func (o *objectGoSliceReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool { - if i := toIntStrict(int64(idx)); i >= 0 { - if i >= o.value.Len() { - if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok { - return res - } - } - o.putIdx(i, val, throw) - } else { - name := idx.string() - if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { - o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) - return false - } else { - return res - } - } - return true -} - func (o *objectGoSliceReflect) setOwnStr(name unistring.String, val Value, throw bool) bool { - if idx := strToGoIdx(name); idx >= 0 { - if idx >= o.value.Len() { - if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok { - return res - } - } - o.putIdx(idx, val, throw) - } else { - if name == "length" { - return o.putLength(o.val.runtime.toLengthUint32(val), throw) - } - if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { - o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) - return false - } else { - return res - } - } - return true -} - -func (o *objectGoSliceReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) { - return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw) -} - -func (o *objectGoSliceReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { - return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw) -} - -func (o *objectGoSliceReflect) hasOwnPropertyIdx(idx valueInt) bool { - return o._hasIdx(idx) -} - -func (o *objectGoSliceReflect) hasOwnPropertyStr(name unistring.String) bool { - if o._hasStr(name) || name == "length" { - return true - } - return o.objectGoReflect._has(name.String()) -} - -func (o *objectGoSliceReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { - if i := toIntStrict(int64(idx)); i >= 0 { - if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) { - return false - } - val := descr.Value - if val == nil { - val = _undefined - } - o.putIdx(i, val, throw) - return true + if name == "length" { + return o.putLength(o.val.runtime.toLengthUint32(val), throw) } - o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx) - return false + return o.objectGoArrayReflect.setOwnStr(name, val, throw) } func (o *objectGoSliceReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { - if idx := strToGoIdx(name); idx >= 0 { - if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) { - return false - } - val := descr.Value - if val == nil { - val = _undefined - } - o.putIdx(idx, val, throw) - return true - } if name == "length" { return o.val.runtime.defineArrayLength(&o.lengthProp, descr, o.putLength, throw) } - o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name) - return false -} - -func (o *objectGoSliceReflect) toPrimitiveNumber() Value { - return o.toPrimitiveString() -} - -func (o *objectGoSliceReflect) toPrimitiveString() Value { - return o.val.runtime.arrayproto_join(FunctionCall{ - This: o.val, - }) -} - -func (o *objectGoSliceReflect) toPrimitive() Value { - return o.toPrimitiveString() -} - -func (o *objectGoSliceReflect) _deleteIdx(idx int) { - if idx < o.value.Len() { - o.value.Index(idx).Set(reflect.Zero(o.value.Type().Elem())) - } -} - -func (o *objectGoSliceReflect) deleteStr(name unistring.String, throw bool) bool { - if idx := strToGoIdx(name); idx >= 0 { - o._deleteIdx(idx) - return true - } - - return o.objectGoReflect.deleteStr(name, throw) -} - -func (o *objectGoSliceReflect) deleteIdx(i valueInt, throw bool) bool { - idx := toIntStrict(int64(i)) - if idx >= 0 { - o._deleteIdx(idx) - } - return true -} - -type gosliceReflectPropIter struct { - o *objectGoSliceReflect - idx, limit int -} - -func (i *gosliceReflectPropIter) next() (propIterItem, iterNextFunc) { - if i.idx < i.limit && i.idx < i.o.value.Len() { - name := strconv.Itoa(i.idx) - i.idx++ - return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next - } - - return i.o.objectGoReflect.iterateStringKeys()() -} - -func (o *objectGoSliceReflect) stringKeys(all bool, accum []Value) []Value { - for i := 0; i < o.value.Len(); i++ { - accum = append(accum, asciiString(strconv.Itoa(i))) - } - - return o.objectGoReflect.stringKeys(all, accum) -} - -func (o *objectGoSliceReflect) iterateStringKeys() iterNextFunc { - return (&gosliceReflectPropIter{ - o: o, - limit: o.value.Len(), - }).next + return o.objectGoArrayReflect.defineOwnPropertyStr(name, descr, throw) } func (o *objectGoSliceReflect) equal(other objectImpl) bool { @@ -323,21 +86,3 @@ func (o *objectGoSliceReflect) equal(other objectImpl) bool { } return false } - -func (o *objectGoSliceReflect) sortLen() int64 { - return int64(o.value.Len()) -} - -func (o *objectGoSliceReflect) sortGet(i int64) Value { - return o.getIdx(valueInt(i), nil) -} - -func (o *objectGoSliceReflect) swap(i, j int64) { - ii := valueInt(i) - jj := valueInt(j) - x := o.getIdx(ii, nil) - y := o.getIdx(jj, nil) - - o.setOwnIdx(ii, y, false) - o.setOwnIdx(jj, x, false) -} diff --git a/vendor/github.com/dop251/goja/object_lazy.go b/vendor/github.com/dop251/goja/object_lazy.go index 6c0cf51cec5..e8de40e2454 100644 --- a/vendor/github.com/dop251/goja/object_lazy.go +++ b/vendor/github.com/dop251/goja/object_lazy.go @@ -253,6 +253,18 @@ func (o *lazyObject) exportType() reflect.Type { return obj.exportType() } +func (o *lazyObject) exportToMap(m reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + obj := o.create(o.val) + o.val.self = obj + return obj.exportToMap(m, typ, ctx) +} + +func (o *lazyObject) exportToArrayOrSlice(s reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + obj := o.create(o.val) + o.val.self = obj + return obj.exportToArrayOrSlice(s, typ, ctx) +} + func (o *lazyObject) equal(other objectImpl) bool { obj := o.create(o.val) o.val.self = obj diff --git a/vendor/github.com/dop251/goja/runtime.go b/vendor/github.com/dop251/goja/runtime.go index 914497240b2..86a16f13fdf 100644 --- a/vendor/github.com/dop251/goja/runtime.go +++ b/vendor/github.com/dop251/goja/runtime.go @@ -1463,6 +1463,8 @@ Export()'ed and therefore copied. This may result in an unexpected behaviour in `) fmt.Println(m["obj"].(map[string]interface{})["test"]) // prints "false" +Non-addressable structs, slices and arrays get copied (as if they were passed as a function parameter, by value). + Notes on individual types: Primitive types @@ -1539,8 +1541,7 @@ Structs Structs are converted to Object-like values. Fields and methods are available as properties, their values are results of this method (ToValue()) applied to the corresponding Go value. -Field properties are writable (if the struct is addressable) and non-configurable. -Method properties are non-writable and non-configurable. +Field properties are writable and non-configurable. Method properties are non-writable and non-configurable. Attempt to define a new property or delete an existing property will fail (throw in strict mode) unless it's a Symbol property. Symbol properties only exist in the wrapper and do not affect the underlying Go value. @@ -1611,6 +1612,11 @@ an index < length will set it to a zero value (but the property will remain). Ni `null`. Accessing an element beyond `length` returns `undefined`. Also see the warning above about passing slices as values (as opposed to pointers). +Arrays + +Arrays are converted similarly to slices, except the resulting Arrays are not resizable (and therefore the 'length' +property is non-writable). + Any other type is converted to a generic reflect based host object. Depending on the underlying type it behaves similar to a Number, String, Boolean or Object. @@ -1767,9 +1773,9 @@ func (r *Runtime) ToValue(i interface{}) Value { return obj } } - case reflect.Slice: + case reflect.Array: obj := &Object{runtime: r} - a := &objectGoSliceReflect{ + a := &objectGoArrayReflect{ objectGoReflect: objectGoReflect{ baseObject: baseObject{ val: obj, @@ -1781,6 +1787,22 @@ func (r *Runtime) ToValue(i interface{}) Value { a.init() obj.self = a return obj + case reflect.Slice: + obj := &Object{runtime: r} + a := &objectGoSliceReflect{ + objectGoArrayReflect: objectGoArrayReflect{ + objectGoReflect: objectGoReflect{ + baseObject: baseObject{ + val: obj, + }, + origValue: origValue, + value: value, + }, + }, + } + a.init() + obj.self = a + return obj case reflect.Func: name := unistring.NewFromString(runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()) return r.newNativeFunc(r.wrapReflectFunc(value), nil, name, nil, value.Type().NumIn()) @@ -1822,7 +1844,6 @@ func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value in = make([]reflect.Value, l) } - callSlice := false for i, a := range call.Arguments { var t reflect.Type @@ -1839,34 +1860,15 @@ func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value t = typ.In(n) } - // if this is a variadic Go function, and the caller has supplied - // exactly the number of JavaScript arguments required, and this - // is the last JavaScript argument, try treating the it as the - // actual set of variadic Go arguments. if that succeeds, break - // out of the loop. - if typ.IsVariadic() && len(call.Arguments) == nargs && i == nargs-1 { - v := reflect.New(typ.In(n)).Elem() - if err := r.toReflectValue(a, v, &objectExportCtx{}); err == nil { - in[i] = v - callSlice = true - break - } - } v := reflect.New(t).Elem() err := r.toReflectValue(a, v, &objectExportCtx{}) if err != nil { - panic(r.newError(r.global.TypeError, "could not convert function call parameter %v to %v", a, t)) + panic(r.NewTypeError("could not convert function call parameter %d: %v", i, err)) } in[i] = v } - var out []reflect.Value - if callSlice { - out = value.CallSlice(in) - } else { - out = value.Call(in) - } - + out := value.Call(in) if len(out) == 0 { return _undefined } @@ -2012,84 +2014,31 @@ func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCt case reflect.Float32: dst.Set(reflect.ValueOf(toFloat32(v)).Convert(typ)) return nil - case reflect.Slice: + case reflect.Slice, reflect.Array: if o, ok := v.(*Object); ok { - if o.self.className() == classArray { - if v, exists := ctx.getTyped(o.self, typ); exists { - dst.Set(reflect.ValueOf(v)) - return nil - } - l := int(toLength(o.self.getStr("length", nil))) - if dst.IsNil() || dst.Len() != l { - dst.Set(reflect.MakeSlice(typ, l, l)) - } - s := dst - ctx.putTyped(o.self, typ, s.Interface()) - for i := 0; i < l; i++ { - item := o.self.getIdx(valueInt(int64(i)), nil) - err := r.toReflectValue(item, s.Index(i), ctx) - if err != nil { - return fmt.Errorf("could not convert array element %v to %v at %d: %w", v, typ, i, err) - } - } + if v, exists := ctx.getTyped(o, typ); exists { + dst.Set(reflect.ValueOf(v)) return nil } + return o.self.exportToArrayOrSlice(dst, typ, ctx) } case reflect.Map: if o, ok := v.(*Object); ok { - if v, exists := ctx.getTyped(o.self, typ); exists { + if v, exists := ctx.getTyped(o, typ); exists { dst.Set(reflect.ValueOf(v)) return nil } - if dst.IsNil() { - dst.Set(reflect.MakeMap(typ)) - } - m := dst - ctx.putTyped(o.self, typ, m.Interface()) - keyTyp := typ.Key() - elemTyp := typ.Elem() - needConvertKeys := !reflect.ValueOf("").Type().AssignableTo(keyTyp) - iter := &enumerableIter{ - o: o, - wrapped: o.self.iterateStringKeys(), - } - for item, next := iter.next(); next != nil; item, next = next() { - var kv reflect.Value - var err error - if needConvertKeys { - kv = reflect.New(keyTyp).Elem() - err = r.toReflectValue(item.name, kv, ctx) - if err != nil { - return fmt.Errorf("could not convert map key %s to %v", item.name.String(), typ) - } - } else { - kv = reflect.ValueOf(item.name.String()) - } - - ival := o.self.getStr(item.name.string(), nil) - if ival != nil { - vv := reflect.New(elemTyp).Elem() - err := r.toReflectValue(ival, vv, ctx) - if err != nil { - return fmt.Errorf("could not convert map value %v to %v at key %s", ival, typ, item.name.String()) - } - m.SetMapIndex(kv, vv) - } else { - m.SetMapIndex(kv, reflect.Zero(elemTyp)) - } - } - - return nil + return o.self.exportToMap(dst, typ, ctx) } case reflect.Struct: if o, ok := v.(*Object); ok { t := reflect.PtrTo(typ) - if v, exists := ctx.getTyped(o.self, t); exists { + if v, exists := ctx.getTyped(o, t); exists { dst.Set(reflect.ValueOf(v).Elem()) return nil } s := dst - ctx.putTyped(o.self, t, s.Addr().Interface()) + ctx.putTyped(o, t, s.Addr().Interface()) for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) if ast.IsExported(field.Name) { @@ -2121,7 +2070,7 @@ func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCt } case reflect.Ptr: if o, ok := v.(*Object); ok { - if v, exists := ctx.getTyped(o.self, typ); exists { + if v, exists := ctx.getTyped(o, typ); exists { dst.Set(reflect.ValueOf(v)) return nil } @@ -2173,10 +2122,73 @@ func (r *Runtime) wrapJSFunc(fn Callable, typ reflect.Type) func(args []reflect. } // ExportTo converts a JavaScript value into the specified Go value. The second parameter must be a non-nil pointer. -// Exporting to an interface{} results in a value of the same type as Export() would produce. -// Exporting to numeric types uses the standard ECMAScript conversion operations, same as used when assigning -// values to non-clamped typed array items, e.g. https://262.ecma-international.org/#sec-toint32 // Returns error if conversion is not possible. +// +// Notes on specific cases: +// +// Empty interface +// +// Exporting to an interface{} results in a value of the same type as Value.Export() would produce. +// +// Numeric types +// +// Exporting to numeric types uses the standard ECMAScript conversion operations, same as used when assigning +// values to non-clamped typed array items, e.g. https://262.ecma-international.org/#sec-toint32. +// +// Functions +// +// Exporting to a 'func' creates a strictly typed 'gateway' into an ES function which can be called from Go. +// The arguments are converted into ES values using Runtime.ToValue(). If the func has no return values, +// the return value is ignored. If the func has exactly one return value, it is converted to the appropriate +// type using ExportTo(). If the func has exactly 2 return values and the second value is 'error', exceptions +// are caught and returned as *Exception. In all other cases exceptions result in a panic. Any extra return values +// are zeroed. +// +// Note, if you want to catch and return exceptions as an `error` and you don't need the return value, +// 'func(...) error' will not work as expected. The 'error' in this case is mapped to the function return value, not +// the exception which will still result in a panic. Use 'func(...) (Value, error)' instead, and ignore the Value. +// +// 'this' value will always be set to 'undefined'. +// +// For a more low-level mechanism see AssertFunction(). +// +// Map types +// +// An ES Map can be exported into a Go map type. If any exported key value is non-hashable, the operation panics +// (as reflect.Value.SetMapIndex() would). Symbol.iterator is ignored. +// +// Exporting an ES Set into a map type results in the map being populated with (element) -> (zero value) key/value +// pairs. If any value is non-hashable, the operation panics (as reflect.Value.SetMapIndex() would). +// Symbol.iterator is ignored. +// +// Any other Object populates the map with own enumerable non-symbol properties. +// +// Slice types +// +// Exporting an ES Set into a slice type results in its elements being exported. +// +// Exporting any Object that implements the iterable protocol (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol) +// into a slice type results in the slice being populated with the results of the iteration. +// +// Array is treated as iterable (i.e. overwriting Symbol.iterator affects the result). +// +// If an object has a 'length' property and is not a function it is treated as array-like. The resulting slice +// will contain obj[0], ... obj[length-1]. +// +// For any other Object an error is returned. +// +// Array types +// +// Anything that can be exported to a slice type can also be exported to an array type, as long as the lengths +// match. If they do not, an error is returned. +// +// Proxy +// +// Proxy objects are treated the same way as if they were accessed from ES code in regard to their properties +// (such as 'length' or [Symbol.iterator]). This means exporting them to slice types works, however +// exporting a proxied Map into a map type does not produce its contents, because the Proxy is not recognised +// as a Map. Same applies to a proxied Set. +// func (r *Runtime) ExportTo(v Value, target interface{}) error { tval := reflect.ValueOf(target) if tval.Kind() != reflect.Ptr || tval.IsNil() { @@ -2689,8 +2701,8 @@ func (r *Runtime) invoke(v Value, p unistring.String, args ...Value) Value { return r.toCallable(o.self.getStr(p, nil))(FunctionCall{This: v, Arguments: args}) } -func (r *Runtime) iterableToList(items Value, method func(FunctionCall) Value) []Value { - iter := r.getIterator(items, method) +func (r *Runtime) iterableToList(iterable Value, method func(FunctionCall) Value) []Value { + iter := r.getIterator(iterable, method) var values []Value iter.iterate(func(item Value) { values = append(values, item) diff --git a/vendor/github.com/dop251/goja/value.go b/vendor/github.com/dop251/goja/value.go index 30460910abf..9a5720cd27e 100644 --- a/vendor/github.com/dop251/goja/value.go +++ b/vendor/github.com/dop251/goja/value.go @@ -53,6 +53,21 @@ var ( var intCache [256]Value +// Value represents an ECMAScript value. +// +// Export returns a "plain" Go value which type depends on the type of the Value. +// +// For integer numbers it's int64. +// +// For any other numbers (including Infinities, NaN and negative zero) it's float64. +// +// For string it's a string. Note that unicode strings are converted into UTF-8 with invalid code points replaced with utf8.RuneError. +// +// For boolean it's bool. +// +// For null and undefined it's nil. +// +// For Object it depends on the Object type, see Object.Export() for more details. type Value interface { ToInteger() int64 toString() valueString @@ -731,8 +746,28 @@ func (o *Object) baseObject(*Runtime) *Object { return o } -// Export the Object to a plain Go type. The returned value will be map[string]interface{} unless -// the Object is a wrapped Go value (created using ToValue()). +// Export the Object to a plain Go type. +// If the Object is a wrapped Go value (created using ToValue()) returns the original value. +// +// If the Object is a function, returns func(FunctionCall) Value. Note that exceptions thrown inside the function +// result in panics, which can also leave the Runtime in an unusable state. Therefore, these values should only +// be used inside another ES function implemented in Go. For calling a function from Go, use AssertFunction() or +// Runtime.ExportTo() as described in the README. +// +// For a Map, returns the list of entries as [][2]interface{}. +// +// For a Set, returns the list of elements as []interface{}. +// +// For a Proxy, returns Proxy. +// +// For a Promise, returns Promise. +// +// For a DynamicObject or a DynamicArray, returns the underlying handler. +// +// For an array, returns its items as []interface{}. +// +// In all other cases returns own enumerable non-symbol properties as map[string]interface{}. +// // This method will panic with an *Exception if a JavaScript exception is thrown in the process. func (o *Object) Export() (ret interface{}) { o.runtime.tryPanic(func() { @@ -742,6 +777,7 @@ func (o *Object) Export() (ret interface{}) { return } +// ExportType returns the type of the value that is returned by Export(). func (o *Object) ExportType() reflect.Type { return o.self.exportType() } diff --git a/vendor/modules.txt b/vendor/modules.txt index 698e2f28a7c..adadcbb4fbc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -23,7 +23,7 @@ github.com/davecgh/go-spew/spew ## explicit github.com/dlclark/regexp2 github.com/dlclark/regexp2/syntax -# github.com/dop251/goja v0.0.0-20220110113543-261677941f3c +# github.com/dop251/goja v0.0.0-20220124171016-cfb079cdc7b4 ## explicit; go 1.14 github.com/dop251/goja github.com/dop251/goja/ast