1package goja 2 3import ( 4 "fmt" 5 "go/ast" 6 "reflect" 7) 8 9// JsonEncodable allows custom JSON encoding by JSON.stringify() 10// Note that if the returned value itself also implements JsonEncodable, it won't have any effect. 11type JsonEncodable interface { 12 JsonEncodable() interface{} 13} 14 15// FieldNameMapper provides custom mapping between Go and JavaScript property names. 16type FieldNameMapper interface { 17 // FieldName returns a JavaScript name for the given struct field in the given type. 18 // If this method returns "" the field becomes hidden. 19 FieldName(t reflect.Type, f reflect.StructField) string 20 21 // FieldName returns a JavaScript name for the given method in the given type. 22 // If this method returns "" the method becomes hidden. 23 MethodName(t reflect.Type, m reflect.Method) string 24} 25 26type reflectFieldInfo struct { 27 Index []int 28 Anonymous bool 29} 30 31type reflectTypeInfo struct { 32 Fields map[string]reflectFieldInfo 33 Methods map[string]int 34 FieldNames, MethodNames []string 35} 36 37type objectGoReflect struct { 38 baseObject 39 origValue, value reflect.Value 40 41 valueTypeInfo, origValueTypeInfo *reflectTypeInfo 42 43 toJson func() interface{} 44} 45 46func (o *objectGoReflect) init() { 47 o.baseObject.init() 48 switch o.value.Kind() { 49 case reflect.Bool: 50 o.class = classBoolean 51 o.prototype = o.val.runtime.global.BooleanPrototype 52 case reflect.String: 53 o.class = classString 54 o.prototype = o.val.runtime.global.StringPrototype 55 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 56 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 57 reflect.Float32, reflect.Float64: 58 59 o.class = classNumber 60 o.prototype = o.val.runtime.global.NumberPrototype 61 default: 62 o.class = classObject 63 o.prototype = o.val.runtime.global.ObjectPrototype 64 } 65 66 o.baseObject._putProp("toString", o.val.runtime.newNativeFunc(o.toStringFunc, nil, "toString", nil, 0), true, false, true) 67 o.baseObject._putProp("valueOf", o.val.runtime.newNativeFunc(o.valueOfFunc, nil, "valueOf", nil, 0), true, false, true) 68 69 o.valueTypeInfo = o.val.runtime.typeInfo(o.value.Type()) 70 o.origValueTypeInfo = o.val.runtime.typeInfo(o.origValue.Type()) 71 72 if j, ok := o.origValue.Interface().(JsonEncodable); ok { 73 o.toJson = j.JsonEncodable 74 } 75} 76 77func (o *objectGoReflect) toStringFunc(call FunctionCall) Value { 78 return o.toPrimitiveString() 79} 80 81func (o *objectGoReflect) valueOfFunc(call FunctionCall) Value { 82 return o.toPrimitive() 83} 84 85func (o *objectGoReflect) get(n Value) Value { 86 return o.getStr(n.String()) 87} 88 89func (o *objectGoReflect) _getField(jsName string) reflect.Value { 90 if info, exists := o.valueTypeInfo.Fields[jsName]; exists { 91 v := o.value.FieldByIndex(info.Index) 92 if info.Anonymous { 93 v = v.Addr() 94 } 95 return v 96 } 97 98 return reflect.Value{} 99} 100 101func (o *objectGoReflect) _getMethod(jsName string) reflect.Value { 102 if idx, exists := o.origValueTypeInfo.Methods[jsName]; exists { 103 return o.origValue.Method(idx) 104 } 105 106 return reflect.Value{} 107} 108 109func (o *objectGoReflect) _get(name string) Value { 110 if o.value.Kind() == reflect.Struct { 111 if v := o._getField(name); v.IsValid() { 112 return o.val.runtime.ToValue(v.Interface()) 113 } 114 } 115 116 if v := o._getMethod(name); v.IsValid() { 117 return o.val.runtime.ToValue(v.Interface()) 118 } 119 120 return nil 121} 122 123func (o *objectGoReflect) getStr(name string) Value { 124 if v := o._get(name); v != nil { 125 return v 126 } 127 return o.baseObject._getStr(name) 128} 129 130func (o *objectGoReflect) getProp(n Value) Value { 131 name := n.String() 132 if p := o.getOwnProp(name); p != nil { 133 return p 134 } 135 return o.baseObject.getOwnProp(name) 136} 137 138func (o *objectGoReflect) getPropStr(name string) Value { 139 if v := o.getOwnProp(name); v != nil { 140 return v 141 } 142 return o.baseObject.getPropStr(name) 143} 144 145func (o *objectGoReflect) getOwnProp(name string) Value { 146 if o.value.Kind() == reflect.Struct { 147 if v := o._getField(name); v.IsValid() { 148 return &valueProperty{ 149 value: o.val.runtime.ToValue(v.Interface()), 150 writable: true, 151 enumerable: true, 152 } 153 } 154 } 155 156 if v := o._getMethod(name); v.IsValid() { 157 return &valueProperty{ 158 value: o.val.runtime.ToValue(v.Interface()), 159 enumerable: true, 160 } 161 } 162 163 return nil 164} 165 166func (o *objectGoReflect) put(n Value, val Value, throw bool) { 167 o.putStr(n.String(), val, throw) 168} 169 170func (o *objectGoReflect) putStr(name string, val Value, throw bool) { 171 if !o._put(name, val, throw) { 172 o.val.runtime.typeErrorResult(throw, "Cannot assign to property %s of a host object", name) 173 } 174} 175 176func (o *objectGoReflect) _put(name string, val Value, throw bool) bool { 177 if o.value.Kind() == reflect.Struct { 178 if v := o._getField(name); v.IsValid() { 179 vv, err := o.val.runtime.toReflectValue(val, v.Type()) 180 if err != nil { 181 o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err) 182 return false 183 } 184 v.Set(vv) 185 return true 186 } 187 } 188 return false 189} 190 191func (o *objectGoReflect) _putProp(name string, value Value, writable, enumerable, configurable bool) Value { 192 if o._put(name, value, false) { 193 return value 194 } 195 return o.baseObject._putProp(name, value, writable, enumerable, configurable) 196} 197 198func (r *Runtime) checkHostObjectPropertyDescr(name string, descr propertyDescr, throw bool) bool { 199 if descr.Getter != nil || descr.Setter != nil { 200 r.typeErrorResult(throw, "Host objects do not support accessor properties") 201 return false 202 } 203 if descr.Writable == FLAG_FALSE { 204 r.typeErrorResult(throw, "Host object field %s cannot be made read-only", name) 205 return false 206 } 207 if descr.Configurable == FLAG_TRUE { 208 r.typeErrorResult(throw, "Host object field %s cannot be made configurable", name) 209 return false 210 } 211 return true 212} 213 214func (o *objectGoReflect) defineOwnProperty(n Value, descr propertyDescr, throw bool) bool { 215 name := n.String() 216 if ast.IsExported(name) { 217 if o.value.Kind() == reflect.Struct { 218 if v := o._getField(name); v.IsValid() { 219 if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) { 220 return false 221 } 222 val := descr.Value 223 if val == nil { 224 val = _undefined 225 } 226 vv, err := o.val.runtime.toReflectValue(val, v.Type()) 227 if err != nil { 228 o.val.runtime.typeErrorResult(throw, "Go struct conversion error: %v", err) 229 return false 230 } 231 v.Set(vv) 232 return true 233 } 234 } 235 } 236 237 return o.baseObject.defineOwnProperty(n, descr, throw) 238} 239 240func (o *objectGoReflect) _has(name string) bool { 241 if !ast.IsExported(name) { 242 return false 243 } 244 if o.value.Kind() == reflect.Struct { 245 if v := o._getField(name); v.IsValid() { 246 return true 247 } 248 } 249 if v := o._getMethod(name); v.IsValid() { 250 return true 251 } 252 return false 253} 254 255func (o *objectGoReflect) hasProperty(n Value) bool { 256 name := n.String() 257 if o._has(name) { 258 return true 259 } 260 return o.baseObject.hasProperty(n) 261} 262 263func (o *objectGoReflect) hasPropertyStr(name string) bool { 264 if o._has(name) { 265 return true 266 } 267 return o.baseObject.hasPropertyStr(name) 268} 269 270func (o *objectGoReflect) hasOwnProperty(n Value) bool { 271 return o._has(n.String()) 272} 273 274func (o *objectGoReflect) hasOwnPropertyStr(name string) bool { 275 return o._has(name) 276} 277 278func (o *objectGoReflect) _toNumber() Value { 279 switch o.value.Kind() { 280 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 281 return intToValue(o.value.Int()) 282 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 283 return intToValue(int64(o.value.Uint())) 284 case reflect.Bool: 285 if o.value.Bool() { 286 return intToValue(1) 287 } else { 288 return intToValue(0) 289 } 290 case reflect.Float32, reflect.Float64: 291 return floatToValue(o.value.Float()) 292 } 293 return nil 294} 295 296func (o *objectGoReflect) _toString() Value { 297 switch o.value.Kind() { 298 case reflect.String: 299 return newStringValue(o.value.String()) 300 case reflect.Bool: 301 if o.value.Interface().(bool) { 302 return stringTrue 303 } else { 304 return stringFalse 305 } 306 } 307 switch v := o.value.Interface().(type) { 308 case fmt.Stringer: 309 return newStringValue(v.String()) 310 } 311 return stringObjectObject 312} 313 314func (o *objectGoReflect) toPrimitiveNumber() Value { 315 if v := o._toNumber(); v != nil { 316 return v 317 } 318 return o._toString() 319} 320 321func (o *objectGoReflect) toPrimitiveString() Value { 322 if v := o._toNumber(); v != nil { 323 return v.ToString() 324 } 325 return o._toString() 326} 327 328func (o *objectGoReflect) toPrimitive() Value { 329 if o.prototype == o.val.runtime.global.NumberPrototype { 330 return o.toPrimitiveNumber() 331 } 332 return o.toPrimitiveString() 333} 334 335func (o *objectGoReflect) deleteStr(name string, throw bool) bool { 336 if o._has(name) { 337 o.val.runtime.typeErrorResult(throw, "Cannot delete property %s from a Go type") 338 return false 339 } 340 return o.baseObject.deleteStr(name, throw) 341} 342 343func (o *objectGoReflect) delete(name Value, throw bool) bool { 344 return o.deleteStr(name.String(), throw) 345} 346 347type goreflectPropIter struct { 348 o *objectGoReflect 349 idx int 350 recursive bool 351} 352 353func (i *goreflectPropIter) nextField() (propIterItem, iterNextFunc) { 354 names := i.o.valueTypeInfo.FieldNames 355 if i.idx < len(names) { 356 name := names[i.idx] 357 i.idx++ 358 return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.nextField 359 } 360 361 i.idx = 0 362 return i.nextMethod() 363} 364 365func (i *goreflectPropIter) nextMethod() (propIterItem, iterNextFunc) { 366 names := i.o.origValueTypeInfo.MethodNames 367 if i.idx < len(names) { 368 name := names[i.idx] 369 i.idx++ 370 return propIterItem{name: name, enumerable: _ENUM_TRUE}, i.nextMethod 371 } 372 373 if i.recursive { 374 return i.o.baseObject._enumerate(true)() 375 } 376 377 return propIterItem{}, nil 378} 379 380func (o *objectGoReflect) _enumerate(recursive bool) iterNextFunc { 381 r := &goreflectPropIter{ 382 o: o, 383 recursive: recursive, 384 } 385 if o.value.Kind() == reflect.Struct { 386 return r.nextField 387 } 388 return r.nextMethod 389} 390 391func (o *objectGoReflect) enumerate(all, recursive bool) iterNextFunc { 392 return (&propFilterIter{ 393 wrapped: o._enumerate(recursive), 394 all: all, 395 seen: make(map[string]bool), 396 }).next 397} 398 399func (o *objectGoReflect) export() interface{} { 400 return o.origValue.Interface() 401} 402 403func (o *objectGoReflect) exportType() reflect.Type { 404 return o.origValue.Type() 405} 406 407func (o *objectGoReflect) equal(other objectImpl) bool { 408 if other, ok := other.(*objectGoReflect); ok { 409 return o.value.Interface() == other.value.Interface() 410 } 411 return false 412} 413 414func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectTypeInfo) { 415 n := t.NumField() 416 for i := 0; i < n; i++ { 417 field := t.Field(i) 418 name := field.Name 419 if !ast.IsExported(name) { 420 continue 421 } 422 if r.fieldNameMapper != nil { 423 name = r.fieldNameMapper.FieldName(t, field) 424 if name == "" { 425 continue 426 } 427 } 428 429 if inf, exists := info.Fields[name]; !exists { 430 info.FieldNames = append(info.FieldNames, name) 431 } else { 432 if len(inf.Index) <= len(index) { 433 continue 434 } 435 } 436 437 idx := make([]int, len(index)+1) 438 copy(idx, index) 439 idx[len(idx)-1] = i 440 441 info.Fields[name] = reflectFieldInfo{ 442 Index: idx, 443 Anonymous: field.Anonymous, 444 } 445 if field.Anonymous { 446 typ := field.Type 447 for typ.Kind() == reflect.Ptr { 448 typ = typ.Elem() 449 } 450 if typ.Kind() == reflect.Struct { 451 r.buildFieldInfo(typ, idx, info) 452 } 453 } 454 } 455} 456 457func (r *Runtime) buildTypeInfo(t reflect.Type) (info *reflectTypeInfo) { 458 info = new(reflectTypeInfo) 459 if t.Kind() == reflect.Struct { 460 info.Fields = make(map[string]reflectFieldInfo) 461 n := t.NumField() 462 info.FieldNames = make([]string, 0, n) 463 r.buildFieldInfo(t, nil, info) 464 } 465 466 info.Methods = make(map[string]int) 467 n := t.NumMethod() 468 info.MethodNames = make([]string, 0, n) 469 for i := 0; i < n; i++ { 470 method := t.Method(i) 471 name := method.Name 472 if !ast.IsExported(name) { 473 continue 474 } 475 if r.fieldNameMapper != nil { 476 name = r.fieldNameMapper.MethodName(t, method) 477 if name == "" { 478 continue 479 } 480 } 481 482 if _, exists := info.Methods[name]; !exists { 483 info.MethodNames = append(info.MethodNames, name) 484 } 485 486 info.Methods[name] = i 487 } 488 return 489} 490 491func (r *Runtime) typeInfo(t reflect.Type) (info *reflectTypeInfo) { 492 var exists bool 493 if info, exists = r.typeInfoCache[t]; !exists { 494 info = r.buildTypeInfo(t) 495 if r.typeInfoCache == nil { 496 r.typeInfoCache = make(map[reflect.Type]*reflectTypeInfo) 497 } 498 r.typeInfoCache[t] = info 499 } 500 501 return 502} 503 504// Sets a custom field name mapper for Go types. It can be called at any time, however 505// the mapping for any given value is fixed at the point of creation. 506// Setting this to nil restores the default behaviour which is all exported fields and methods are mapped to their 507// original unchanged names. 508func (r *Runtime) SetFieldNameMapper(mapper FieldNameMapper) { 509 r.fieldNameMapper = mapper 510 r.typeInfoCache = nil 511} 512