go.ketch.com/lib/goja@v0.0.1/object_goreflect.go (about) 1 package goja 2 3 import ( 4 "fmt" 5 "go/ast" 6 "reflect" 7 "strings" 8 9 "go.ketch.com/lib/goja/parser" 10 "go.ketch.com/lib/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 o.toString != nil || o.valueOf != nil { 185 o.baseObject._putProp("toString", o.val.runtime.newNativeFunc(o.toStringFunc, nil, "toString", nil, 0), true, false, true) 186 o.baseObject._putProp("valueOf", o.val.runtime.newNativeFunc(o.valueOfFunc, nil, "valueOf", nil, 0), true, false, true) 187 } 188 189 if len(o.methodsInfo.Names) > 0 && o.fieldsValue.Kind() != reflect.Interface { 190 o.methodsValue = o.fieldsValue.Addr() 191 } else { 192 o.methodsValue = o.fieldsValue 193 } 194 195 if j, ok := o.origValue.Interface().(JsonEncodable); ok { 196 o.toJson = j.JsonEncodable 197 } 198 } 199 200 func (o *objectGoReflect) toStringFunc(FunctionCall) Value { 201 return o.toPrimitiveString() 202 } 203 204 func (o *objectGoReflect) valueOfFunc(FunctionCall) Value { 205 return o.toPrimitiveNumber() 206 } 207 208 func (o *objectGoReflect) getStr(name unistring.String, receiver Value) Value { 209 if v := o._get(name.String()); v != nil { 210 return v 211 } 212 return o.baseObject.getStr(name, receiver) 213 } 214 215 func (o *objectGoReflect) _getField(jsName string) reflect.Value { 216 if o.fieldsInfo != nil { 217 if info, exists := o.fieldsInfo.Fields[jsName]; exists { 218 return o.fieldsValue.FieldByIndex(info.Index) 219 } 220 } 221 222 return reflect.Value{} 223 } 224 225 func (o *objectGoReflect) _getMethod(jsName string) reflect.Value { 226 if o.methodsInfo != nil { 227 if idx, exists := o.methodsInfo.Methods[jsName]; exists { 228 return o.methodsValue.Method(idx) 229 } 230 } 231 232 return reflect.Value{} 233 } 234 235 func (o *objectGoReflect) elemToValue(ev reflect.Value) (Value, reflectValueWrapper) { 236 if isContainer(ev.Kind()) { 237 if ev.Type() == reflectTypeArray { 238 a := o.val.runtime.newObjectGoSlice(ev.Addr().Interface().(*[]interface{})) 239 return a.val, a 240 } 241 ret := o.val.runtime.reflectValueToValue(ev) 242 if obj, ok := ret.(*Object); ok { 243 if w, ok := obj.self.(reflectValueWrapper); ok { 244 return ret, w 245 } 246 } 247 panic("reflectValueToValue() returned a value which is not a reflectValueWrapper") 248 } 249 250 for ev.Kind() == reflect.Interface { 251 ev = ev.Elem() 252 } 253 254 if ev.Kind() == reflect.Invalid { 255 return _null, nil 256 } 257 258 return o.val.runtime.ToValue(ev.Interface()), nil 259 } 260 261 func (o *objectGoReflect) _getFieldValue(name string) Value { 262 if v := o.valueCache[name]; v != nil { 263 return v.esValue() 264 } 265 if v := o._getField(name); v.IsValid() { 266 res, w := o.elemToValue(v) 267 if w != nil { 268 if o.valueCache == nil { 269 o.valueCache = make(map[string]reflectValueWrapper) 270 } 271 o.valueCache[name] = w 272 } 273 return res 274 } 275 return nil 276 } 277 278 func (o *objectGoReflect) _get(name string) Value { 279 if o.fieldsValue.Kind() == reflect.Struct { 280 if ret := o._getFieldValue(name); ret != nil { 281 return ret 282 } 283 } 284 285 if v := o._getMethod(name); v.IsValid() { 286 return o.val.runtime.reflectValueToValue(v) 287 } 288 289 return nil 290 } 291 292 func (o *objectGoReflect) getOwnPropStr(name unistring.String) Value { 293 n := name.String() 294 if o.fieldsValue.Kind() == reflect.Struct { 295 if v := o._getFieldValue(n); v != nil { 296 return &valueProperty{ 297 value: v, 298 writable: true, 299 enumerable: true, 300 } 301 } 302 } 303 304 if v := o._getMethod(n); v.IsValid() { 305 return &valueProperty{ 306 value: o.val.runtime.reflectValueToValue(v), 307 enumerable: true, 308 } 309 } 310 311 return nil 312 } 313 314 func (o *objectGoReflect) setOwnStr(name unistring.String, val Value, throw bool) bool { 315 has, ok := o._put(name.String(), val, throw) 316 if !has { 317 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { 318 o.val.runtime.typeErrorResult(throw, "Cannot assign to property %s of a host object", name) 319 return false 320 } else { 321 return res 322 } 323 } 324 return ok 325 } 326 327 func (o *objectGoReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { 328 return o._setForeignStr(name, trueValIfPresent(o._has(name.String())), val, receiver, throw) 329 } 330 331 func (o *objectGoReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) { 332 return o._setForeignIdx(idx, nil, val, receiver, throw) 333 } 334 335 func (o *objectGoReflect) _put(name string, val Value, throw bool) (has, ok bool) { 336 if o.fieldsValue.Kind() == reflect.Struct { 337 if v := o._getField(name); v.IsValid() { 338 cached := o.valueCache[name] 339 if cached != nil { 340 copyReflectValueWrapper(cached) 341 } 342 343 err := o.val.runtime.toReflectValue(val, v, &objectExportCtx{}) 344 if err != nil { 345 if cached != nil { 346 cached.setReflectValue(v) 347 } 348 o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err) 349 return true, false 350 } 351 if cached != nil { 352 delete(o.valueCache, name) 353 } 354 return true, true 355 } 356 } 357 return false, false 358 } 359 360 func (o *objectGoReflect) _putProp(name unistring.String, value Value, writable, enumerable, configurable bool) Value { 361 if _, ok := o._put(name.String(), value, false); ok { 362 return value 363 } 364 return o.baseObject._putProp(name, value, writable, enumerable, configurable) 365 } 366 367 func (r *Runtime) checkHostObjectPropertyDescr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 368 if descr.Getter != nil || descr.Setter != nil { 369 r.typeErrorResult(throw, "Host objects do not support accessor properties") 370 return false 371 } 372 if descr.Writable == FLAG_FALSE { 373 r.typeErrorResult(throw, "Host object field %s cannot be made read-only", name) 374 return false 375 } 376 if descr.Configurable == FLAG_TRUE { 377 r.typeErrorResult(throw, "Host object field %s cannot be made configurable", name) 378 return false 379 } 380 return true 381 } 382 383 func (o *objectGoReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 384 if o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) { 385 n := name.String() 386 if has, ok := o._put(n, descr.Value, throw); !has { 387 o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a host object", n) 388 return false 389 } else { 390 return ok 391 } 392 } 393 return false 394 } 395 396 func (o *objectGoReflect) _has(name string) bool { 397 if o.fieldsValue.Kind() == reflect.Struct { 398 if v := o._getField(name); v.IsValid() { 399 return true 400 } 401 } 402 if v := o._getMethod(name); v.IsValid() { 403 return true 404 } 405 return false 406 } 407 408 func (o *objectGoReflect) hasOwnPropertyStr(name unistring.String) bool { 409 return o._has(name.String()) 410 } 411 412 func (o *objectGoReflect) _valueOfInt() Value { 413 return intToValue(o.fieldsValue.Int()) 414 } 415 416 func (o *objectGoReflect) _valueOfUint() Value { 417 return intToValue(int64(o.fieldsValue.Uint())) 418 } 419 420 func (o *objectGoReflect) _valueOfBool() Value { 421 if o.fieldsValue.Bool() { 422 return valueTrue 423 } else { 424 return valueFalse 425 } 426 } 427 428 func (o *objectGoReflect) _valueOfFloat() Value { 429 return floatToValue(o.fieldsValue.Float()) 430 } 431 432 func (o *objectGoReflect) _toStringStringer() Value { 433 return newStringValue(o.origValue.Interface().(fmt.Stringer).String()) 434 } 435 436 func (o *objectGoReflect) _toStringString() Value { 437 return newStringValue(o.fieldsValue.String()) 438 } 439 440 func (o *objectGoReflect) _toStringBool() Value { 441 if o.fieldsValue.Bool() { 442 return stringTrue 443 } else { 444 return stringFalse 445 } 446 } 447 448 func (o *objectGoReflect) _toStringError() Value { 449 return newStringValue(o.origValue.Interface().(error).Error()) 450 } 451 452 func (o *objectGoReflect) toPrimitiveNumber() Value { 453 if o.valueOf != nil { 454 return o.valueOf() 455 } 456 if o.toString != nil { 457 return o.toString() 458 } 459 return o.baseObject.toPrimitiveNumber() 460 } 461 462 func (o *objectGoReflect) toPrimitiveString() Value { 463 if o.toString != nil { 464 return o.toString() 465 } 466 if o.valueOf != nil { 467 return o.valueOf().toString() 468 } 469 return o.baseObject.toPrimitiveString() 470 } 471 472 func (o *objectGoReflect) toPrimitive() Value { 473 if o.valueOf != nil { 474 return o.valueOf() 475 } 476 if o.toString != nil { 477 return o.toString() 478 } 479 480 return o.baseObject.toPrimitive() 481 } 482 483 func (o *objectGoReflect) deleteStr(name unistring.String, throw bool) bool { 484 n := name.String() 485 if o._has(n) { 486 o.val.runtime.typeErrorResult(throw, "Cannot delete property %s from a Go type", n) 487 return false 488 } 489 return o.baseObject.deleteStr(name, throw) 490 } 491 492 type goreflectPropIter struct { 493 o *objectGoReflect 494 idx int 495 } 496 497 func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) { 498 names := i.o.fieldsInfo.Names 499 if i.idx < len(names) { 500 name := names[i.idx] 501 i.idx++ 502 return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.nextField 503 } 504 505 i.idx = 0 506 return i.nextMethod() 507 } 508 509 func (i *goreflectPropIter) nextMethod() (propIterItem, iterNextFunc) { 510 names := i.o.methodsInfo.Names 511 if i.idx < len(names) { 512 name := names[i.idx] 513 i.idx++ 514 return propIterItem{name: newStringValue(name), enumerable: _ENUM_TRUE}, i.nextMethod 515 } 516 517 return propIterItem{}, nil 518 } 519 520 func (o *objectGoReflect) iterateStringKeys() iterNextFunc { 521 r := &goreflectPropIter{ 522 o: o, 523 } 524 if o.fieldsInfo != nil { 525 return r.nextField 526 } 527 528 return r.nextMethod 529 } 530 531 func (o *objectGoReflect) stringKeys(_ bool, accum []Value) []Value { 532 // all own keys are enumerable 533 if o.fieldsInfo != nil { 534 for _, name := range o.fieldsInfo.Names { 535 accum = append(accum, newStringValue(name)) 536 } 537 } 538 539 for _, name := range o.methodsInfo.Names { 540 accum = append(accum, newStringValue(name)) 541 } 542 543 return accum 544 } 545 546 func (o *objectGoReflect) export(*objectExportCtx) interface{} { 547 return o.origValue.Interface() 548 } 549 550 func (o *objectGoReflect) exportType() reflect.Type { 551 return o.origValue.Type() 552 } 553 554 func (o *objectGoReflect) equal(other objectImpl) bool { 555 if other, ok := other.(*objectGoReflect); ok { 556 k1, k2 := o.fieldsValue.Kind(), other.fieldsValue.Kind() 557 if k1 == k2 { 558 if isContainer(k1) { 559 return o.fieldsValue == other.fieldsValue 560 } 561 return o.fieldsValue.Interface() == other.fieldsValue.Interface() 562 } 563 } 564 return false 565 } 566 567 func (o *objectGoReflect) reflectValue() reflect.Value { 568 return o.fieldsValue 569 } 570 571 func (o *objectGoReflect) setReflectValue(v reflect.Value) { 572 o.fieldsValue = v 573 o.origValue = v 574 o.methodsValue = v.Addr() 575 } 576 577 func (o *objectGoReflect) esValue() Value { 578 return o.val 579 } 580 581 func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectFieldsInfo) { 582 n := t.NumField() 583 for i := 0; i < n; i++ { 584 field := t.Field(i) 585 name := field.Name 586 if !ast.IsExported(name) { 587 continue 588 } 589 if r.fieldNameMapper != nil { 590 name = r.fieldNameMapper.FieldName(t, field) 591 } 592 593 if name != "" { 594 if inf, exists := info.Fields[name]; !exists { 595 info.Names = append(info.Names, name) 596 } else { 597 if len(inf.Index) <= len(index) { 598 continue 599 } 600 } 601 } 602 603 if name != "" || field.Anonymous { 604 idx := make([]int, len(index)+1) 605 copy(idx, index) 606 idx[len(idx)-1] = i 607 608 if name != "" { 609 info.Fields[name] = reflectFieldInfo{ 610 Index: idx, 611 Anonymous: field.Anonymous, 612 } 613 } 614 if field.Anonymous { 615 typ := field.Type 616 for typ.Kind() == reflect.Ptr { 617 typ = typ.Elem() 618 } 619 if typ.Kind() == reflect.Struct { 620 r.buildFieldInfo(typ, idx, info) 621 } 622 } 623 } 624 } 625 } 626 627 var emptyMethodsInfo = reflectMethodsInfo{} 628 629 func (r *Runtime) buildMethodsInfo(t reflect.Type) (info *reflectMethodsInfo) { 630 n := t.NumMethod() 631 if n == 0 { 632 return &emptyMethodsInfo 633 } 634 info = new(reflectMethodsInfo) 635 info.Methods = make(map[string]int, n) 636 info.Names = make([]string, 0, n) 637 for i := 0; i < n; i++ { 638 method := t.Method(i) 639 name := method.Name 640 if !ast.IsExported(name) { 641 continue 642 } 643 if r.fieldNameMapper != nil { 644 name = r.fieldNameMapper.MethodName(t, method) 645 if name == "" { 646 continue 647 } 648 } 649 650 if _, exists := info.Methods[name]; !exists { 651 info.Names = append(info.Names, name) 652 } 653 654 info.Methods[name] = i 655 } 656 return 657 } 658 659 func (r *Runtime) buildFieldsInfo(t reflect.Type) (info *reflectFieldsInfo) { 660 info = new(reflectFieldsInfo) 661 n := t.NumField() 662 info.Fields = make(map[string]reflectFieldInfo, n) 663 info.Names = make([]string, 0, n) 664 r.buildFieldInfo(t, nil, info) 665 return 666 } 667 668 func (r *Runtime) fieldsInfo(t reflect.Type) (info *reflectFieldsInfo) { 669 var exists bool 670 if info, exists = r.fieldsInfoCache[t]; !exists { 671 info = r.buildFieldsInfo(t) 672 if r.fieldsInfoCache == nil { 673 r.fieldsInfoCache = make(map[reflect.Type]*reflectFieldsInfo) 674 } 675 r.fieldsInfoCache[t] = info 676 } 677 678 return 679 } 680 681 func (r *Runtime) methodsInfo(t reflect.Type) (info *reflectMethodsInfo) { 682 var exists bool 683 if info, exists = r.methodsInfoCache[t]; !exists { 684 info = r.buildMethodsInfo(t) 685 if r.methodsInfoCache == nil { 686 r.methodsInfoCache = make(map[reflect.Type]*reflectMethodsInfo) 687 } 688 r.methodsInfoCache[t] = info 689 } 690 691 return 692 } 693 694 // SetFieldNameMapper sets a custom field name mapper for Go types. It can be called at any time, however 695 // the mapping for any given value is fixed at the point of creation. 696 // Setting this to nil restores the default behaviour which is all exported fields and methods are mapped to their 697 // original unchanged names. 698 func (r *Runtime) SetFieldNameMapper(mapper FieldNameMapper) { 699 r.fieldNameMapper = mapper 700 r.fieldsInfoCache = nil 701 r.methodsInfoCache = nil 702 } 703 704 // TagFieldNameMapper returns a FieldNameMapper that uses the given tagName for struct fields and optionally 705 // uncapitalises (making the first letter lower case) method names. 706 // The common tag value syntax is supported (name[,options]), however options are ignored. 707 // Setting name to anything other than a valid ECMAScript identifier makes the field hidden. 708 func TagFieldNameMapper(tagName string, uncapMethods bool) FieldNameMapper { 709 return tagFieldNameMapper{ 710 tagName: tagName, 711 uncapMethods: uncapMethods, 712 } 713 } 714 715 // UncapFieldNameMapper returns a FieldNameMapper that uncapitalises struct field and method names 716 // making the first letter lower case. 717 func UncapFieldNameMapper() FieldNameMapper { 718 return uncapFieldNameMapper{} 719 }