github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goreflect.go (about)

     1  package goja
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"reflect"
     7  	"strings"
     8  
     9  	"github.com/nuvolaris/goja/parser"
    10  	"github.com/nuvolaris/goja/unistring"
    11  )
    12  
    13  // JsonEncodable allows custom JSON encoding by JSON.stringify()
    14  // Note that if the returned value itself also implements JsonEncodable, it won't have any effect.
    15  type JsonEncodable interface {
    16  	JsonEncodable() interface{}
    17  }
    18  
    19  // FieldNameMapper provides custom mapping between Go and JavaScript property names.
    20  type FieldNameMapper interface {
    21  	// FieldName returns a JavaScript name for the given struct field in the given type.
    22  	// If this method returns "" the field becomes hidden.
    23  	FieldName(t reflect.Type, f reflect.StructField) string
    24  
    25  	// MethodName returns a JavaScript name for the given method in the given type.
    26  	// If this method returns "" the method becomes hidden.
    27  	MethodName(t reflect.Type, m reflect.Method) string
    28  }
    29  
    30  type tagFieldNameMapper struct {
    31  	tagName      string
    32  	uncapMethods bool
    33  }
    34  
    35  func (tfm tagFieldNameMapper) FieldName(_ reflect.Type, f reflect.StructField) string {
    36  	tag := f.Tag.Get(tfm.tagName)
    37  	if idx := strings.IndexByte(tag, ','); idx != -1 {
    38  		tag = tag[:idx]
    39  	}
    40  	if parser.IsIdentifier(tag) {
    41  		return tag
    42  	}
    43  	return ""
    44  }
    45  
    46  func uncapitalize(s string) string {
    47  	return strings.ToLower(s[0:1]) + s[1:]
    48  }
    49  
    50  func (tfm tagFieldNameMapper) MethodName(_ reflect.Type, m reflect.Method) string {
    51  	if tfm.uncapMethods {
    52  		return uncapitalize(m.Name)
    53  	}
    54  	return m.Name
    55  }
    56  
    57  type uncapFieldNameMapper struct {
    58  }
    59  
    60  func (u uncapFieldNameMapper) FieldName(_ reflect.Type, f reflect.StructField) string {
    61  	return uncapitalize(f.Name)
    62  }
    63  
    64  func (u uncapFieldNameMapper) MethodName(_ reflect.Type, m reflect.Method) string {
    65  	return uncapitalize(m.Name)
    66  }
    67  
    68  type reflectFieldInfo struct {
    69  	Index     []int
    70  	Anonymous bool
    71  }
    72  
    73  type reflectFieldsInfo struct {
    74  	Fields map[string]reflectFieldInfo
    75  	Names  []string
    76  }
    77  
    78  type reflectMethodsInfo struct {
    79  	Methods map[string]int
    80  	Names   []string
    81  }
    82  
    83  type reflectValueWrapper interface {
    84  	esValue() Value
    85  	reflectValue() reflect.Value
    86  	setReflectValue(reflect.Value)
    87  }
    88  
    89  func isContainer(k reflect.Kind) bool {
    90  	switch k {
    91  	case reflect.Struct, reflect.Slice, reflect.Array:
    92  		return true
    93  	}
    94  	return false
    95  }
    96  
    97  func copyReflectValueWrapper(w reflectValueWrapper) {
    98  	v := w.reflectValue()
    99  	c := reflect.New(v.Type()).Elem()
   100  	c.Set(v)
   101  	w.setReflectValue(c)
   102  }
   103  
   104  type objectGoReflect struct {
   105  	baseObject
   106  	origValue, fieldsValue reflect.Value
   107  
   108  	fieldsInfo  *reflectFieldsInfo
   109  	methodsInfo *reflectMethodsInfo
   110  
   111  	methodsValue reflect.Value
   112  
   113  	valueCache map[string]reflectValueWrapper
   114  
   115  	toString, valueOf func() Value
   116  
   117  	toJson func() interface{}
   118  }
   119  
   120  func (o *objectGoReflect) init() {
   121  	o.baseObject.init()
   122  	switch o.fieldsValue.Kind() {
   123  	case reflect.Bool:
   124  		o.class = classBoolean
   125  		o.prototype = o.val.runtime.global.BooleanPrototype
   126  		o.toString = o._toStringBool
   127  		o.valueOf = o._valueOfBool
   128  	case reflect.String:
   129  		o.class = classString
   130  		o.prototype = o.val.runtime.global.StringPrototype
   131  		o.toString = o._toStringString
   132  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   133  		o.class = classNumber
   134  		o.prototype = o.val.runtime.global.NumberPrototype
   135  		o.valueOf = o._valueOfInt
   136  	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
   137  		o.class = classNumber
   138  		o.prototype = o.val.runtime.global.NumberPrototype
   139  		o.valueOf = o._valueOfUint
   140  	case reflect.Float32, reflect.Float64:
   141  		o.class = classNumber
   142  		o.prototype = o.val.runtime.global.NumberPrototype
   143  		o.valueOf = o._valueOfFloat
   144  	default:
   145  		o.class = classObject
   146  		o.prototype = o.val.runtime.global.ObjectPrototype
   147  	}
   148  
   149  	if o.fieldsValue.Kind() == reflect.Struct {
   150  		o.fieldsInfo = o.val.runtime.fieldsInfo(o.fieldsValue.Type())
   151  	}
   152  
   153  	var methodsType reflect.Type
   154  	// Always use pointer type for non-interface values to be able to access both methods defined on
   155  	// the literal type and on the pointer.
   156  	if o.fieldsValue.Kind() != reflect.Interface {
   157  		methodsType = reflect.PtrTo(o.fieldsValue.Type())
   158  	} else {
   159  		methodsType = o.fieldsValue.Type()
   160  	}
   161  
   162  	o.methodsInfo = o.val.runtime.methodsInfo(methodsType)
   163  
   164  	// Container values and values that have at least one method defined on the pointer type
   165  	// need to be addressable.
   166  	if !o.origValue.CanAddr() && (isContainer(o.origValue.Kind()) || len(o.methodsInfo.Names) > 0) {
   167  		value := reflect.New(o.origValue.Type()).Elem()
   168  		value.Set(o.origValue)
   169  		o.origValue = value
   170  		if value.Kind() != reflect.Ptr {
   171  			o.fieldsValue = value
   172  		}
   173  	}
   174  
   175  	o.extensible = true
   176  
   177  	switch o.origValue.Interface().(type) {
   178  	case fmt.Stringer:
   179  		o.toString = o._toStringStringer
   180  	case error:
   181  		o.toString = o._toStringError
   182  	}
   183  
   184  	if len(o.methodsInfo.Names) > 0 && o.fieldsValue.Kind() != reflect.Interface {
   185  		o.methodsValue = o.fieldsValue.Addr()
   186  	} else {
   187  		o.methodsValue = o.fieldsValue
   188  	}
   189  
   190  	if j, ok := o.origValue.Interface().(JsonEncodable); ok {
   191  		o.toJson = j.JsonEncodable
   192  	}
   193  }
   194  
   195  func (o *objectGoReflect) getStr(name unistring.String, receiver Value) Value {
   196  	if v := o._get(name.String()); v != nil {
   197  		return v
   198  	}
   199  	return o.baseObject.getStr(name, receiver)
   200  }
   201  
   202  func (o *objectGoReflect) _getField(jsName string) reflect.Value {
   203  	if o.fieldsInfo != nil {
   204  		if info, exists := o.fieldsInfo.Fields[jsName]; exists {
   205  			return o.fieldsValue.FieldByIndex(info.Index)
   206  		}
   207  	}
   208  
   209  	return reflect.Value{}
   210  }
   211  
   212  func (o *objectGoReflect) _getMethod(jsName string) reflect.Value {
   213  	if o.methodsInfo != nil {
   214  		if idx, exists := o.methodsInfo.Methods[jsName]; exists {
   215  			return o.methodsValue.Method(idx)
   216  		}
   217  	}
   218  
   219  	return reflect.Value{}
   220  }
   221  
   222  func (o *objectGoReflect) elemToValue(ev reflect.Value) (Value, reflectValueWrapper) {
   223  	if isContainer(ev.Kind()) {
   224  		ret := o.val.runtime.toValue(ev.Interface(), ev)
   225  		if obj, ok := ret.(*Object); ok {
   226  			if w, ok := obj.self.(reflectValueWrapper); ok {
   227  				return ret, w
   228  			}
   229  		}
   230  		return ret, nil
   231  	}
   232  
   233  	if ev.Kind() == reflect.Interface {
   234  		ev = ev.Elem()
   235  	}
   236  
   237  	if ev.Kind() == reflect.Invalid {
   238  		return _null, nil
   239  	}
   240  
   241  	return o.val.runtime.toValue(ev.Interface(), ev), nil
   242  }
   243  
   244  func (o *objectGoReflect) _getFieldValue(name string) Value {
   245  	if v := o.valueCache[name]; v != nil {
   246  		return v.esValue()
   247  	}
   248  	if v := o._getField(name); v.IsValid() {
   249  		res, w := o.elemToValue(v)
   250  		if w != nil {
   251  			if o.valueCache == nil {
   252  				o.valueCache = make(map[string]reflectValueWrapper)
   253  			}
   254  			o.valueCache[name] = w
   255  		}
   256  		return res
   257  	}
   258  	return nil
   259  }
   260  
   261  func (o *objectGoReflect) _get(name string) Value {
   262  	if o.fieldsValue.Kind() == reflect.Struct {
   263  		if ret := o._getFieldValue(name); ret != nil {
   264  			return ret
   265  		}
   266  	}
   267  
   268  	if v := o._getMethod(name); v.IsValid() {
   269  		return o.val.runtime.toValue(v.Interface(), v)
   270  	}
   271  
   272  	return nil
   273  }
   274  
   275  func (o *objectGoReflect) getOwnPropStr(name unistring.String) Value {
   276  	n := name.String()
   277  	if o.fieldsValue.Kind() == reflect.Struct {
   278  		if v := o._getFieldValue(n); v != nil {
   279  			return &valueProperty{
   280  				value:      v,
   281  				writable:   true,
   282  				enumerable: true,
   283  			}
   284  		}
   285  	}
   286  
   287  	if v := o._getMethod(n); v.IsValid() {
   288  		return &valueProperty{
   289  			value:      o.val.runtime.toValue(v.Interface(), v),
   290  			enumerable: true,
   291  		}
   292  	}
   293  
   294  	return o.baseObject.getOwnPropStr(name)
   295  }
   296  
   297  func (o *objectGoReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
   298  	has, ok := o._put(name.String(), val, throw)
   299  	if !has {
   300  		if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
   301  			o.val.runtime.typeErrorResult(throw, "Cannot assign to property %s of a host object", name)
   302  			return false
   303  		} else {
   304  			return res
   305  		}
   306  	}
   307  	return ok
   308  }
   309  
   310  func (o *objectGoReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
   311  	return o._setForeignStr(name, trueValIfPresent(o._has(name.String())), val, receiver, throw)
   312  }
   313  
   314  func (o *objectGoReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
   315  	return o._setForeignIdx(idx, nil, val, receiver, throw)
   316  }
   317  
   318  func (o *objectGoReflect) _put(name string, val Value, throw bool) (has, ok bool) {
   319  	if o.fieldsValue.Kind() == reflect.Struct {
   320  		if v := o._getField(name); v.IsValid() {
   321  			cached := o.valueCache[name]
   322  			if cached != nil {
   323  				copyReflectValueWrapper(cached)
   324  			}
   325  
   326  			err := o.val.runtime.toReflectValue(val, v, &objectExportCtx{})
   327  			if err != nil {
   328  				if cached != nil {
   329  					cached.setReflectValue(v)
   330  				}
   331  				o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err)
   332  				return true, false
   333  			}
   334  			if cached != nil {
   335  				delete(o.valueCache, name)
   336  			}
   337  			return true, true
   338  		}
   339  	}
   340  	return false, false
   341  }
   342  
   343  func (o *objectGoReflect) _putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value {
   344  	if _, ok := o._put(name.String(), value, false); ok {
   345  		return value
   346  	}
   347  	return o.baseObject._putProp(name, value, writable, enumerable, configurable)
   348  }
   349  
   350  func (r *Runtime) checkHostObjectPropertyDescr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   351  	if descr.Getter != nil || descr.Setter != nil {
   352  		r.typeErrorResult(throw, "Host objects do not support accessor properties")
   353  		return false
   354  	}
   355  	if descr.Writable == FLAG_FALSE {
   356  		r.typeErrorResult(throw, "Host object field %s cannot be made read-only", name)
   357  		return false
   358  	}
   359  	if descr.Configurable == FLAG_TRUE {
   360  		r.typeErrorResult(throw, "Host object field %s cannot be made configurable", name)
   361  		return false
   362  	}
   363  	return true
   364  }
   365  
   366  func (o *objectGoReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
   367  	if o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
   368  		n := name.String()
   369  		if has, ok := o._put(n, descr.Value, throw); !has {
   370  			o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a host object", n)
   371  			return false
   372  		} else {
   373  			return ok
   374  		}
   375  	}
   376  	return false
   377  }
   378  
   379  func (o *objectGoReflect) _has(name string) bool {
   380  	if o.fieldsValue.Kind() == reflect.Struct {
   381  		if v := o._getField(name); v.IsValid() {
   382  			return true
   383  		}
   384  	}
   385  	if v := o._getMethod(name); v.IsValid() {
   386  		return true
   387  	}
   388  	return false
   389  }
   390  
   391  func (o *objectGoReflect) hasOwnPropertyStr(name unistring.String) bool {
   392  	return o._has(name.String()) || o.baseObject.hasOwnPropertyStr(name)
   393  }
   394  
   395  func (o *objectGoReflect) _valueOfInt() Value {
   396  	return intToValue(o.fieldsValue.Int())
   397  }
   398  
   399  func (o *objectGoReflect) _valueOfUint() Value {
   400  	return intToValue(int64(o.fieldsValue.Uint()))
   401  }
   402  
   403  func (o *objectGoReflect) _valueOfBool() Value {
   404  	if o.fieldsValue.Bool() {
   405  		return valueTrue
   406  	} else {
   407  		return valueFalse
   408  	}
   409  }
   410  
   411  func (o *objectGoReflect) _valueOfFloat() Value {
   412  	return floatToValue(o.fieldsValue.Float())
   413  }
   414  
   415  func (o *objectGoReflect) _toStringStringer() Value {
   416  	return newStringValue(o.origValue.Interface().(fmt.Stringer).String())
   417  }
   418  
   419  func (o *objectGoReflect) _toStringString() Value {
   420  	return newStringValue(o.fieldsValue.String())
   421  }
   422  
   423  func (o *objectGoReflect) _toStringBool() Value {
   424  	if o.fieldsValue.Bool() {
   425  		return stringTrue
   426  	} else {
   427  		return stringFalse
   428  	}
   429  }
   430  
   431  func (o *objectGoReflect) _toStringError() Value {
   432  	return newStringValue(o.origValue.Interface().(error).Error())
   433  }
   434  
   435  func (o *objectGoReflect) deleteStr(name unistring.String, throw bool) bool {
   436  	n := name.String()
   437  	if o._has(n) {
   438  		o.val.runtime.typeErrorResult(throw, "Cannot delete property %s from a Go type", n)
   439  		return false
   440  	}
   441  	return o.baseObject.deleteStr(name, throw)
   442  }
   443  
   444  type goreflectPropIter struct {
   445  	o   *objectGoReflect
   446  	idx int
   447  }
   448  
   449  func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) {
   450  	names := i.o.fieldsInfo.Names
   451  	if i.idx < len(names) {
   452  		name := names[i.idx]
   453  		i.idx++
   454  		return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.nextField
   455  	}
   456  
   457  	i.idx = 0
   458  	return i.nextMethod()
   459  }
   460  
   461  func (i *goreflectPropIter) nextMethod() (propIterItem, iterNextFunc) {
   462  	names := i.o.methodsInfo.Names
   463  	if i.idx < len(names) {
   464  		name := names[i.idx]
   465  		i.idx++
   466  		return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.nextMethod
   467  	}
   468  
   469  	return propIterItem{}, nil
   470  }
   471  
   472  func (o *objectGoReflect) iterateStringKeys() iterNextFunc {
   473  	r := &goreflectPropIter{
   474  		o: o,
   475  	}
   476  	if o.fieldsInfo != nil {
   477  		return r.nextField
   478  	}
   479  
   480  	return r.nextMethod
   481  }
   482  
   483  func (o *objectGoReflect) stringKeys(_ bool, accum []Value) []Value {
   484  	// all own keys are enumerable
   485  	if o.fieldsInfo != nil {
   486  		for _, name := range o.fieldsInfo.Names {
   487  			accum = append(accum, newStringValue(name))
   488  		}
   489  	}
   490  
   491  	for _, name := range o.methodsInfo.Names {
   492  		accum = append(accum, newStringValue(name))
   493  	}
   494  
   495  	return accum
   496  }
   497  
   498  func (o *objectGoReflect) export(*objectExportCtx) interface{} {
   499  	return o.origValue.Interface()
   500  }
   501  
   502  func (o *objectGoReflect) exportType() reflect.Type {
   503  	return o.origValue.Type()
   504  }
   505  
   506  func (o *objectGoReflect) equal(other objectImpl) bool {
   507  	if other, ok := other.(*objectGoReflect); ok {
   508  		k1, k2 := o.fieldsValue.Kind(), other.fieldsValue.Kind()
   509  		if k1 == k2 {
   510  			if isContainer(k1) {
   511  				return o.fieldsValue == other.fieldsValue
   512  			}
   513  			return o.fieldsValue.Interface() == other.fieldsValue.Interface()
   514  		}
   515  	}
   516  	return false
   517  }
   518  
   519  func (o *objectGoReflect) reflectValue() reflect.Value {
   520  	return o.fieldsValue
   521  }
   522  
   523  func (o *objectGoReflect) setReflectValue(v reflect.Value) {
   524  	o.fieldsValue = v
   525  	o.origValue = v
   526  	o.methodsValue = v.Addr()
   527  }
   528  
   529  func (o *objectGoReflect) esValue() Value {
   530  	return o.val
   531  }
   532  
   533  func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectFieldsInfo) {
   534  	n := t.NumField()
   535  	for i := 0; i < n; i++ {
   536  		field := t.Field(i)
   537  		name := field.Name
   538  		if !ast.IsExported(name) {
   539  			continue
   540  		}
   541  		if r.fieldNameMapper != nil {
   542  			name = r.fieldNameMapper.FieldName(t, field)
   543  		}
   544  
   545  		if name != "" {
   546  			if inf, exists := info.Fields[name]; !exists {
   547  				info.Names = append(info.Names, name)
   548  			} else {
   549  				if len(inf.Index) <= len(index) {
   550  					continue
   551  				}
   552  			}
   553  		}
   554  
   555  		if name != "" || field.Anonymous {
   556  			idx := make([]int, len(index)+1)
   557  			copy(idx, index)
   558  			idx[len(idx)-1] = i
   559  
   560  			if name != "" {
   561  				info.Fields[name] = reflectFieldInfo{
   562  					Index:     idx,
   563  					Anonymous: field.Anonymous,
   564  				}
   565  			}
   566  			if field.Anonymous {
   567  				typ := field.Type
   568  				for typ.Kind() == reflect.Ptr {
   569  					typ = typ.Elem()
   570  				}
   571  				if typ.Kind() == reflect.Struct {
   572  					r.buildFieldInfo(typ, idx, info)
   573  				}
   574  			}
   575  		}
   576  	}
   577  }
   578  
   579  var emptyMethodsInfo = reflectMethodsInfo{}
   580  
   581  func (r *Runtime) buildMethodsInfo(t reflect.Type) (info *reflectMethodsInfo) {
   582  	n := t.NumMethod()
   583  	if n == 0 {
   584  		return &emptyMethodsInfo
   585  	}
   586  	info = new(reflectMethodsInfo)
   587  	info.Methods = make(map[string]int, n)
   588  	info.Names = make([]string, 0, n)
   589  	for i := 0; i < n; i++ {
   590  		method := t.Method(i)
   591  		name := method.Name
   592  		if !ast.IsExported(name) {
   593  			continue
   594  		}
   595  		if r.fieldNameMapper != nil {
   596  			name = r.fieldNameMapper.MethodName(t, method)
   597  			if name == "" {
   598  				continue
   599  			}
   600  		}
   601  
   602  		if _, exists := info.Methods[name]; !exists {
   603  			info.Names = append(info.Names, name)
   604  		}
   605  
   606  		info.Methods[name] = i
   607  	}
   608  	return
   609  }
   610  
   611  func (r *Runtime) buildFieldsInfo(t reflect.Type) (info *reflectFieldsInfo) {
   612  	info = new(reflectFieldsInfo)
   613  	n := t.NumField()
   614  	info.Fields = make(map[string]reflectFieldInfo, n)
   615  	info.Names = make([]string, 0, n)
   616  	r.buildFieldInfo(t, nil, info)
   617  	return
   618  }
   619  
   620  func (r *Runtime) fieldsInfo(t reflect.Type) (info *reflectFieldsInfo) {
   621  	var exists bool
   622  	if info, exists = r.fieldsInfoCache[t]; !exists {
   623  		info = r.buildFieldsInfo(t)
   624  		if r.fieldsInfoCache == nil {
   625  			r.fieldsInfoCache = make(map[reflect.Type]*reflectFieldsInfo)
   626  		}
   627  		r.fieldsInfoCache[t] = info
   628  	}
   629  
   630  	return
   631  }
   632  
   633  func (r *Runtime) methodsInfo(t reflect.Type) (info *reflectMethodsInfo) {
   634  	var exists bool
   635  	if info, exists = r.methodsInfoCache[t]; !exists {
   636  		info = r.buildMethodsInfo(t)
   637  		if r.methodsInfoCache == nil {
   638  			r.methodsInfoCache = make(map[reflect.Type]*reflectMethodsInfo)
   639  		}
   640  		r.methodsInfoCache[t] = info
   641  	}
   642  
   643  	return
   644  }
   645  
   646  // SetFieldNameMapper sets a custom field name mapper for Go types. It can be called at any time, however
   647  // the mapping for any given value is fixed at the point of creation.
   648  // Setting this to nil restores the default behaviour which is all exported fields and methods are mapped to their
   649  // original unchanged names.
   650  func (r *Runtime) SetFieldNameMapper(mapper FieldNameMapper) {
   651  	r.fieldNameMapper = mapper
   652  	r.fieldsInfoCache = nil
   653  	r.methodsInfoCache = nil
   654  }
   655  
   656  // TagFieldNameMapper returns a FieldNameMapper that uses the given tagName for struct fields and optionally
   657  // uncapitalises (making the first letter lower case) method names.
   658  // The common tag value syntax is supported (name[,options]), however options are ignored.
   659  // Setting name to anything other than a valid ECMAScript identifier makes the field hidden.
   660  func TagFieldNameMapper(tagName string, uncapMethods bool) FieldNameMapper {
   661  	return tagFieldNameMapper{
   662  		tagName:      tagName,
   663  		uncapMethods: uncapMethods,
   664  	}
   665  }
   666  
   667  // UncapFieldNameMapper returns a FieldNameMapper that uncapitalises struct field and method names
   668  // making the first letter lower case.
   669  func UncapFieldNameMapper() FieldNameMapper {
   670  	return uncapFieldNameMapper{}
   671  }