ai_old/goja/object_goarray_reflect.go

359 lines
8.5 KiB
Go
Raw Normal View History

2024-09-20 16:50:35 +08:00
package goja
import (
"reflect"
"strconv"
"github.com/dop251/goja/unistring"
)
type objectGoArrayReflect struct {
objectGoReflect
lengthProp valueProperty
valueCache valueArrayCache
putIdx func(idx int, v Value, throw bool) bool
}
type valueArrayCache []reflectValueWrapper
func (c *valueArrayCache) get(idx int) reflectValueWrapper {
if idx < len(*c) {
return (*c)[idx]
}
return nil
}
func (c *valueArrayCache) grow(newlen int) {
oldcap := cap(*c)
if oldcap < newlen {
a := make([]reflectValueWrapper, newlen, growCap(newlen, len(*c), oldcap))
copy(a, *c)
*c = a
} else {
*c = (*c)[:newlen]
}
}
func (c *valueArrayCache) put(idx int, w reflectValueWrapper) {
if len(*c) <= idx {
c.grow(idx + 1)
}
(*c)[idx] = w
}
func (c *valueArrayCache) shrink(newlen int) {
if len(*c) > newlen {
tail := (*c)[newlen:]
for i, item := range tail {
if item != nil {
copyReflectValueWrapper(item)
tail[i] = nil
}
}
*c = (*c)[:newlen]
}
}
func (o *objectGoArrayReflect) _init() {
o.objectGoReflect.init()
o.class = classArray
o.prototype = o.val.runtime.getArrayPrototype()
o.baseObject._put("length", &o.lengthProp)
}
func (o *objectGoArrayReflect) init() {
o._init()
o.updateLen()
o.putIdx = o._putIdx
}
func (o *objectGoArrayReflect) updateLen() {
o.lengthProp.value = intToValue(int64(o.fieldsValue.Len()))
}
func (o *objectGoArrayReflect) _hasIdx(idx valueInt) bool {
if idx := int64(idx); idx >= 0 && idx < int64(o.fieldsValue.Len()) {
return true
}
return false
}
func (o *objectGoArrayReflect) _hasStr(name unistring.String) bool {
if idx := strToIdx64(name); idx >= 0 && idx < int64(o.fieldsValue.Len()) {
return true
}
return false
}
func (o *objectGoArrayReflect) _getIdx(idx int) Value {
if v := o.valueCache.get(idx); v != nil {
return v.esValue()
}
v := o.fieldsValue.Index(idx)
res, w := o.elemToValue(v)
if w != nil {
o.valueCache.put(idx, w)
}
return res
}
func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value {
if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
return o._getIdx(idx)
}
return o.objectGoReflect.getStr(idx.string(), receiver)
}
func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Value {
var ownProp Value
if idx := strToGoIdx(name); idx >= 0 && idx < o.fieldsValue.Len() {
ownProp = o._getIdx(idx)
} else if name == "length" {
if o.fieldsValue.Kind() == reflect.Slice {
o.updateLen()
}
ownProp = &o.lengthProp
} else {
ownProp = o.objectGoReflect.getOwnPropStr(name)
}
return o.getStrWithOwnProp(ownProp, name, receiver)
}
func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value {
if idx := strToGoIdx(name); idx >= 0 {
if idx < o.fieldsValue.Len() {
return &valueProperty{
value: o._getIdx(idx),
writable: true,
enumerable: true,
}
}
return nil
}
if name == "length" {
if o.fieldsValue.Kind() == reflect.Slice {
o.updateLen()
}
return &o.lengthProp
}
return o.objectGoReflect.getOwnPropStr(name)
}
func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value {
if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() {
return &valueProperty{
value: o._getIdx(idx),
writable: true,
enumerable: true,
}
}
return nil
}
func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool {
cached := o.valueCache.get(idx)
if cached != nil {
copyReflectValueWrapper(cached)
}
rv := o.fieldsValue.Index(idx)
err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{})
if err != nil {
if cached != nil {
cached.setReflectValue(rv)
}
o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err)
return false
}
if cached != nil {
o.valueCache[idx] = nil
}
return true
}
func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool {
if i := toIntStrict(int64(idx)); i >= 0 {
if i >= o.fieldsValue.Len() {
if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok {
return res
}
}
return o.putIdx(i, val, throw)
} else {
name := idx.string()
if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
return false
} else {
return res
}
}
}
func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool {
if idx := strToGoIdx(name); idx >= 0 {
if idx >= o.fieldsValue.Len() {
if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok {
return res
}
}
return o.putIdx(idx, val, throw)
} else {
if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok {
o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name)
return false
} else {
return res
}
}
}
func (o *objectGoArrayReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) {
return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw)
}
func (o *objectGoArrayReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw)
}
func (o *objectGoArrayReflect) hasOwnPropertyIdx(idx valueInt) bool {
return o._hasIdx(idx)
}
func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool {
if o._hasStr(name) || name == "length" {
return true
}
return o.objectGoReflect.hasOwnPropertyStr(name)
}
func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool {
if i := toIntStrict(int64(idx)); i >= 0 {
if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) {
return false
}
val := descr.Value
if val == nil {
val = _undefined
}
return o.putIdx(i, val, throw)
}
o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx)
return false
}
func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool {
if idx := strToGoIdx(name); idx >= 0 {
if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) {
return false
}
val := descr.Value
if val == nil {
val = _undefined
}
return o.putIdx(idx, val, throw)
}
o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name)
return false
}
func (o *objectGoArrayReflect) _deleteIdx(idx int) {
if idx < o.fieldsValue.Len() {
if cv := o.valueCache.get(idx); cv != nil {
copyReflectValueWrapper(cv)
o.valueCache[idx] = nil
}
o.fieldsValue.Index(idx).Set(reflect.Zero(o.fieldsValue.Type().Elem()))
}
}
func (o *objectGoArrayReflect) deleteStr(name unistring.String, throw bool) bool {
if idx := strToGoIdx(name); idx >= 0 {
o._deleteIdx(idx)
return true
}
return o.objectGoReflect.deleteStr(name, throw)
}
func (o *objectGoArrayReflect) deleteIdx(i valueInt, throw bool) bool {
idx := toIntStrict(int64(i))
if idx >= 0 {
o._deleteIdx(idx)
}
return true
}
type goArrayReflectPropIter struct {
o *objectGoArrayReflect
idx, limit int
}
func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) {
if i.idx < i.limit && i.idx < i.o.fieldsValue.Len() {
name := strconv.Itoa(i.idx)
i.idx++
return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next
}
return i.o.objectGoReflect.iterateStringKeys()()
}
func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value {
for i := 0; i < o.fieldsValue.Len(); i++ {
accum = append(accum, asciiString(strconv.Itoa(i)))
}
return o.objectGoReflect.stringKeys(all, accum)
}
func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc {
return (&goArrayReflectPropIter{
o: o,
limit: o.fieldsValue.Len(),
}).next
}
func (o *objectGoArrayReflect) sortLen() int {
return o.fieldsValue.Len()
}
func (o *objectGoArrayReflect) sortGet(i int) Value {
return o.getIdx(valueInt(i), nil)
}
func (o *objectGoArrayReflect) swap(i int, j int) {
vi := o.fieldsValue.Index(i)
vj := o.fieldsValue.Index(j)
tmp := reflect.New(o.fieldsValue.Type().Elem()).Elem()
tmp.Set(vi)
vi.Set(vj)
vj.Set(tmp)
cachedI := o.valueCache.get(i)
cachedJ := o.valueCache.get(j)
if cachedI != nil {
cachedI.setReflectValue(vj)
o.valueCache.put(j, cachedI)
} else {
if j < len(o.valueCache) {
o.valueCache[j] = nil
}
}
if cachedJ != nil {
cachedJ.setReflectValue(vi)
o.valueCache.put(i, cachedJ)
} else {
if i < len(o.valueCache) {
o.valueCache[i] = nil
}
}
}