Skip to content

Commit

Permalink
feat: support push to go slices (#460)
Browse files Browse the repository at this point in the history
Add support for direct use of push to go slices without converting to
array using .slice().

Fixes #357

Also:
* Add classGoSlice and use it, to make it clear the type when debugging.
  • Loading branch information
stevenh authored Nov 27, 2022
1 parent e92282a commit c4b9430
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 11 deletions.
1 change: 1 addition & 0 deletions consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const (
// Common classes.
classString = "String"
classGoArray = "GoArray"
classGoSlice = "GoSlice"
classNumber = "Number"
classDate = "Date"
classArray = "Array"
Expand Down
20 changes: 20 additions & 0 deletions issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -942,3 +942,23 @@ func Test_issue383(t *testing.T) {
`)
require.NoError(t, err)
}

func Test_issue357(t *testing.T) {
vm := New()
arr := []string{"wow", "hey"}
err := vm.Set("arr", arr)
require.NoError(t, err)

val, err := vm.Run(`
arr.push('another', 'more');
arr;
`)
require.NoError(t, err)
iface, err := val.Export()
require.NoError(t, err)

slice, ok := iface.([]string)
require.True(t, ok)

require.Equal(t, []string{"wow", "hey", "another", "more"}, slice)
}
5 changes: 3 additions & 2 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,8 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Val

tt := t.Elem()

if o.class == classArray {
switch o.class {
case classArray:
for i := int64(0); i < l; i++ {
p, ok := o.property[strconv.FormatInt(i, 10)]
if !ok {
Expand All @@ -450,7 +451,7 @@ func (self *_runtime) convertCallParameter(v Value, t reflect.Type) (reflect.Val

s.Index(int(i)).Set(ev)
}
} else if o.class == classGoArray {
case classGoArray, classGoSlice:
var gslice bool
switch o.value.(type) {
case *_goSliceObject:
Expand Down
13 changes: 11 additions & 2 deletions type_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@ func (runtime *_runtime) newArrayObject(length uint32) *_object {
}

func isArray(object *_object) bool {
return object != nil && (object.class == classArray || object.class == classGoArray)
if object == nil {
return false
}

switch object.class {
case classArray, classGoArray, classGoSlice:
return true
default:
return false
}
}

func objectLength(object *_object) uint32 {
Expand All @@ -25,7 +34,7 @@ func objectLength(object *_object) uint32 {
return object.get(propertyLength).value.(uint32)
case classString:
return uint32(object.get(propertyLength).value.(int))
case classGoArray:
case classGoArray, classGoSlice:
return uint32(object.get(propertyLength).value.(int))
}
return 0
Expand Down
43 changes: 36 additions & 7 deletions type_go_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

func (runtime *_runtime) newGoSliceObject(value reflect.Value) *_object {
self := runtime.newObject()
self.class = classGoArray // TODO GoSlice?
self.class = classGoSlice
self.objectClass = _classGoSlice
self.value = _newGoSliceObject(value)
return self
Expand All @@ -31,15 +31,43 @@ func (self _goSliceObject) getValue(index int64) (reflect.Value, bool) {
return reflect.Value{}, false
}

func (self _goSliceObject) setValue(index int64, value Value) bool {
indexValue, exists := self.getValue(index)
if !exists {
return false
func (self *_goSliceObject) setLength(value Value) {
want, err := value.ToInteger()
if err != nil {
panic(err)
}

wantInt := int(want)
switch {
case wantInt == self.value.Len():
// No change needed.
case wantInt < self.value.Cap():
// Fits in current capacity.
self.value.SetLen(wantInt)
default:
// Needs expanding.
newSlice := reflect.MakeSlice(self.value.Type(), wantInt, wantInt)
reflect.Copy(newSlice, self.value)
self.value = newSlice
}
}

func (self *_goSliceObject) setValue(index int64, value Value) bool {
reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind())
if err != nil {
panic(err)
}

indexValue, exists := self.getValue(index)
if !exists {
if int64(self.value.Len()) == index {
// Trying to append e.g. slice.push(...), allow it.
self.value = reflect.Append(self.value, reflectValue)
return true
}
return false
}

indexValue.Set(reflectValue)
return true
}
Expand All @@ -49,7 +77,7 @@ func goSliceGetOwnProperty(self *_object, name string) *_property {
if name == propertyLength {
return &_property{
value: toValue(self.value.(*_goSliceObject).value.Len()),
mode: 0,
mode: 0110,
}
}

Expand Down Expand Up @@ -93,7 +121,8 @@ func goSliceEnumerate(self *_object, all bool, each func(string) bool) {

func goSliceDefineOwnProperty(self *_object, name string, descriptor _property, throw bool) bool {
if name == propertyLength {
return self.runtime.typeErrorResult(throw)
self.value.(*_goSliceObject).setLength(descriptor.value.(Value))
return true
} else if index := stringToArrayIndex(name); index >= 0 {
if self.value.(*_goSliceObject).setValue(index, descriptor.value.(Value)) {
return true
Expand Down

0 comments on commit c4b9430

Please sign in to comment.