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 }