mirror of
https://github.com/robertkrimen/otto
synced 2025-10-19 19:55:30 +08:00
Merge pull request #190 from deoxxa/refactor-native-function-parameter-conversion
refactor native function parameter conversion
This commit is contained in:
commit
96f57ba9bb
1366
call_test.go
Normal file
1366
call_test.go
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -250,7 +250,7 @@ func Test_reflectStruct(t *testing.T) {
|
||||||
|
|
||||||
test(`raise:
|
test(`raise:
|
||||||
abc.Func1Int(1.1);
|
abc.Func1Int(1.1);
|
||||||
`, "converting float64 to int would cause loss of precision")
|
`, "RangeError: converting float64 to int would cause loss of precision")
|
||||||
|
|
||||||
test(`
|
test(`
|
||||||
var v = 1;
|
var v = 1;
|
||||||
|
|
|
||||||
336
runtime.go
336
runtime.go
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/robertkrimen/otto/ast"
|
"github.com/robertkrimen/otto/ast"
|
||||||
|
|
@ -189,13 +190,12 @@ func testObjectCoercible(value Value) (isObject bool, mustCoerce bool) {
|
||||||
case valueReference, valueEmpty, valueNull, valueUndefined:
|
case valueReference, valueEmpty, valueNull, valueUndefined:
|
||||||
return false, false
|
return false, false
|
||||||
case valueNumber, valueString, valueBoolean:
|
case valueNumber, valueString, valueBoolean:
|
||||||
isObject = false
|
return false, true
|
||||||
mustCoerce = true
|
|
||||||
case valueObject:
|
case valueObject:
|
||||||
isObject = true
|
return true, false
|
||||||
mustCoerce = false
|
default:
|
||||||
|
panic("this should never happen")
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) safeToValue(value interface{}) (Value, error) {
|
func (self *_runtime) safeToValue(value interface{}) (Value, error) {
|
||||||
|
|
@ -208,7 +208,9 @@ func (self *_runtime) safeToValue(value interface{}) (Value, error) {
|
||||||
|
|
||||||
// convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics.
|
// convertNumeric converts numeric parameter val from js to that of type t if it is safe to do so, otherwise it panics.
|
||||||
// This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily.
|
// This allows literals (int64), bitwise values (int32) and the general form (float64) of javascript numerics to be passed as parameters to go functions easily.
|
||||||
func convertNumeric(val reflect.Value, t reflect.Type) reflect.Value {
|
func (self *_runtime) convertNumeric(v Value, t reflect.Type) reflect.Value {
|
||||||
|
val := reflect.ValueOf(v.export())
|
||||||
|
|
||||||
if val.Kind() == t.Kind() {
|
if val.Kind() == t.Kind() {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
@ -225,20 +227,20 @@ func convertNumeric(val reflect.Value, t reflect.Type) reflect.Value {
|
||||||
return reflect.ValueOf(f64)
|
return reflect.ValueOf(f64)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
if reflect.Zero(t).OverflowFloat(f64) {
|
if reflect.Zero(t).OverflowFloat(f64) {
|
||||||
panic("converting float64 to float32 would overflow")
|
panic(self.panicRangeError("converting float64 to float32 would overflow"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return val.Convert(t)
|
return val.Convert(t)
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
i64 := int64(f64)
|
i64 := int64(f64)
|
||||||
if float64(i64) != f64 {
|
if float64(i64) != f64 {
|
||||||
panic(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t))
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would cause loss of precision", val.Type(), t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The float represents an integer
|
// The float represents an integer
|
||||||
val = reflect.ValueOf(i64)
|
val = reflect.ValueOf(i64)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("cannot convert %v to %v", val.Type(), t))
|
panic(self.panicTypeError(fmt.Sprintf("cannot convert %v to %v", val.Type(), t)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,15 +250,15 @@ func convertNumeric(val reflect.Value, t reflect.Type) reflect.Value {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
if reflect.Zero(t).OverflowInt(i64) {
|
if reflect.Zero(t).OverflowInt(i64) {
|
||||||
panic(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
}
|
}
|
||||||
return val.Convert(t)
|
return val.Convert(t)
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
if i64 < 0 {
|
if i64 < 0 {
|
||||||
panic(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t))
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would underflow", val.Type(), t)))
|
||||||
}
|
}
|
||||||
if reflect.Zero(t).OverflowUint(uint64(i64)) {
|
if reflect.Zero(t).OverflowUint(uint64(i64)) {
|
||||||
panic(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
}
|
}
|
||||||
return val.Convert(t)
|
return val.Convert(t)
|
||||||
}
|
}
|
||||||
|
|
@ -266,86 +268,174 @@ func convertNumeric(val reflect.Value, t reflect.Type) reflect.Value {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) {
|
if u64 > math.MaxInt64 || reflect.Zero(t).OverflowInt(int64(u64)) {
|
||||||
panic(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
}
|
}
|
||||||
return val.Convert(t)
|
return val.Convert(t)
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
if reflect.Zero(t).OverflowUint(u64) {
|
if reflect.Zero(t).OverflowUint(u64) {
|
||||||
panic(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t))
|
panic(self.panicRangeError(fmt.Sprintf("converting %v to %v would overflow", val.Type(), t)))
|
||||||
}
|
}
|
||||||
return val.Convert(t)
|
return val.Convert(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
panic(fmt.Sprintf("unsupported type %v for numeric conversion", val.Type()))
|
panic(self.panicTypeError(fmt.Sprintf("unsupported type %v for numeric conversion", val.Type())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// callParamConvert converts request val to type t if possible.
|
var typeOfValue = reflect.TypeOf(Value{})
|
||||||
|
|
||||||
|
// convertCallParameter converts request val to type t if possible.
|
||||||
// If the conversion fails due to overflow or type miss-match then it panics.
|
// If the conversion fails due to overflow or type miss-match then it panics.
|
||||||
// If no conversion is known then the original value is returned.
|
// If no conversion is known then the original value is returned.
|
||||||
func callParamConvert(val reflect.Value, t reflect.Type) reflect.Value {
|
func (self *_runtime) convertCallParameter(v Value, t reflect.Type) reflect.Value {
|
||||||
if val.Kind() == reflect.Interface {
|
if t == typeOfValue {
|
||||||
val = reflect.ValueOf(val.Interface())
|
return reflect.ValueOf(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t.Kind() {
|
if t.Kind() == reflect.Interface {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
iv := reflect.ValueOf(v.export())
|
||||||
if val.Kind() == t.Kind() {
|
if iv.Type().AssignableTo(t) {
|
||||||
// Types already match
|
return iv
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
return convertNumeric(val, t)
|
|
||||||
case reflect.Slice:
|
|
||||||
if val.Kind() != reflect.Slice {
|
|
||||||
// Conversion from none slice type to slice not possible
|
|
||||||
panic(fmt.Sprintf("cannot use %v as type %v", val, t))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// No supported conversion
|
|
||||||
return val
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elemType := t.Elem()
|
tk := t.Kind()
|
||||||
switch elemType.Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.Slice:
|
|
||||||
// Attempt to convert to slice of the type t
|
|
||||||
s := reflect.MakeSlice(reflect.SliceOf(elemType), val.Len(), val.Len())
|
|
||||||
for i := 0; i < val.Len(); i++ {
|
|
||||||
s.Index(i).Set(callParamConvert(val.Index(i), elemType))
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
if tk == reflect.Ptr {
|
||||||
}
|
switch v.kind {
|
||||||
|
case valueEmpty, valueNull, valueUndefined:
|
||||||
|
return reflect.Zero(t)
|
||||||
|
default:
|
||||||
|
var vv reflect.Value
|
||||||
|
if err := catchPanic(func() { vv = self.convertCallParameter(v, t.Elem()) }); err == nil {
|
||||||
|
if vv.CanAddr() {
|
||||||
|
return vv.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
// Not a slice type we can convert
|
pv := reflect.New(vv.Type())
|
||||||
return val
|
pv.Elem().Set(vv)
|
||||||
}
|
return pv
|
||||||
|
|
||||||
// callSliceRequired returns true if CallSlice is required instead of Call.
|
|
||||||
func callSliceRequired(param reflect.Type, val reflect.Value) bool {
|
|
||||||
vt := val.Type()
|
|
||||||
for param.Kind() == reflect.Slice {
|
|
||||||
if val.Kind() == reflect.Interface {
|
|
||||||
val = reflect.ValueOf(val.Interface())
|
|
||||||
vt = val.Type()
|
|
||||||
}
|
|
||||||
|
|
||||||
if vt.Kind() != reflect.Slice {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
vt = vt.Elem()
|
|
||||||
if val.Kind() != reflect.Invalid {
|
|
||||||
if val.Len() > 0 {
|
|
||||||
val = val.Index(0)
|
|
||||||
} else {
|
|
||||||
val = reflect.Value{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
param = param.Elem()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
switch tk {
|
||||||
|
case reflect.Bool:
|
||||||
|
return reflect.ValueOf(v.bool())
|
||||||
|
case reflect.String:
|
||||||
|
switch v.kind {
|
||||||
|
case valueString:
|
||||||
|
return reflect.ValueOf(v.value)
|
||||||
|
case valueNumber:
|
||||||
|
return reflect.ValueOf(fmt.Sprintf("%v", v.value))
|
||||||
|
}
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
|
||||||
|
switch v.kind {
|
||||||
|
case valueNumber:
|
||||||
|
return self.convertNumeric(v, t)
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if o := v._object(); o != nil {
|
||||||
|
if lv := o.get("length"); lv.IsNumber() {
|
||||||
|
l := lv.number().int64
|
||||||
|
|
||||||
|
s := reflect.MakeSlice(t, int(l), int(l))
|
||||||
|
|
||||||
|
tt := t.Elem()
|
||||||
|
|
||||||
|
for i := int64(0); i < l; i++ {
|
||||||
|
p, ok := o.property[strconv.FormatInt(i, 10)]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
e, ok := p.value.(Value)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ev := self.convertCallParameter(e, tt)
|
||||||
|
|
||||||
|
s.Index(int(i)).Set(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if o := v._object(); o != nil && t.Key().Kind() == reflect.String {
|
||||||
|
m := reflect.MakeMap(t)
|
||||||
|
|
||||||
|
o.enumerate(false, func(k string) bool {
|
||||||
|
m.SetMapIndex(reflect.ValueOf(k), self.convertCallParameter(o.get(k), t.Elem()))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
case reflect.Func:
|
||||||
|
if t.NumOut() > 1 {
|
||||||
|
panic(self.panicTypeError("converting JavaScript values to Go functions with more than one return value is currently not supported"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if o := v._object(); o != nil && o.class == "Function" {
|
||||||
|
return reflect.MakeFunc(t, func(args []reflect.Value) []reflect.Value {
|
||||||
|
l := make([]interface{}, len(args))
|
||||||
|
for i, a := range args {
|
||||||
|
if a.CanInterface() {
|
||||||
|
l[i] = a.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv, err := v.Call(nullValue, l...)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.NumOut() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []reflect.Value{self.convertCallParameter(rv, t.Out(0))}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk == reflect.String {
|
||||||
|
if o := v._object(); o != nil && o.hasProperty("toString") {
|
||||||
|
if fn := o.get("toString"); fn.IsFunction() {
|
||||||
|
sv, err := fn.Call(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var r reflect.Value
|
||||||
|
if err := catchPanic(func() { r = self.convertCallParameter(sv, t) }); err == nil {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reflect.ValueOf(v.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
s := "OTTO DOES NOT UNDERSTAND THIS TYPE"
|
||||||
|
switch v.kind {
|
||||||
|
case valueBoolean:
|
||||||
|
s = "boolean"
|
||||||
|
case valueNull:
|
||||||
|
s = "null"
|
||||||
|
case valueNumber:
|
||||||
|
s = "number"
|
||||||
|
case valueString:
|
||||||
|
s = "string"
|
||||||
|
case valueUndefined:
|
||||||
|
s = "undefined"
|
||||||
|
case valueObject:
|
||||||
|
s = v.Class()
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(self.panicTypeError("can't convert from %q to %q", s, t.String()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *_runtime) toValue(value interface{}) Value {
|
func (self *_runtime) toValue(value interface{}) Value {
|
||||||
|
|
@ -381,6 +471,7 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
value := reflect.ValueOf(value)
|
value := reflect.ValueOf(value)
|
||||||
|
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
switch reflect.Indirect(value).Kind() {
|
switch reflect.Indirect(value).Kind() {
|
||||||
|
|
@ -389,11 +480,18 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
return toValue_object(self.newGoArray(value))
|
return toValue_object(self.newGoArray(value))
|
||||||
}
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
return toValue_object(self.newGoStructObject(value))
|
||||||
|
case reflect.Map:
|
||||||
|
return toValue_object(self.newGoMapObject(value))
|
||||||
|
case reflect.Slice:
|
||||||
|
return toValue_object(self.newGoSlice(value))
|
||||||
|
case reflect.Array:
|
||||||
|
return toValue_object(self.newGoArray(value))
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
var name, file string
|
var name, file string
|
||||||
var line int
|
var line int
|
||||||
v := reflect.ValueOf(value)
|
if v := reflect.ValueOf(value); v.Kind() == reflect.Ptr {
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
pc := v.Pointer()
|
pc := v.Pointer()
|
||||||
fn := runtime.FuncForPC(pc)
|
fn := runtime.FuncForPC(pc)
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
|
|
@ -403,39 +501,54 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Maybe cache this?
|
typ := value.Type()
|
||||||
return toValue_object(self.newNativeFunction(name, file, line, func(call FunctionCall) Value {
|
|
||||||
argsCount := len(call.ArgumentList)
|
return toValue_object(self.newNativeFunction(name, file, line, func(c FunctionCall) Value {
|
||||||
in := make([]reflect.Value, len(call.ArgumentList))
|
nargs := typ.NumIn()
|
||||||
t := value.Type()
|
|
||||||
|
if len(c.ArgumentList) != nargs {
|
||||||
|
if typ.IsVariadic() {
|
||||||
|
if len(c.ArgumentList) < nargs-1 {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("expected at least %d arguments; got %d", nargs-1, len(c.ArgumentList))))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic(self.panicRangeError(fmt.Sprintf("expected %d argument(s); got %d", nargs, len(c.ArgumentList))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in := make([]reflect.Value, len(c.ArgumentList))
|
||||||
|
|
||||||
callSlice := false
|
callSlice := false
|
||||||
paramsCount := t.NumIn()
|
|
||||||
lastParam := paramsCount - 1
|
for i, a := range c.ArgumentList {
|
||||||
lastArg := argsCount - 1
|
var t reflect.Type
|
||||||
isVariadic := t.IsVariadic()
|
|
||||||
for i, value := range call.ArgumentList {
|
n := i
|
||||||
var paramType reflect.Type
|
if n >= nargs-1 && typ.IsVariadic() {
|
||||||
if isVariadic && i == lastArg && argsCount == paramsCount {
|
if n > nargs-1 {
|
||||||
// Variadic functions last parameter and parameter numbers match incoming args
|
n = nargs - 1
|
||||||
paramType = t.In(lastArg)
|
}
|
||||||
val := reflect.ValueOf(value.export())
|
|
||||||
callSlice = callSliceRequired(paramType, val)
|
t = typ.In(n).Elem()
|
||||||
if callSlice {
|
} else {
|
||||||
in[i] = callParamConvert(reflect.ValueOf(value.export()), paramType)
|
t = typ.In(n)
|
||||||
continue
|
}
|
||||||
|
|
||||||
|
// 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(c.ArgumentList) == nargs && i == nargs-1 {
|
||||||
|
var v reflect.Value
|
||||||
|
if err := catchPanic(func() { v = self.convertCallParameter(a, typ.In(n)) }); err == nil {
|
||||||
|
in[i] = v
|
||||||
|
callSlice = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if i >= lastParam {
|
in[i] = self.convertCallParameter(a, t)
|
||||||
if isVariadic {
|
|
||||||
paramType = t.In(lastParam).Elem()
|
|
||||||
} else {
|
|
||||||
paramType = t.In(lastParam)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
paramType = t.In(i)
|
|
||||||
}
|
|
||||||
in[i] = callParamConvert(reflect.ValueOf(value.export()), paramType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var out []reflect.Value
|
var out []reflect.Value
|
||||||
|
|
@ -445,33 +558,24 @@ func (self *_runtime) toValue(value interface{}) Value {
|
||||||
out = value.Call(in)
|
out = value.Call(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
l := len(out)
|
switch len(out) {
|
||||||
switch l {
|
|
||||||
case 0:
|
case 0:
|
||||||
return Value{}
|
return Value{}
|
||||||
case 1:
|
case 1:
|
||||||
return self.toValue(out[0].Interface())
|
return self.toValue(out[0].Interface())
|
||||||
}
|
default:
|
||||||
|
s := make([]interface{}, len(out))
|
||||||
|
for i, v := range out {
|
||||||
|
s[i] = self.toValue(v.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
// Return an array of the values to emulate multi value return.
|
return self.toValue(s)
|
||||||
// In the future this can be used along side destructuring assignment.
|
|
||||||
s := make([]interface{}, l)
|
|
||||||
for i, v := range out {
|
|
||||||
s[i] = self.toValue(v.Interface())
|
|
||||||
}
|
}
|
||||||
return self.toValue(s)
|
|
||||||
}))
|
}))
|
||||||
case reflect.Struct:
|
|
||||||
return toValue_object(self.newGoStructObject(value))
|
|
||||||
case reflect.Map:
|
|
||||||
return toValue_object(self.newGoMapObject(value))
|
|
||||||
case reflect.Slice:
|
|
||||||
return toValue_object(self.newGoSlice(value))
|
|
||||||
case reflect.Array:
|
|
||||||
return toValue_object(self.newGoArray(value))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return toValue(value)
|
return toValue(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user