Skip to content

Commit

Permalink
feat: use exported methods when setting go values
Browse files Browse the repository at this point in the history
When putting JavaScript objects into _go*object use exported values.
This reverts PR #467 in favour of this more complete work.

Fixes #118 #252

Co-authored-by: Dmitry Panov <[email protected]>
  • Loading branch information
stevenh and dop251 committed Nov 28, 2022
1 parent eb72a6e commit de7f9b4
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 72 deletions.
15 changes: 15 additions & 0 deletions issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1122,3 +1122,18 @@ func Test_issue177(t *testing.T) {
require.NoError(t, err)
require.Equal(t, float64(9), exp)
}

func Test_issue159(t *testing.T) {
vm := New()
val, err := vm.Run(`
var Chars =" !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_` + "`" + `abcdefghijklmnopqrstuvwxyz{|}~€???????????Ž????????????ž? ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ ";
function CharToAscii(c){
return Chars.indexOf(c) + 32;
}
CharToAscii('A');
`)
require.NoError(t, err)
exp, err := val.Export()
require.NoError(t, err)
require.Equal(t, float64(65), exp)
}
16 changes: 10 additions & 6 deletions reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"reflect"
"strings"
"testing"

"github.com/stretchr/testify/require"
)

type _abcStruct struct {
Expand Down Expand Up @@ -138,14 +140,16 @@ func (mno _mnoStruct) Func() string {
}

func TestReflect(t *testing.T) {
if true {
return
}
tt(t, func() {
// Testing dbgf
// These should panic
toValue("Xyzzy").toReflectValue(reflect.Ptr)
stringToReflectValue("Xyzzy", reflect.Ptr)
str := "test"
require.Panics(t, func() {
toValue("Xyzzy").toReflectValue(reflect.ValueOf(&str).Type())
})
require.Panics(t, func() {
stringToReflectValue("Xyzzy", reflect.Ptr)
})
})
}

Expand Down Expand Up @@ -708,7 +712,7 @@ func Test_reflectMapInterface(t *testing.T) {
`, "Nothing happens.,1,[object Object],[object Object]")

is(abc["xyz"], "pqr")
is(abc["ghi"], "[object Object]")
is(abc["ghi"], map[string]interface{}{})
is(abc["jkl"], float64(3.14159))
mno, valid := abc["mno"].(*_abcStruct)
is(valid, true)
Expand Down
2 changes: 1 addition & 1 deletion type_go_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func (self _goArrayObject) setValue(index int64, value Value) bool {
if !exists {
return false
}
reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem().Kind())
reflectValue, err := value.toReflectValue(reflect.Indirect(self.value).Type().Elem())
if err != nil {
panic(err)
}
Expand Down
12 changes: 6 additions & 6 deletions type_go_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ func (runtime *_runtime) newGoMapObject(value reflect.Value) *_object {

type _goMapObject struct {
value reflect.Value
keyKind reflect.Kind
valueKind reflect.Kind
keyType reflect.Type
valueType reflect.Type
}

func _newGoMapObject(value reflect.Value) *_goMapObject {
Expand All @@ -24,22 +24,22 @@ func _newGoMapObject(value reflect.Value) *_goMapObject {
}
self := &_goMapObject{
value: value,
keyKind: value.Type().Key().Kind(),
valueKind: value.Type().Elem().Kind(),
keyType: value.Type().Key(),
valueType: value.Type().Elem(),
}
return self
}

func (self _goMapObject) toKey(name string) reflect.Value {
reflectValue, err := stringToReflectValue(name, self.keyKind)
reflectValue, err := stringToReflectValue(name, self.keyType.Kind())
if err != nil {
panic(err)
}
return reflectValue
}

func (self _goMapObject) toValue(value Value) reflect.Value {
reflectValue, err := value.toReflectValue(self.valueKind)
reflectValue, err := value.toReflectValue(self.valueType)
if err != nil {
panic(err)
}
Expand Down
2 changes: 1 addition & 1 deletion type_go_slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (self *_goSliceObject) setLength(value Value) {
}

func (self *_goSliceObject) setValue(index int64, value Value) bool {
reflectValue, err := value.toReflectValue(self.value.Type().Elem().Kind())
reflectValue, err := value.toReflectValue(self.value.Type().Elem())
if err != nil {
panic(err)
}
Expand Down
82 changes: 25 additions & 57 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -645,13 +645,6 @@ func (self Value) export() interface{} {
case *_goStructObject:
return value.value.Interface()
case *_goMapObject:
iter := value.value.MapRange()
for iter.Next() {
v := iter.Value()
if ov, ok := v.Interface().(Value); ok {
value.value.SetMapIndex(iter.Key(), reflect.ValueOf(ov.export()))
}
}
return value.value.Interface()
case *_goArrayObject:
return value.value.Interface()
Expand Down Expand Up @@ -756,42 +749,13 @@ func (self Value) evaluateBreak(labels []string) _resultKind {
return resultReturn
}

func (self Value) exportNative() interface{} {
switch self.kind {
case valueUndefined:
return self
case valueNull:
return nil
case valueNumber, valueBoolean:
return self.value
case valueString:
switch value := self.value.(type) {
case string:
return value
case []uint16:
return string(utf16.Decode(value))
}
case valueObject:
object := self._object()
switch value := object.value.(type) {
case *_goStructObject:
return value.value.Interface()
case *_goMapObject:
return value.value.Interface()
case *_goArrayObject:
return value.value.Interface()
case *_goSliceObject:
return value.value.Interface()
}
}

return self
}

// Make a best effort to return a reflect.Value corresponding to reflect.Kind, but
// fallback to just returning the Go value we have handy.
func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
if kind != reflect.Float32 && kind != reflect.Float64 && kind != reflect.Interface {
func (value Value) toReflectValue(typ reflect.Type) (reflect.Value, error) {
kind := typ.Kind()
switch kind {
case reflect.Float32, reflect.Float64, reflect.Interface:
default:
switch value := value.value.(type) {
case float32:
_, frac := math.Modf(float64(value))
Expand All @@ -808,36 +772,36 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {

switch kind {
case reflect.Bool: // Bool
return reflect.ValueOf(value.bool()), nil
return reflect.ValueOf(value.bool()).Convert(typ), nil
case reflect.Int: // Int
// We convert to float64 here because converting to int64 will not tell us
// if a value is outside the range of int64
tmp := toIntegerFloat(value)
if tmp < float_minInt || tmp > float_maxInt {
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to int", tmp, value)
} else {
return reflect.ValueOf(int(tmp)), nil
return reflect.ValueOf(int(tmp)).Convert(typ), nil
}
case reflect.Int8: // Int8
tmp := value.number().int64
if tmp < int64_minInt8 || tmp > int64_maxInt8 {
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int8", tmp, value)
} else {
return reflect.ValueOf(int8(tmp)), nil
return reflect.ValueOf(int8(tmp)).Convert(typ), nil
}
case reflect.Int16: // Int16
tmp := value.number().int64
if tmp < int64_minInt16 || tmp > int64_maxInt16 {
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int16", tmp, value)
} else {
return reflect.ValueOf(int16(tmp)), nil
return reflect.ValueOf(int16(tmp)).Convert(typ), nil
}
case reflect.Int32: // Int32
tmp := value.number().int64
if tmp < int64_minInt32 || tmp > int64_maxInt32 {
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to int32", tmp, value)
} else {
return reflect.ValueOf(int32(tmp)), nil
return reflect.ValueOf(int32(tmp)).Convert(typ), nil
}
case reflect.Int64: // Int64
// We convert to float64 here because converting to int64 will not tell us
Expand All @@ -846,7 +810,7 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
if tmp < float_minInt64 || tmp > float_maxInt64 {
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to int", tmp, value)
} else {
return reflect.ValueOf(int64(tmp)), nil
return reflect.ValueOf(int64(tmp)).Convert(typ), nil
}
case reflect.Uint: // Uint
// We convert to float64 here because converting to int64 will not tell us
Expand All @@ -855,28 +819,28 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
if tmp < 0 || tmp > float_maxUint {
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint", tmp, value)
} else {
return reflect.ValueOf(uint(tmp)), nil
return reflect.ValueOf(uint(tmp)).Convert(typ), nil
}
case reflect.Uint8: // Uint8
tmp := value.number().int64
if tmp < 0 || tmp > int64_maxUint8 {
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint8", tmp, value)
} else {
return reflect.ValueOf(uint8(tmp)), nil
return reflect.ValueOf(uint8(tmp)).Convert(typ), nil
}
case reflect.Uint16: // Uint16
tmp := value.number().int64
if tmp < 0 || tmp > int64_maxUint16 {
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint16", tmp, value)
} else {
return reflect.ValueOf(uint16(tmp)), nil
return reflect.ValueOf(uint16(tmp)).Convert(typ), nil
}
case reflect.Uint32: // Uint32
tmp := value.number().int64
if tmp < 0 || tmp > int64_maxUint32 {
return reflect.Value{}, fmt.Errorf("RangeError: %d (%v) to uint32", tmp, value)
} else {
return reflect.ValueOf(uint32(tmp)), nil
return reflect.ValueOf(uint32(tmp)).Convert(typ), nil
}
case reflect.Uint64: // Uint64
// We convert to float64 here because converting to int64 will not tell us
Expand All @@ -885,7 +849,7 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
if tmp < 0 || tmp > float_maxUint64 {
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to uint64", tmp, value)
} else {
return reflect.ValueOf(uint64(tmp)), nil
return reflect.ValueOf(uint64(tmp)).Convert(typ), nil
}
case reflect.Float32: // Float32
tmp := value.float64()
Expand All @@ -896,13 +860,13 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
if tmp1 > 0 && (tmp1 < math.SmallestNonzeroFloat32 || tmp1 > math.MaxFloat32) {
return reflect.Value{}, fmt.Errorf("RangeError: %f (%v) to float32", tmp, value)
} else {
return reflect.ValueOf(float32(tmp)), nil
return reflect.ValueOf(float32(tmp)).Convert(typ), nil
}
case reflect.Float64: // Float64
value := value.float64()
return reflect.ValueOf(float64(value)), nil
return reflect.ValueOf(float64(value)).Convert(typ), nil
case reflect.String: // String
return reflect.ValueOf(value.string()), nil
return reflect.ValueOf(value.string()).Convert(typ), nil
case reflect.Invalid: // Invalid
case reflect.Complex64: // FIXME? Complex64
case reflect.Complex128: // FIXME? Complex128
Expand All @@ -924,7 +888,11 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
case *_goSliceObject: // Slice
return reflect.ValueOf(vl.value.Interface()), nil
}
return reflect.ValueOf(value.exportNative()), nil
exported := reflect.ValueOf(value.export())
if exported.Type().ConvertibleTo(typ) {
return exported.Convert(typ), nil
}
return reflect.Value{}, fmt.Errorf("TypeError: could not convert %v to reflect.Type: %v", exported, typ)
case valueEmpty, valueResult, valueReference:
// These are invalid, and should panic
default:
Expand All @@ -933,7 +901,7 @@ func (value Value) toReflectValue(kind reflect.Kind) (reflect.Value, error) {
}

// FIXME Should this end up as a TypeError?
panic(fmt.Errorf("invalid conversion of %v (%v) to reflect.Kind: %v", value.kind, value, kind))
panic(fmt.Errorf("invalid conversion of %v (%v) to reflect.Type: %v", value.kind, value, typ))
}

func stringToReflectValue(value string, kind reflect.Kind) (reflect.Value, error) {
Expand Down
2 changes: 1 addition & 1 deletion value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func TestExport(t *testing.T) {
func Test_toReflectValue(t *testing.T) {
tt(t, func() {
value := toValue(0.0)
tmp, err := value.toReflectValue(reflect.Float32)
tmp, err := value.toReflectValue(reflect.TypeOf(0.0))
is(tmp.Float(), 0.0)
is(err, nil)
})
Expand Down

0 comments on commit de7f9b4

Please sign in to comment.