359 lines
8.5 KiB
Go
359 lines
8.5 KiB
Go
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
|
|
}
|
|
}
|
|
}
|