123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- package goja
- import (
- "fmt"
- "reflect"
- "github.com/dop251/goja/unistring"
- )
- type objectGoMapReflect struct {
- objectGoReflect
- keyType, valueType reflect.Type
- }
- func (o *objectGoMapReflect) init() {
- o.objectGoReflect.init()
- o.keyType = o.fieldsValue.Type().Key()
- o.valueType = o.fieldsValue.Type().Elem()
- }
- func (o *objectGoMapReflect) toKey(n Value, throw bool) reflect.Value {
- key := reflect.New(o.keyType).Elem()
- err := o.val.runtime.toReflectValue(n, key, &objectExportCtx{})
- if err != nil {
- o.val.runtime.typeErrorResult(throw, "map key conversion error: %v", err)
- return reflect.Value{}
- }
- return key
- }
- func (o *objectGoMapReflect) strToKey(name string, throw bool) reflect.Value {
- if o.keyType.Kind() == reflect.String {
- return reflect.ValueOf(name).Convert(o.keyType)
- }
- return o.toKey(newStringValue(name), throw)
- }
- func (o *objectGoMapReflect) _getKey(key reflect.Value) Value {
- if !key.IsValid() {
- return nil
- }
- if v := o.fieldsValue.MapIndex(key); v.IsValid() {
- rv := v
- if rv.Kind() == reflect.Interface {
- rv = rv.Elem()
- }
- return o.val.runtime.toValue(v.Interface(), rv)
- }
- return nil
- }
- func (o *objectGoMapReflect) _get(n Value) Value {
- return o._getKey(o.toKey(n, false))
- }
- func (o *objectGoMapReflect) _getStr(name string) Value {
- return o._getKey(o.strToKey(name, false))
- }
- func (o *objectGoMapReflect) getStr(name unistring.String, receiver Value) Value {
- if v := o._getStr(name.String()); v != nil {
- return v
- }
- return o.objectGoReflect.getStr(name, receiver)
- }
- func (o *objectGoMapReflect) getIdx(idx valueInt, receiver Value) Value {
- if v := o._get(idx); v != nil {
- return v
- }
- return o.objectGoReflect.getIdx(idx, receiver)
- }
- func (o *objectGoMapReflect) getOwnPropStr(name unistring.String) Value {
- if v := o._getStr(name.String()); v != nil {
- return &valueProperty{
- value: v,
- writable: true,
- enumerable: true,
- }
- }
- return o.objectGoReflect.getOwnPropStr(name)
- }
- func (o *objectGoMapReflect) getOwnPropIdx(idx valueInt) Value {
- if v := o._get(idx); v != nil {
- return &valueProperty{
- value: v,
- writable: true,
- enumerable: true,
- }
- }
- return o.objectGoReflect.getOwnPropStr(idx.string())
- }
- func (o *objectGoMapReflect) toValue(val Value, throw bool) (reflect.Value, bool) {
- v := reflect.New(o.valueType).Elem()
- err := o.val.runtime.toReflectValue(val, v, &objectExportCtx{})
- if err != nil {
- o.val.runtime.typeErrorResult(throw, "map value conversion error: %v", err)
- return reflect.Value{}, false
- }
- return v, true
- }
- func (o *objectGoMapReflect) _put(key reflect.Value, val Value, throw bool) bool {
- if key.IsValid() {
- if o.extensible || o.fieldsValue.MapIndex(key).IsValid() {
- v, ok := o.toValue(val, throw)
- if !ok {
- return false
- }
- o.fieldsValue.SetMapIndex(key, v)
- } else {
- o.val.runtime.typeErrorResult(throw, "Cannot set property %v, object is not extensible", key)
- return false
- }
- return true
- }
- return false
- }
- func (o *objectGoMapReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
- n := name.String()
- key := o.strToKey(n, false)
- 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 {
- return res
- }
- }
- // new property
- if !o.extensible {
- o.val.runtime.typeErrorResult(throw, "Cannot add property %s, object is not extensible", n)
- return false
- } else {
- if throw && !key.IsValid() {
- o.strToKey(n, true)
- return false
- }
- }
- }
- o._put(key, val, throw)
- return true
- }
- func (o *objectGoMapReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool {
- key := o.toKey(idx, false)
- 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 {
- return res
- }
- }
- // new property
- if !o.extensible {
- o.val.runtime.typeErrorResult(throw, "Cannot add property %d, object is not extensible", idx)
- return false
- } else {
- if throw && !key.IsValid() {
- o.toKey(idx, true)
- return false
- }
- }
- }
- o._put(key, val, throw)
- return true
- }
- func (o *objectGoMapReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
- return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw)
- }
- func (o *objectGoMapReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
- return o._setForeignIdx(idx, trueValIfPresent(o.hasOwnPropertyIdx(idx)), val, receiver, throw)
- }
- func (o *objectGoMapReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
- if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
- return false
- }
- return o._put(o.strToKey(name.String(), throw), descr.Value, throw)
- }
- func (o *objectGoMapReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
- if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) {
- return false
- }
- return o._put(o.toKey(idx, throw), descr.Value, throw)
- }
- func (o *objectGoMapReflect) hasOwnPropertyStr(name unistring.String) bool {
- key := o.strToKey(name.String(), false)
- if key.IsValid() && o.fieldsValue.MapIndex(key).IsValid() {
- return true
- }
- return false
- }
- func (o *objectGoMapReflect) hasOwnPropertyIdx(idx valueInt) bool {
- key := o.toKey(idx, false)
- if key.IsValid() && o.fieldsValue.MapIndex(key).IsValid() {
- return true
- }
- return false
- }
- func (o *objectGoMapReflect) deleteStr(name unistring.String, throw bool) bool {
- key := o.strToKey(name.String(), throw)
- if !key.IsValid() {
- return false
- }
- o.fieldsValue.SetMapIndex(key, reflect.Value{})
- return true
- }
- func (o *objectGoMapReflect) deleteIdx(idx valueInt, throw bool) bool {
- key := o.toKey(idx, throw)
- if !key.IsValid() {
- return false
- }
- o.fieldsValue.SetMapIndex(key, reflect.Value{})
- return true
- }
- type gomapReflectPropIter struct {
- o *objectGoMapReflect
- keys []reflect.Value
- idx int
- }
- func (i *gomapReflectPropIter) next() (propIterItem, iterNextFunc) {
- for i.idx < len(i.keys) {
- key := i.keys[i.idx]
- v := i.o.fieldsValue.MapIndex(key)
- i.idx++
- if v.IsValid() {
- return propIterItem{name: i.o.keyToString(key), enumerable: _ENUM_TRUE}, i.next
- }
- }
- return propIterItem{}, nil
- }
- func (o *objectGoMapReflect) iterateStringKeys() iterNextFunc {
- return (&gomapReflectPropIter{
- o: o,
- keys: o.fieldsValue.MapKeys(),
- }).next
- }
- func (o *objectGoMapReflect) stringKeys(_ bool, accum []Value) []Value {
- // all own keys are enumerable
- for _, key := range o.fieldsValue.MapKeys() {
- accum = append(accum, o.keyToString(key))
- }
- return accum
- }
- func (*objectGoMapReflect) keyToString(key reflect.Value) String {
- kind := key.Kind()
- if kind == reflect.String {
- return newStringValue(key.String())
- }
- str := fmt.Sprintf("%v", key)
- switch kind {
- case reflect.Int,
- reflect.Int8,
- reflect.Int16,
- reflect.Int32,
- reflect.Int64,
- reflect.Uint,
- reflect.Uint8,
- reflect.Uint16,
- reflect.Uint32,
- reflect.Uint64,
- reflect.Float32,
- reflect.Float64:
- return asciiString(str)
- default:
- return newStringValue(str)
- }
- }
|