Skip to content

Commit

Permalink
Added support for go arrays (both to ToValue and ExportTo). Nom-addre…
Browse files Browse the repository at this point in the history
…ssable structs and arrays are copied in ToValue() so they remain writable in ES. Subsequent Export() returns the value including any changes made.
  • Loading branch information
dop251 committed Jan 24, 2022
1 parent 3c02223 commit dd567e7
Show file tree
Hide file tree
Showing 17 changed files with 629 additions and 325 deletions.
12 changes: 8 additions & 4 deletions array.go
Original file line number Diff line number Diff line change
Expand Up @@ -507,12 +507,16 @@ func (a *arrayObject) exportType() reflect.Type {
return reflectTypeArray
}

func (a *arrayObject) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
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.IsNil() || dst.Len() != l {
dst.Set(reflect.MakeSlice(typ, l, l))
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++ {
Expand All @@ -530,7 +534,7 @@ func (a *arrayObject) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *ob
}
return nil
}
return a.baseObject.exportToSlice(dst, typ, ctx)
return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
}

func (a *arrayObject) setValuesFromSparse(items []sparseArrayItem, newMaxIdx int) {
Expand Down
12 changes: 8 additions & 4 deletions array_sparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,16 @@ func (a *sparseArrayObject) exportType() reflect.Type {
return reflectTypeArray
}

func (a *sparseArrayObject) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
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.IsNil() || dst.Len() != l {
dst.Set(reflect.MakeSlice(typ, l, l))
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 {
Expand All @@ -483,5 +487,5 @@ func (a *sparseArrayObject) exportToSlice(dst reflect.Value, typ reflect.Type, c
}
return nil
}
return a.baseObject.exportToSlice(dst, typ, ctx)
return a.baseObject.exportToArrayOrSlice(dst, typ, ctx)
}
17 changes: 17 additions & 0 deletions builtin_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,23 @@ func ExampleRuntime_ExportTo_mapToSlice() {
// Output: [[1 true] [2 false]]
}

func ExampleRuntime_ExportTo_mapToTypedSlice() {
vm := New()
m, err := vm.RunString(`
new Map([[1, true], [2, false]]);
`)
if err != nil {
panic(err)
}
exp := make([][2]interface{}, 0)
err = vm.ExportTo(m, &exp)
if err != nil {
panic(err)
}
fmt.Println(exp)
// Output: [[1 true] [2 false]]
}

func BenchmarkMapDelete(b *testing.B) {
var key1 Value = asciiString("a")
var key2 Value = asciiString("b")
Expand Down
15 changes: 11 additions & 4 deletions builtin_set.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package goja

import "reflect"
import (
"fmt"
"reflect"
)

var setExportType = reflectTypeArray

Expand Down Expand Up @@ -60,10 +63,14 @@ func (so *setObject) export(ctx *objectExportCtx) interface{} {
return a
}

func (so *setObject) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
func (so *setObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
l := so.m.size
if dst.IsNil() || dst.Len() != l {
dst.Set(reflect.MakeSlice(typ, l, l))
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()
Expand Down
19 changes: 19 additions & 0 deletions builtin_set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package goja

import (
"fmt"
"strings"
"testing"
)

Expand Down Expand Up @@ -81,3 +82,21 @@ func TestSetExportToSliceCircular(t *testing.T) {
t.Fatalf("a: %v", a)
}
}

func TestSetExportToArrayMismatchedLengths(t *testing.T) {
vm := New()
s, err := vm.RunString(`
new Set([1, 2])
`)
if err != nil {
panic(err)
}
var s1 [3]int
err = vm.ExportTo(s, &s1)
if err == nil {
t.Fatal("expected error")
}
if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
t.Fatalf("unexpected error: %v", err)
}
}
4 changes: 2 additions & 2 deletions destruct.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,8 @@ func (d *destructKeyedSource) exportToMap(dst reflect.Value, typ reflect.Type, c
return d.w().exportToMap(dst, typ, ctx)
}

func (d *destructKeyedSource) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
return d.w().exportToSlice(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 {
Expand Down
24 changes: 16 additions & 8 deletions object.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ type objectImpl interface {
export(ctx *objectExportCtx) interface{}
exportType() reflect.Type
exportToMap(m reflect.Value, typ reflect.Type, ctx *objectExportCtx) error
exportToSlice(s reflect.Value, typ reflect.Type, ctx *objectExportCtx) error
exportToArrayOrSlice(s reflect.Value, typ reflect.Type, ctx *objectExportCtx) error
equal(objectImpl) bool

iterateStringKeys() iterNextFunc
Expand Down Expand Up @@ -1014,7 +1014,7 @@ func (o *baseObject) exportToMap(m reflect.Value, typ reflect.Type, ctx *objectE
return genericExportToMap(o.val, m, typ, ctx)
}

func genericExportToSlice(o *Object, dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) (err error) {
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 {
Expand All @@ -1028,8 +1028,12 @@ func genericExportToSlice(o *Object, dst reflect.Value, typ reflect.Type, ctx *o
if ex != nil {
return ex
}
if dst.IsNil() || dst.Len() != len(values) {
dst.Set(reflect.MakeSlice(typ, len(values), len(values)))
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 {
Expand All @@ -1041,8 +1045,12 @@ func genericExportToSlice(o *Object, dst reflect.Value, typ reflect.Type, ctx *o
} else {
// array-like
l := toIntStrict(toLength(o.self.getStr("length", nil)))
if dst.IsNil() || dst.Len() != l {
dst.Set(reflect.MakeSlice(typ, l, l))
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++ {
Expand All @@ -1057,8 +1065,8 @@ func genericExportToSlice(o *Object, dst reflect.Value, typ reflect.Type, ctx *o
return
}

func (o *baseObject) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
return genericExportToSlice(o.val, dst, typ, ctx)
func (o *baseObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
return genericExportToArrayOrSlice(o.val, dst, typ, ctx)
}

type enumerableFlag int
Expand Down
4 changes: 2 additions & 2 deletions object_dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,8 +487,8 @@ func (o *baseDynamicObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx
return genericExportToMap(o.val, dst, typ, ctx)
}

func (o *baseDynamicObject) exportToSlice(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error {
return genericExportToSlice(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 {
Expand Down
Loading

0 comments on commit dd567e7

Please sign in to comment.