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) } }