github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goarray_reflect.go (about) 1 package goja 2 3 import ( 4 "reflect" 5 "strconv" 6 7 "github.com/nuvolaris/goja/unistring" 8 ) 9 10 type objectGoArrayReflect struct { 11 objectGoReflect 12 lengthProp valueProperty 13 14 valueCache valueArrayCache 15 16 putIdx func(idx int, v Value, throw bool) bool 17 } 18 19 type valueArrayCache []reflectValueWrapper 20 21 func (c *valueArrayCache) get(idx int) reflectValueWrapper { 22 if idx < len(*c) { 23 return (*c)[idx] 24 } 25 return nil 26 } 27 28 func (c *valueArrayCache) grow(newlen int) { 29 oldcap := cap(*c) 30 if oldcap < newlen { 31 a := make([]reflectValueWrapper, newlen, growCap(newlen, len(*c), oldcap)) 32 copy(a, *c) 33 *c = a 34 } else { 35 *c = (*c)[:newlen] 36 } 37 } 38 39 func (c *valueArrayCache) put(idx int, w reflectValueWrapper) { 40 if len(*c) <= idx { 41 c.grow(idx + 1) 42 } 43 (*c)[idx] = w 44 } 45 46 func (c *valueArrayCache) shrink(newlen int) { 47 if len(*c) > newlen { 48 tail := (*c)[newlen:] 49 for i, item := range tail { 50 if item != nil { 51 copyReflectValueWrapper(item) 52 tail[i] = nil 53 } 54 } 55 *c = (*c)[:newlen] 56 } 57 } 58 59 func (o *objectGoArrayReflect) _init() { 60 o.objectGoReflect.init() 61 o.class = classArray 62 o.prototype = o.val.runtime.global.ArrayPrototype 63 o.baseObject._put("length", &o.lengthProp) 64 } 65 66 func (o *objectGoArrayReflect) init() { 67 o._init() 68 o.updateLen() 69 o.putIdx = o._putIdx 70 } 71 72 func (o *objectGoArrayReflect) updateLen() { 73 o.lengthProp.value = intToValue(int64(o.fieldsValue.Len())) 74 } 75 76 func (o *objectGoArrayReflect) _hasIdx(idx valueInt) bool { 77 if idx := int64(idx); idx >= 0 && idx < int64(o.fieldsValue.Len()) { 78 return true 79 } 80 return false 81 } 82 83 func (o *objectGoArrayReflect) _hasStr(name unistring.String) bool { 84 if idx := strToIdx64(name); idx >= 0 && idx < int64(o.fieldsValue.Len()) { 85 return true 86 } 87 return false 88 } 89 90 func (o *objectGoArrayReflect) _getIdx(idx int) Value { 91 if v := o.valueCache.get(idx); v != nil { 92 return v.esValue() 93 } 94 95 v := o.fieldsValue.Index(idx) 96 97 res, w := o.elemToValue(v) 98 if w != nil { 99 o.valueCache.put(idx, w) 100 } 101 102 return res 103 } 104 105 func (o *objectGoArrayReflect) getIdx(idx valueInt, receiver Value) Value { 106 if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() { 107 return o._getIdx(idx) 108 } 109 return o.objectGoReflect.getStr(idx.string(), receiver) 110 } 111 112 func (o *objectGoArrayReflect) getStr(name unistring.String, receiver Value) Value { 113 var ownProp Value 114 if idx := strToGoIdx(name); idx >= 0 && idx < o.fieldsValue.Len() { 115 ownProp = o._getIdx(idx) 116 } else if name == "length" { 117 if o.fieldsValue.Kind() == reflect.Slice { 118 o.updateLen() 119 } 120 ownProp = &o.lengthProp 121 } else { 122 ownProp = o.objectGoReflect.getOwnPropStr(name) 123 } 124 return o.getStrWithOwnProp(ownProp, name, receiver) 125 } 126 127 func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value { 128 if idx := strToGoIdx(name); idx >= 0 { 129 if idx < o.fieldsValue.Len() { 130 return &valueProperty{ 131 value: o._getIdx(idx), 132 writable: true, 133 enumerable: true, 134 } 135 } 136 return nil 137 } 138 if name == "length" { 139 if o.fieldsValue.Kind() == reflect.Slice { 140 o.updateLen() 141 } 142 return &o.lengthProp 143 } 144 return o.objectGoReflect.getOwnPropStr(name) 145 } 146 147 func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value { 148 if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() { 149 return &valueProperty{ 150 value: o._getIdx(idx), 151 writable: true, 152 enumerable: true, 153 } 154 } 155 return nil 156 } 157 158 func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool { 159 cached := o.valueCache.get(idx) 160 if cached != nil { 161 copyReflectValueWrapper(cached) 162 } 163 164 rv := o.fieldsValue.Index(idx) 165 err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{}) 166 if err != nil { 167 if cached != nil { 168 cached.setReflectValue(rv) 169 } 170 o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err) 171 return false 172 } 173 if cached != nil { 174 o.valueCache[idx] = nil 175 } 176 return true 177 } 178 179 func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool { 180 if i := toIntStrict(int64(idx)); i >= 0 { 181 if i >= o.fieldsValue.Len() { 182 if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok { 183 return res 184 } 185 } 186 return o.putIdx(i, val, throw) 187 } else { 188 name := idx.string() 189 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { 190 o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) 191 return false 192 } else { 193 return res 194 } 195 } 196 } 197 198 func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool { 199 if idx := strToGoIdx(name); idx >= 0 { 200 if idx >= o.fieldsValue.Len() { 201 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok { 202 return res 203 } 204 } 205 return o.putIdx(idx, val, throw) 206 } else { 207 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { 208 o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) 209 return false 210 } else { 211 return res 212 } 213 } 214 } 215 216 func (o *objectGoArrayReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) { 217 return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw) 218 } 219 220 func (o *objectGoArrayReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { 221 return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw) 222 } 223 224 func (o *objectGoArrayReflect) hasOwnPropertyIdx(idx valueInt) bool { 225 return o._hasIdx(idx) 226 } 227 228 func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool { 229 if o._hasStr(name) || name == "length" { 230 return true 231 } 232 return o.objectGoReflect.hasOwnPropertyStr(name) 233 } 234 235 func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { 236 if i := toIntStrict(int64(idx)); i >= 0 { 237 if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) { 238 return false 239 } 240 val := descr.Value 241 if val == nil { 242 val = _undefined 243 } 244 return o.putIdx(i, val, throw) 245 } 246 o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx) 247 return false 248 } 249 250 func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 251 if idx := strToGoIdx(name); idx >= 0 { 252 if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) { 253 return false 254 } 255 val := descr.Value 256 if val == nil { 257 val = _undefined 258 } 259 return o.putIdx(idx, val, throw) 260 } 261 o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name) 262 return false 263 } 264 265 func (o *objectGoArrayReflect) _deleteIdx(idx int) { 266 if idx < o.fieldsValue.Len() { 267 if cv := o.valueCache.get(idx); cv != nil { 268 copyReflectValueWrapper(cv) 269 o.valueCache[idx] = nil 270 } 271 272 o.fieldsValue.Index(idx).Set(reflect.Zero(o.fieldsValue.Type().Elem())) 273 } 274 } 275 276 func (o *objectGoArrayReflect) deleteStr(name unistring.String, throw bool) bool { 277 if idx := strToGoIdx(name); idx >= 0 { 278 o._deleteIdx(idx) 279 return true 280 } 281 282 return o.objectGoReflect.deleteStr(name, throw) 283 } 284 285 func (o *objectGoArrayReflect) deleteIdx(i valueInt, throw bool) bool { 286 idx := toIntStrict(int64(i)) 287 if idx >= 0 { 288 o._deleteIdx(idx) 289 } 290 return true 291 } 292 293 type goArrayReflectPropIter struct { 294 o *objectGoArrayReflect 295 idx, limit int 296 } 297 298 func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) { 299 if i.idx < i.limit && i.idx < i.o.fieldsValue.Len() { 300 name := strconv.Itoa(i.idx) 301 i.idx++ 302 return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next 303 } 304 305 return i.o.objectGoReflect.iterateStringKeys()() 306 } 307 308 func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value { 309 for i := 0; i < o.fieldsValue.Len(); i++ { 310 accum = append(accum, asciiString(strconv.Itoa(i))) 311 } 312 313 return o.objectGoReflect.stringKeys(all, accum) 314 } 315 316 func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc { 317 return (&goArrayReflectPropIter{ 318 o: o, 319 limit: o.fieldsValue.Len(), 320 }).next 321 } 322 323 func (o *objectGoArrayReflect) sortLen() int { 324 return o.fieldsValue.Len() 325 } 326 327 func (o *objectGoArrayReflect) sortGet(i int) Value { 328 return o.getIdx(valueInt(i), nil) 329 } 330 331 func (o *objectGoArrayReflect) swap(i int, j int) { 332 vi := o.fieldsValue.Index(i) 333 vj := o.fieldsValue.Index(j) 334 tmp := reflect.New(o.fieldsValue.Type().Elem()).Elem() 335 tmp.Set(vi) 336 vi.Set(vj) 337 vj.Set(tmp) 338 339 cachedI := o.valueCache.get(i) 340 cachedJ := o.valueCache.get(j) 341 if cachedI != nil { 342 cachedI.setReflectValue(vj) 343 o.valueCache.put(j, cachedI) 344 } else { 345 if j < len(o.valueCache) { 346 o.valueCache[j] = nil 347 } 348 } 349 350 if cachedJ != nil { 351 cachedJ.setReflectValue(vi) 352 o.valueCache.put(i, cachedJ) 353 } else { 354 if i < len(o.valueCache) { 355 o.valueCache[i] = nil 356 } 357 } 358 }