Browse Source

Allow accessing methods defined on the pointer type when literal value is used. Closes #402.

Dmitry Panov 3 years ago
parent
commit
d13e99b5b2
6 changed files with 245 additions and 117 deletions
  1. 20 20
      object_goarray_reflect.go
  2. 15 15
      object_gomap_reflect.go
  3. 118 59
      object_goreflect.go
  4. 67 0
      object_goreflect_test.go
  5. 13 13
      object_goslice_reflect.go
  6. 12 10
      runtime.go

+ 20 - 20
object_goarray_reflect.go

@@ -70,18 +70,18 @@ func (o *objectGoArrayReflect) init() {
 }
 
 func (o *objectGoArrayReflect) updateLen() {
-	o.lengthProp.value = intToValue(int64(o.value.Len()))
+	o.lengthProp.value = intToValue(int64(o.fieldsValue.Len()))
 }
 
 func (o *objectGoArrayReflect) _hasIdx(idx valueInt) bool {
-	if idx := int64(idx); idx >= 0 && idx < int64(o.value.Len()) {
+	if idx := int64(idx); idx >= 0 && idx < int64(o.fieldsValue.Len()) {
 		return true
 	}
 	return false
 }
 
 func (o *objectGoArrayReflect) _hasStr(name unistring.String) bool {
-	if idx := strToIdx64(name); idx >= 0 && idx < int64(o.value.Len()) {
+	if idx := strToIdx64(name); idx >= 0 && idx < int64(o.fieldsValue.Len()) {
 		return true
 	}
 	return false
@@ -92,7 +92,7 @@ func (o *objectGoArrayReflect) _getIdx(idx int) Value {
 		return v.esValue()
 	}
 
-	v := o.value.Index(idx)
+	v := o.fieldsValue.Index(idx)
 
 	res, w := o.elemToValue(v)
 	if w != nil {
@@ -103,7 +103,7 @@ func (o *objectGoArrayReflect) _getIdx(idx int) Value {
 }
 
 func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value {
-	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.value.Len() {
+	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
 		return o._getIdx(idx)
 	}
 	return o.objectGoReflect.getStr(idx.string(), receiver)
@@ -111,7 +111,7 @@ func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value {
 
 func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Value {
 	var ownProp Value
-	if idx := strToGoIdx(name); idx >= 0 && idx < o.value.Len() {
+	if idx := strToGoIdx(name); idx >= 0 && idx < o.fieldsValue.Len() {
 		ownProp = o._getIdx(idx)
 	} else if name == "length" {
 		ownProp = &o.lengthProp
@@ -123,7 +123,7 @@ func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Val
 
 func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value {
 	if idx := strToGoIdx(name); idx >= 0 {
-		if idx < o.value.Len() {
+		if idx < o.fieldsValue.Len() {
 			return &valueProperty{
 				value:      o._getIdx(idx),
 				writable:   true,
@@ -139,7 +139,7 @@ func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value {
 }
 
 func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value {
-	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.value.Len() {
+	if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
 		return &valueProperty{
 			value:      o._getIdx(idx),
 			writable:   true,
@@ -155,7 +155,7 @@ func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool {
 		copyReflectValueWrapper(cached)
 	}
 
-	rv := o.value.Index(idx)
+	rv := o.fieldsValue.Index(idx)
 	err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{})
 	if err != nil {
 		if cached != nil {
@@ -172,7 +172,7 @@ func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool {
 
 func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool {
 	if i := toIntStrict(int64(idx)); i >= 0 {
-		if i >= o.value.Len() {
+		if i >= o.fieldsValue.Len() {
 			if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok {
 				return res
 			}
@@ -191,7 +191,7 @@ func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bo
 
 func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
 	if idx := strToGoIdx(name); idx >= 0 {
-		if idx >= o.value.Len() {
+		if idx >= o.fieldsValue.Len() {
 			if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok {
 				return res
 			}
@@ -261,13 +261,13 @@ func (o *objectGoArrayReflect) toPrimitive() Value {
 }
 
 func (o *objectGoArrayReflect) _deleteIdx(idx int) {
-	if idx < o.value.Len() {
+	if idx < o.fieldsValue.Len() {
 		if cv := o.valueCache.get(idx); cv != nil {
 			copyReflectValueWrapper(cv)
 			o.valueCache[idx] = nil
 		}
 
-		o.value.Index(idx).Set(reflect.Zero(o.value.Type().Elem()))
+		o.fieldsValue.Index(idx).Set(reflect.Zero(o.fieldsValue.Type().Elem()))
 	}
 }
 
@@ -294,7 +294,7 @@ type goArrayReflectPropIter struct {
 }
 
 func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) {
-	if i.idx < i.limit && i.idx < i.o.value.Len() {
+	if i.idx < i.limit && i.idx < i.o.fieldsValue.Len() {
 		name := strconv.Itoa(i.idx)
 		i.idx++
 		return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
@@ -304,7 +304,7 @@ func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) {
 }
 
 func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value {
-	for i := 0; i < o.value.Len(); i++ {
+	for i := 0; i < o.fieldsValue.Len(); i++ {
 		accum = append(accum, asciiString(strconv.Itoa(i)))
 	}
 
@@ -314,12 +314,12 @@ func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value {
 func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc {
 	return (&goArrayReflectPropIter{
 		o:     o,
-		limit: o.value.Len(),
+		limit: o.fieldsValue.Len(),
 	}).next
 }
 
 func (o *objectGoArrayReflect) sortLen() int {
-	return o.value.Len()
+	return o.fieldsValue.Len()
 }
 
 func (o *objectGoArrayReflect) sortGet(i int) Value {
@@ -327,9 +327,9 @@ func (o *objectGoArrayReflect) sortGet(i int) Value {
 }
 
 func (o *objectGoArrayReflect) swap(i int, j int) {
-	vi := o.value.Index(i)
-	vj := o.value.Index(j)
-	tmp := reflect.New(o.value.Type().Elem()).Elem()
+	vi := o.fieldsValue.Index(i)
+	vj := o.fieldsValue.Index(j)
+	tmp := reflect.New(o.fieldsValue.Type().Elem()).Elem()
 	tmp.Set(vi)
 	vi.Set(vj)
 	vj.Set(tmp)

+ 15 - 15
object_gomap_reflect.go

@@ -14,8 +14,8 @@ type objectGoMapReflect struct {
 
 func (o *objectGoMapReflect) init() {
 	o.objectGoReflect.init()
-	o.keyType = o.value.Type().Key()
-	o.valueType = o.value.Type().Elem()
+	o.keyType = o.fieldsValue.Type().Key()
+	o.valueType = o.fieldsValue.Type().Elem()
 }
 
 func (o *objectGoMapReflect) toKey(n Value, throw bool) reflect.Value {
@@ -40,7 +40,7 @@ func (o *objectGoMapReflect) _get(n Value) Value {
 	if !key.IsValid() {
 		return nil
 	}
-	if v := o.value.MapIndex(key); v.IsValid() {
+	if v := o.fieldsValue.MapIndex(key); v.IsValid() {
 		return o.val.runtime.ToValue(v.Interface())
 	}
 
@@ -52,7 +52,7 @@ func (o *objectGoMapReflect) _getStr(name string) Value {
 	if !key.IsValid() {
 		return nil
 	}
-	if v := o.value.MapIndex(key); v.IsValid() {
+	if v := o.fieldsValue.MapIndex(key); v.IsValid() {
 		return o.val.runtime.ToValue(v.Interface())
 	}
 
@@ -108,12 +108,12 @@ func (o *objectGoMapReflect) toValue(val Value, throw bool) (reflect.Value, bool
 
 func (o *objectGoMapReflect) _put(key reflect.Value, val Value, throw bool) bool {
 	if key.IsValid() {
-		if o.extensible || o.value.MapIndex(key).IsValid() {
+		if o.extensible || o.fieldsValue.MapIndex(key).IsValid() {
 			v, ok := o.toValue(val, throw)
 			if !ok {
 				return false
 			}
-			o.value.SetMapIndex(key, v)
+			o.fieldsValue.SetMapIndex(key, v)
 		} else {
 			o.val.runtime.typeErrorResult(throw, "Cannot set property %s, object is not extensible", key.String())
 			return false
@@ -126,7 +126,7 @@ func (o *objectGoMapReflect) _put(key reflect.Value, val Value, throw bool) bool
 func (o *objectGoMapReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
 	n := name.String()
 	key := o.strToKey(n, false)
-	if !key.IsValid() || !o.value.MapIndex(key).IsValid() {
+	if !key.IsValid() || !o.fieldsValue.MapIndex(key).IsValid() {
 		if proto := o.prototype; proto != nil {
 			// we know it's foreign because prototype loops are not allowed
 			if res, ok := proto.self.setForeignStr(name, val, o.val, throw); ok {
@@ -150,7 +150,7 @@ func (o *objectGoMapReflect) setOwnStr(name unistring.String, val Value, throw b
 
 func (o *objectGoMapReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool {
 	key := o.toKey(idx, false)
-	if !key.IsValid() || !o.value.MapIndex(key).IsValid() {
+	if !key.IsValid() || !o.fieldsValue.MapIndex(key).IsValid() {
 		if proto := o.prototype; proto != nil {
 			// we know it's foreign because prototype loops are not allowed
 			if res, ok := proto.self.setForeignIdx(idx, val, o.val, throw); ok {
@@ -198,7 +198,7 @@ func (o *objectGoMapReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDe
 
 func (o *objectGoMapReflect) hasOwnPropertyStr(name unistring.String) bool {
 	key := o.strToKey(name.String(), false)
-	if key.IsValid() && o.value.MapIndex(key).IsValid() {
+	if key.IsValid() && o.fieldsValue.MapIndex(key).IsValid() {
 		return true
 	}
 	return false
@@ -206,7 +206,7 @@ func (o *objectGoMapReflect) hasOwnPropertyStr(name unistring.String) bool {
 
 func (o *objectGoMapReflect) hasOwnPropertyIdx(idx valueInt) bool {
 	key := o.toKey(idx, false)
-	if key.IsValid() && o.value.MapIndex(key).IsValid() {
+	if key.IsValid() && o.fieldsValue.MapIndex(key).IsValid() {
 		return true
 	}
 	return false
@@ -217,7 +217,7 @@ func (o *objectGoMapReflect) deleteStr(name unistring.String, throw bool) bool {
 	if !key.IsValid() {
 		return false
 	}
-	o.value.SetMapIndex(key, reflect.Value{})
+	o.fieldsValue.SetMapIndex(key, reflect.Value{})
 	return true
 }
 
@@ -226,7 +226,7 @@ func (o *objectGoMapReflect) deleteIdx(idx valueInt, throw bool) bool {
 	if !key.IsValid() {
 		return false
 	}
-	o.value.SetMapIndex(key, reflect.Value{})
+	o.fieldsValue.SetMapIndex(key, reflect.Value{})
 	return true
 }
 
@@ -239,7 +239,7 @@ type gomapReflectPropIter struct {
 func (i *gomapReflectPropIter) next() (propIterItem, iterNextFunc) {
 	for i.idx < len(i.keys) {
 		key := i.keys[i.idx]
-		v := i.o.value.MapIndex(key)
+		v := i.o.fieldsValue.MapIndex(key)
 		i.idx++
 		if v.IsValid() {
 			return propIterItem{name: newStringValue(key.String()), enumerable: _ENUM_TRUE}, i.next
@@ -252,13 +252,13 @@ func (i *gomapReflectPropIter) next() (propIterItem, iterNextFunc) {
 func (o *objectGoMapReflect) iterateStringKeys() iterNextFunc {
 	return (&gomapReflectPropIter{
 		o:    o,
-		keys: o.value.MapKeys(),
+		keys: o.fieldsValue.MapKeys(),
 	}).next
 }
 
 func (o *objectGoMapReflect) stringKeys(_ bool, accum []Value) []Value {
 	// all own keys are enumerable
-	for _, key := range o.value.MapKeys() {
+	for _, key := range o.fieldsValue.MapKeys() {
 		accum = append(accum, newStringValue(key.String()))
 	}
 

+ 118 - 59
object_goreflect.go

@@ -70,10 +70,14 @@ type reflectFieldInfo struct {
 	Anonymous bool
 }
 
-type reflectTypeInfo struct {
-	Fields                  map[string]reflectFieldInfo
-	Methods                 map[string]int
-	FieldNames, MethodNames []string
+type reflectFieldsInfo struct {
+	Fields map[string]reflectFieldInfo
+	Names  []string
+}
+
+type reflectMethodsInfo struct {
+	Methods map[string]int
+	Names   []string
 }
 
 type reflectValueWrapper interface {
@@ -99,9 +103,12 @@ func copyReflectValueWrapper(w reflectValueWrapper) {
 
 type objectGoReflect struct {
 	baseObject
-	origValue, value reflect.Value
+	origValue, fieldsValue reflect.Value
 
-	valueTypeInfo, origValueTypeInfo *reflectTypeInfo
+	fieldsInfo  *reflectFieldsInfo
+	methodsInfo *reflectMethodsInfo
+
+	methodsValue reflect.Value
 
 	valueCache map[string]reflectValueWrapper
 
@@ -112,7 +119,7 @@ type objectGoReflect struct {
 
 func (o *objectGoReflect) init() {
 	o.baseObject.init()
-	switch o.value.Kind() {
+	switch o.fieldsValue.Kind() {
 	case reflect.Bool:
 		o.class = classBoolean
 		o.prototype = o.val.runtime.global.BooleanPrototype
@@ -137,13 +144,34 @@ func (o *objectGoReflect) init() {
 	default:
 		o.class = classObject
 		o.prototype = o.val.runtime.global.ObjectPrototype
-		if !o.value.CanAddr() {
-			value := reflect.New(o.value.Type()).Elem()
-			value.Set(o.value)
-			o.origValue = value
-			o.value = value
+	}
+
+	if o.fieldsValue.Kind() == reflect.Struct {
+		o.fieldsInfo = o.val.runtime.fieldsInfo(o.fieldsValue.Type())
+	}
+
+	var methodsType reflect.Type
+	// Always use pointer type for non-interface values to be able to access both methods defined on
+	// the literal type and on the pointer.
+	if o.fieldsValue.Kind() != reflect.Interface {
+		methodsType = reflect.PointerTo(o.fieldsValue.Type())
+	} else {
+		methodsType = o.fieldsValue.Type()
+	}
+
+	o.methodsInfo = o.val.runtime.methodsInfo(methodsType)
+
+	// Container values and values that have at least one method defined on the pointer type
+	// need to be addressable.
+	if !o.origValue.CanAddr() && (isContainer(o.origValue.Kind()) || len(o.methodsInfo.Names) > 0) {
+		value := reflect.New(o.origValue.Type()).Elem()
+		value.Set(o.origValue)
+		o.origValue = value
+		if value.Kind() != reflect.Ptr {
+			o.fieldsValue = value
 		}
 	}
+
 	o.extensible = true
 
 	switch o.origValue.Interface().(type) {
@@ -158,8 +186,11 @@ func (o *objectGoReflect) init() {
 		o.baseObject._putProp("valueOf", o.val.runtime.newNativeFunc(o.valueOfFunc, nil, "valueOf", nil, 0), true, false, true)
 	}
 
-	o.valueTypeInfo = o.val.runtime.typeInfo(o.value.Type())
-	o.origValueTypeInfo = o.val.runtime.typeInfo(o.origValue.Type())
+	if len(o.methodsInfo.Names) > 0 && o.fieldsValue.Kind() != reflect.Interface {
+		o.methodsValue = o.fieldsValue.Addr()
+	} else {
+		o.methodsValue = o.fieldsValue
+	}
 
 	if j, ok := o.origValue.Interface().(JsonEncodable); ok {
 		o.toJson = j.JsonEncodable
@@ -182,16 +213,20 @@ func (o *objectGoReflect) getStr(name unistring.String, receiver Value) Value {
 }
 
 func (o *objectGoReflect) _getField(jsName string) reflect.Value {
-	if info, exists := o.valueTypeInfo.Fields[jsName]; exists {
-		return o.value.FieldByIndex(info.Index)
+	if o.fieldsInfo != nil {
+		if info, exists := o.fieldsInfo.Fields[jsName]; exists {
+			return o.fieldsValue.FieldByIndex(info.Index)
+		}
 	}
 
 	return reflect.Value{}
 }
 
 func (o *objectGoReflect) _getMethod(jsName string) reflect.Value {
-	if idx, exists := o.origValueTypeInfo.Methods[jsName]; exists {
-		return o.origValue.Method(idx)
+	if o.methodsInfo != nil {
+		if idx, exists := o.methodsInfo.Methods[jsName]; exists {
+			return o.methodsValue.Method(idx)
+		}
 	}
 
 	return reflect.Value{}
@@ -241,7 +276,7 @@ func (o *objectGoReflect) _getFieldValue(name string) Value {
 }
 
 func (o *objectGoReflect) _get(name string) Value {
-	if o.value.Kind() == reflect.Struct {
+	if o.fieldsValue.Kind() == reflect.Struct {
 		if ret := o._getFieldValue(name); ret != nil {
 			return ret
 		}
@@ -256,7 +291,7 @@ func (o *objectGoReflect) _get(name string) Value {
 
 func (o *objectGoReflect) getOwnPropStr(name unistring.String) Value {
 	n := name.String()
-	if o.value.Kind() == reflect.Struct {
+	if o.fieldsValue.Kind() == reflect.Struct {
 		if v := o._getFieldValue(n); v != nil {
 			return &valueProperty{
 				value:      v,
@@ -298,7 +333,7 @@ func (o *objectGoReflect) setForeignIdx(idx valueInt, val, receiver Value, throw
 }
 
 func (o *objectGoReflect) _put(name string, val Value, throw bool) (has, ok bool) {
-	if o.value.Kind() == reflect.Struct {
+	if o.fieldsValue.Kind() == reflect.Struct {
 		if v := o._getField(name); v.IsValid() {
 			cached := o.valueCache[name]
 			if cached != nil {
@@ -359,7 +394,7 @@ func (o *objectGoReflect) defineOwnPropertyStr(name unistring.String, descr Prop
 }
 
 func (o *objectGoReflect) _has(name string) bool {
-	if o.value.Kind() == reflect.Struct {
+	if o.fieldsValue.Kind() == reflect.Struct {
 		if v := o._getField(name); v.IsValid() {
 			return true
 		}
@@ -375,15 +410,15 @@ func (o *objectGoReflect) hasOwnPropertyStr(name unistring.String) bool {
 }
 
 func (o *objectGoReflect) _valueOfInt() Value {
-	return intToValue(o.value.Int())
+	return intToValue(o.fieldsValue.Int())
 }
 
 func (o *objectGoReflect) _valueOfUint() Value {
-	return intToValue(int64(o.value.Uint()))
+	return intToValue(int64(o.fieldsValue.Uint()))
 }
 
 func (o *objectGoReflect) _valueOfBool() Value {
-	if o.value.Bool() {
+	if o.fieldsValue.Bool() {
 		return valueTrue
 	} else {
 		return valueFalse
@@ -391,7 +426,7 @@ func (o *objectGoReflect) _valueOfBool() Value {
 }
 
 func (o *objectGoReflect) _valueOfFloat() Value {
-	return floatToValue(o.value.Float())
+	return floatToValue(o.fieldsValue.Float())
 }
 
 func (o *objectGoReflect) _toStringStringer() Value {
@@ -399,11 +434,11 @@ func (o *objectGoReflect) _toStringStringer() Value {
 }
 
 func (o *objectGoReflect) _toStringString() Value {
-	return newStringValue(o.value.String())
+	return newStringValue(o.fieldsValue.String())
 }
 
 func (o *objectGoReflect) _toStringBool() Value {
-	if o.value.Bool() {
+	if o.fieldsValue.Bool() {
 		return stringTrue
 	} else {
 		return stringFalse
@@ -460,7 +495,7 @@ type goreflectPropIter struct {
 }
 
 func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) {
-	names := i.o.valueTypeInfo.FieldNames
+	names := i.o.fieldsInfo.Names
 	if i.idx < len(names) {
 		name := names[i.idx]
 		i.idx++
@@ -472,7 +507,7 @@ func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) {
 }
 
 func (i *goreflectPropIter) nextMethod() (propIterItem, iterNextFunc) {
-	names := i.o.origValueTypeInfo.MethodNames
+	names := i.o.methodsInfo.Names
 	if i.idx < len(names) {
 		name := names[i.idx]
 		i.idx++
@@ -486,7 +521,7 @@ func (o *objectGoReflect) iterateStringKeys() iterNextFunc {
 	r := &goreflectPropIter{
 		o: o,
 	}
-	if o.value.Kind() == reflect.Struct {
+	if o.fieldsInfo != nil {
 		return r.nextField
 	}
 
@@ -495,11 +530,13 @@ func (o *objectGoReflect) iterateStringKeys() iterNextFunc {
 
 func (o *objectGoReflect) stringKeys(_ bool, accum []Value) []Value {
 	// all own keys are enumerable
-	for _, name := range o.valueTypeInfo.FieldNames {
-		accum = append(accum, newStringValue(name))
+	if o.fieldsInfo != nil {
+		for _, name := range o.fieldsInfo.Names {
+			accum = append(accum, newStringValue(name))
+		}
 	}
 
-	for _, name := range o.valueTypeInfo.MethodNames {
+	for _, name := range o.methodsInfo.Names {
 		accum = append(accum, newStringValue(name))
 	}
 
@@ -516,31 +553,32 @@ func (o *objectGoReflect) exportType() reflect.Type {
 
 func (o *objectGoReflect) equal(other objectImpl) bool {
 	if other, ok := other.(*objectGoReflect); ok {
-		k1, k2 := o.value.Kind(), other.value.Kind()
+		k1, k2 := o.fieldsValue.Kind(), other.fieldsValue.Kind()
 		if k1 == k2 {
 			if isContainer(k1) {
-				return o.value == other.value
+				return o.fieldsValue == other.fieldsValue
 			}
-			return o.value.Interface() == other.value.Interface()
+			return o.fieldsValue.Interface() == other.fieldsValue.Interface()
 		}
 	}
 	return false
 }
 
 func (o *objectGoReflect) reflectValue() reflect.Value {
-	return o.value
+	return o.fieldsValue
 }
 
 func (o *objectGoReflect) setReflectValue(v reflect.Value) {
-	o.value = v
+	o.fieldsValue = v
 	o.origValue = v
+	o.methodsValue = v.Addr()
 }
 
 func (o *objectGoReflect) esValue() Value {
 	return o.val
 }
 
-func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectTypeInfo) {
+func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectFieldsInfo) {
 	n := t.NumField()
 	for i := 0; i < n; i++ {
 		field := t.Field(i)
@@ -554,7 +592,7 @@ func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectTypeI
 
 		if name != "" {
 			if inf, exists := info.Fields[name]; !exists {
-				info.FieldNames = append(info.FieldNames, name)
+				info.Names = append(info.Names, name)
 			} else {
 				if len(inf.Index) <= len(index) {
 					continue
@@ -586,18 +624,16 @@ func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectTypeI
 	}
 }
 
-func (r *Runtime) buildTypeInfo(t reflect.Type) (info *reflectTypeInfo) {
-	info = new(reflectTypeInfo)
-	if t.Kind() == reflect.Struct {
-		info.Fields = make(map[string]reflectFieldInfo)
-		n := t.NumField()
-		info.FieldNames = make([]string, 0, n)
-		r.buildFieldInfo(t, nil, info)
-	}
+var emptyMethodsInfo = reflectMethodsInfo{}
 
-	info.Methods = make(map[string]int)
+func (r *Runtime) buildMethodsInfo(t reflect.Type) (info *reflectMethodsInfo) {
 	n := t.NumMethod()
-	info.MethodNames = make([]string, 0, n)
+	if n == 0 {
+		return &emptyMethodsInfo
+	}
+	info = new(reflectMethodsInfo)
+	info.Methods = make(map[string]int, n)
+	info.Names = make([]string, 0, n)
 	for i := 0; i < n; i++ {
 		method := t.Method(i)
 		name := method.Name
@@ -612,7 +648,7 @@ func (r *Runtime) buildTypeInfo(t reflect.Type) (info *reflectTypeInfo) {
 		}
 
 		if _, exists := info.Methods[name]; !exists {
-			info.MethodNames = append(info.MethodNames, name)
+			info.Names = append(info.Names, name)
 		}
 
 		info.Methods[name] = i
@@ -620,14 +656,36 @@ func (r *Runtime) buildTypeInfo(t reflect.Type) (info *reflectTypeInfo) {
 	return
 }
 
-func (r *Runtime) typeInfo(t reflect.Type) (info *reflectTypeInfo) {
+func (r *Runtime) buildFieldsInfo(t reflect.Type) (info *reflectFieldsInfo) {
+	info = new(reflectFieldsInfo)
+	n := t.NumField()
+	info.Fields = make(map[string]reflectFieldInfo, n)
+	info.Names = make([]string, 0, n)
+	r.buildFieldInfo(t, nil, info)
+	return
+}
+
+func (r *Runtime) fieldsInfo(t reflect.Type) (info *reflectFieldsInfo) {
+	var exists bool
+	if info, exists = r.fieldsInfoCache[t]; !exists {
+		info = r.buildFieldsInfo(t)
+		if r.fieldsInfoCache == nil {
+			r.fieldsInfoCache = make(map[reflect.Type]*reflectFieldsInfo)
+		}
+		r.fieldsInfoCache[t] = info
+	}
+
+	return
+}
+
+func (r *Runtime) methodsInfo(t reflect.Type) (info *reflectMethodsInfo) {
 	var exists bool
-	if info, exists = r.typeInfoCache[t]; !exists {
-		info = r.buildTypeInfo(t)
-		if r.typeInfoCache == nil {
-			r.typeInfoCache = make(map[reflect.Type]*reflectTypeInfo)
+	if info, exists = r.methodsInfoCache[t]; !exists {
+		info = r.buildMethodsInfo(t)
+		if r.methodsInfoCache == nil {
+			r.methodsInfoCache = make(map[reflect.Type]*reflectMethodsInfo)
 		}
-		r.typeInfoCache[t] = info
+		r.methodsInfoCache[t] = info
 	}
 
 	return
@@ -639,7 +697,8 @@ func (r *Runtime) typeInfo(t reflect.Type) (info *reflectTypeInfo) {
 // original unchanged names.
 func (r *Runtime) SetFieldNameMapper(mapper FieldNameMapper) {
 	r.fieldNameMapper = mapper
-	r.typeInfoCache = nil
+	r.fieldsInfoCache = nil
+	r.methodsInfoCache = nil
 }
 
 // TagFieldNameMapper returns a FieldNameMapper that uses the given tagName for struct fields and optionally

+ 67 - 0
object_goreflect_test.go

@@ -273,6 +273,54 @@ func TestGoReflectMethodPtr(t *testing.T) {
 	}
 }
 
+func (b *testBoolS) Method() bool {
+	return bool(*b)
+}
+
+func TestGoReflectPtrMethodOnNonPtrValue(t *testing.T) {
+	var o testGoReflectMethod_O
+	o.Get()
+	vm := New()
+	vm.Set("o", o)
+	_, err := vm.RunString(`o.Get()`)
+	if err != nil {
+		t.Fatal(err)
+	}
+	_, err = vm.RunString(`o.Method()`)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var b testBoolS
+	vm.Set("b", b)
+	_, err = vm.RunString(`b.Method()`)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestGoReflectStructField(t *testing.T) {
+	type S struct {
+		F testGoReflectMethod_O
+		B testBoolS
+	}
+	var s S
+	vm := New()
+	vm.Set("s", &s)
+
+	const SCRIPT = `
+	s.F.Set("Test");
+	assert.sameValue(s.F.Method(""), "Test", "1");
+
+	s.B = true;
+	assert.sameValue(s.B.Method(), true, "2");
+
+	assert.sameValue(s.B.toString(), "B", "3");
+	`
+
+	vm.testScriptWithTestLib(SCRIPT, _undefined, t)
+}
+
 func TestGoReflectProp(t *testing.T) {
 	const SCRIPT = `
 	var d1 = Object.getOwnPropertyDescriptor(o, "Get");
@@ -1245,6 +1293,25 @@ func TestGoReflectCopyOnWrite(t *testing.T) {
 	}
 }
 
+func TestReflectSetReflectValue(t *testing.T) {
+	o := []testGoReflectMethod_O{{}}
+	vm := New()
+	vm.Set("o", o)
+	_, err := vm.RunString(`
+		const t = o[0];
+		t.Set("a");
+		o[0] = {};
+		o[0].Set("b");
+		if (t.Get() !== "a") {
+			throw new Error();
+		}
+	`)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
 func TestReflectOverwriteReflectMap(t *testing.T) {
 	vm := New()
 	type S struct {

+ 13 - 13
object_goslice_reflect.go

@@ -19,46 +19,46 @@ func (o *objectGoSliceReflect) init() {
 }
 
 func (o *objectGoSliceReflect) _putIdx(idx int, v Value, throw bool) bool {
-	if idx >= o.value.Len() {
+	if idx >= o.fieldsValue.Len() {
 		o.grow(idx + 1)
 	}
 	return o.objectGoArrayReflect._putIdx(idx, v, throw)
 }
 
 func (o *objectGoSliceReflect) grow(size int) {
-	oldcap := o.value.Cap()
+	oldcap := o.fieldsValue.Cap()
 	if oldcap < size {
-		n := reflect.MakeSlice(o.value.Type(), size, growCap(size, o.value.Len(), oldcap))
-		reflect.Copy(n, o.value)
-		o.value.Set(n)
+		n := reflect.MakeSlice(o.fieldsValue.Type(), size, growCap(size, o.fieldsValue.Len(), oldcap))
+		reflect.Copy(n, o.fieldsValue)
+		o.fieldsValue.Set(n)
 		l := len(o.valueCache)
 		if l > size {
 			l = size
 		}
 		for i, w := range o.valueCache[:l] {
 			if w != nil {
-				w.setReflectValue(o.value.Index(i))
+				w.setReflectValue(o.fieldsValue.Index(i))
 			}
 		}
 	} else {
-		tail := o.value.Slice(o.value.Len(), size)
-		zero := reflect.Zero(o.value.Type().Elem())
+		tail := o.fieldsValue.Slice(o.fieldsValue.Len(), size)
+		zero := reflect.Zero(o.fieldsValue.Type().Elem())
 		for i := 0; i < tail.Len(); i++ {
 			tail.Index(i).Set(zero)
 		}
-		o.value.SetLen(size)
+		o.fieldsValue.SetLen(size)
 	}
 	o.updateLen()
 }
 
 func (o *objectGoSliceReflect) shrink(size int) {
 	o.valueCache.shrink(size)
-	tail := o.value.Slice(size, o.value.Len())
-	zero := reflect.Zero(o.value.Type().Elem())
+	tail := o.fieldsValue.Slice(size, o.fieldsValue.Len())
+	zero := reflect.Zero(o.fieldsValue.Type().Elem())
 	for i := 0; i < tail.Len(); i++ {
 		tail.Index(i).Set(zero)
 	}
-	o.value.SetLen(size)
+	o.fieldsValue.SetLen(size)
 	o.updateLen()
 }
 
@@ -67,7 +67,7 @@ func (o *objectGoSliceReflect) putLength(v uint32, throw bool) bool {
 		panic(rangeError("Integer value overflows 32-bit int"))
 	}
 	newLen := int(v)
-	curLen := o.value.Len()
+	curLen := o.fieldsValue.Len()
 	if newLen > curLen {
 		o.grow(newLen)
 	} else if newLen < curLen {

+ 12 - 10
runtime.go

@@ -175,7 +175,9 @@ type Runtime struct {
 
 	symbolRegistry map[unistring.String]*Symbol
 
-	typeInfoCache   map[reflect.Type]*reflectTypeInfo
+	fieldsInfoCache  map[reflect.Type]*reflectFieldsInfo
+	methodsInfoCache map[reflect.Type]*reflectMethodsInfo
+
 	fieldNameMapper FieldNameMapper
 
 	vm    *vm
@@ -1826,7 +1828,7 @@ func (r *Runtime) ToValue(i interface{}) Value {
 func (r *Runtime) reflectValueToValue(origValue reflect.Value) Value {
 	value := origValue
 	for value.Kind() == reflect.Ptr {
-		value = reflect.Indirect(value)
+		value = value.Elem()
 	}
 
 	if !value.IsValid() {
@@ -1848,8 +1850,8 @@ func (r *Runtime) reflectValueToValue(origValue reflect.Value) Value {
 							val:        obj,
 							extensible: true,
 						},
-						origValue: origValue,
-						value:     value,
+						origValue:   origValue,
+						fieldsValue: value,
 					},
 				}
 				m.init()
@@ -1864,8 +1866,8 @@ func (r *Runtime) reflectValueToValue(origValue reflect.Value) Value {
 				baseObject: baseObject{
 					val: obj,
 				},
-				origValue: origValue,
-				value:     value,
+				origValue:   origValue,
+				fieldsValue: value,
 			},
 		}
 		a.init()
@@ -1879,8 +1881,8 @@ func (r *Runtime) reflectValueToValue(origValue reflect.Value) Value {
 					baseObject: baseObject{
 						val: obj,
 					},
-					origValue: origValue,
-					value:     value,
+					origValue:   origValue,
+					fieldsValue: value,
 				},
 			},
 		}
@@ -1897,8 +1899,8 @@ func (r *Runtime) reflectValueToValue(origValue reflect.Value) Value {
 		baseObject: baseObject{
 			val: obj,
 		},
-		origValue: origValue,
-		value:     value,
+		origValue:   origValue,
+		fieldsValue: value,
 	}
 	obj.self = o
 	o.init()