瀏覽代碼

Prefer the exported value when doing type conversions to preserve wrapped values. Only use reflect.Convert() if the types' kinds match. Closes #329.

Dmitry Panov 4 年之前
父節點
當前提交
6e5fa0950d
共有 2 個文件被更改,包括 92 次插入64 次删除
  1. 22 0
      object_goreflect_test.go
  2. 70 64
      runtime.go

+ 22 - 0
object_goreflect_test.go

@@ -3,9 +3,11 @@ package goja
 import (
 	"errors"
 	"fmt"
+	"math"
 	"reflect"
 	"strings"
 	"testing"
+	"time"
 )
 
 func TestGoReflectGet(t *testing.T) {
@@ -1169,3 +1171,23 @@ func TestGoReflectUnicodeProps(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+func TestGoReflectPreserveType(t *testing.T) {
+	vm := New()
+	var expect = time.Duration(math.MaxInt64)
+	vm.Set(`make`, func() time.Duration {
+		return expect
+	})
+	vm.Set(`handle`, func(d time.Duration) {
+		if d.String() != expect.String() {
+			t.Fatal(`expect`, expect, `, but get`, d)
+		}
+	})
+	_, e := vm.RunString(`
+	var d=make()
+	handle(d)
+	`)
+	if e != nil {
+		t.Fatal(e)
+	}
+}

+ 70 - 64
runtime.go

@@ -1788,7 +1788,76 @@ func (r *Runtime) wrapReflectFunc(value reflect.Value) func(FunctionCall) Value
 
 func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCtx) error {
 	typ := dst.Type()
-	switch typ.Kind() {
+
+	if typ == typeValue {
+		dst.Set(reflect.ValueOf(v))
+		return nil
+	}
+
+	if typ == typeObject {
+		if obj, ok := v.(*Object); ok {
+			dst.Set(reflect.ValueOf(obj))
+			return nil
+		}
+	}
+
+	if typ == typeCallable {
+		if fn, ok := AssertFunction(v); ok {
+			dst.Set(reflect.ValueOf(fn))
+			return nil
+		}
+	}
+
+	et := v.ExportType()
+	if et == nil || et == reflectTypeNil {
+		dst.Set(reflect.Zero(typ))
+		return nil
+	}
+
+	kind := typ.Kind()
+	for i := 0; ; i++ {
+		if et.AssignableTo(typ) {
+			ev := reflect.ValueOf(exportValue(v, ctx))
+			for ; i > 0; i-- {
+				ev = ev.Elem()
+			}
+			dst.Set(ev)
+			return nil
+		}
+		expKind := et.Kind()
+		if expKind == kind && et.ConvertibleTo(typ) {
+			ev := reflect.ValueOf(exportValue(v, ctx))
+			for ; i > 0; i-- {
+				ev = ev.Elem()
+			}
+			dst.Set(ev.Convert(typ))
+			return nil
+		}
+		if expKind == reflect.Ptr {
+			et = et.Elem()
+		} else {
+			break
+		}
+	}
+
+	if typ == typeTime {
+		if obj, ok := v.(*Object); ok {
+			if d, ok := obj.self.(*dateObject); ok {
+				dst.Set(reflect.ValueOf(d.time()))
+				return nil
+			}
+		}
+		if et.Kind() == reflect.String {
+			tme, ok := dateParse(v.String())
+			if !ok {
+				return fmt.Errorf("could not convert string %v to %v", v, typ)
+			}
+			dst.Set(reflect.ValueOf(tme))
+			return nil
+		}
+	}
+
+	switch kind {
 	case reflect.String:
 		dst.Set(reflect.ValueOf(v.String()).Convert(typ))
 		return nil
@@ -1831,69 +1900,6 @@ func (r *Runtime) toReflectValue(v Value, dst reflect.Value, ctx *objectExportCt
 	case reflect.Float32:
 		dst.Set(reflect.ValueOf(toFloat32(v)).Convert(typ))
 		return nil
-	}
-
-	if typ == typeCallable {
-		if fn, ok := AssertFunction(v); ok {
-			dst.Set(reflect.ValueOf(fn))
-			return nil
-		}
-	}
-
-	if typ == typeValue {
-		dst.Set(reflect.ValueOf(v))
-		return nil
-	}
-
-	if typ == typeObject {
-		if obj, ok := v.(*Object); ok {
-			dst.Set(reflect.ValueOf(obj))
-			return nil
-		}
-	}
-
-	{
-		et := v.ExportType()
-		if et == nil || et == reflectTypeNil {
-			dst.Set(reflect.Zero(typ))
-			return nil
-		}
-
-		for i := 0; ; i++ {
-			if et.ConvertibleTo(typ) {
-				ev := reflect.ValueOf(exportValue(v, ctx))
-				for ; i > 0; i-- {
-					ev = ev.Elem()
-				}
-				dst.Set(ev.Convert(typ))
-				return nil
-			}
-			if et.Kind() == reflect.Ptr {
-				et = et.Elem()
-			} else {
-				break
-			}
-		}
-
-		if typ == typeTime {
-			if obj, ok := v.(*Object); ok {
-				if d, ok := obj.self.(*dateObject); ok {
-					dst.Set(reflect.ValueOf(d.time()))
-					return nil
-				}
-			}
-			if et.Kind() == reflect.String {
-				tme, ok := dateParse(v.String())
-				if !ok {
-					return fmt.Errorf("could not convert string %v to %v", v, typ)
-				}
-				dst.Set(reflect.ValueOf(tme))
-				return nil
-			}
-		}
-	}
-
-	switch typ.Kind() {
 	case reflect.Slice:
 		if o, ok := v.(*Object); ok {
 			if o.self.className() == classArray {