go.ketch.com/lib/goja@v0.0.1/object_goarray_reflect.go (about) 1 package goja 2 3 import ( 4 "reflect" 5 "strconv" 6 7 "go.ketch.com/lib/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.updateLen() 64 o.baseObject._put("length", &o.lengthProp) 65 } 66 67 func (o *objectGoArrayReflect) init() { 68 o._init() 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 ownProp = &o.lengthProp 118 } else { 119 ownProp = o.objectGoReflect.getOwnPropStr(name) 120 } 121 return o.getStrWithOwnProp(ownProp, name, receiver) 122 } 123 124 func (o *objectGoArrayReflect) getOwnPropStr(name unistring.String) Value { 125 if idx := strToGoIdx(name); idx >= 0 { 126 if idx < o.fieldsValue.Len() { 127 return &valueProperty{ 128 value: o._getIdx(idx), 129 writable: true, 130 enumerable: true, 131 } 132 } 133 return nil 134 } 135 if name == "length" { 136 return &o.lengthProp 137 } 138 return o.objectGoReflect.getOwnPropStr(name) 139 } 140 141 func (o *objectGoArrayReflect) getOwnPropIdx(idx valueInt) Value { 142 if idx := toIntStrict(int64(idx)); idx >= 0 && idx < o.fieldsValue.Len() { 143 return &valueProperty{ 144 value: o._getIdx(idx), 145 writable: true, 146 enumerable: true, 147 } 148 } 149 return nil 150 } 151 152 func (o *objectGoArrayReflect) _putIdx(idx int, v Value, throw bool) bool { 153 cached := o.valueCache.get(idx) 154 if cached != nil { 155 copyReflectValueWrapper(cached) 156 } 157 158 rv := o.fieldsValue.Index(idx) 159 err := o.val.runtime.toReflectValue(v, rv, &objectExportCtx{}) 160 if err != nil { 161 if cached != nil { 162 cached.setReflectValue(rv) 163 } 164 o.val.runtime.typeErrorResult(throw, "Go type conversion error: %v", err) 165 return false 166 } 167 if cached != nil { 168 o.valueCache[idx] = nil 169 } 170 return true 171 } 172 173 func (o *objectGoArrayReflect) setOwnIdx(idx valueInt, val Value, throw bool) bool { 174 if i := toIntStrict(int64(idx)); i >= 0 { 175 if i >= o.fieldsValue.Len() { 176 if res, ok := o._setForeignIdx(idx, nil, val, o.val, throw); ok { 177 return res 178 } 179 } 180 return o.putIdx(i, val, throw) 181 } else { 182 name := idx.string() 183 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { 184 o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) 185 return false 186 } else { 187 return res 188 } 189 } 190 } 191 192 func (o *objectGoArrayReflect) setOwnStr(name unistring.String, val Value, throw bool) bool { 193 if idx := strToGoIdx(name); idx >= 0 { 194 if idx >= o.fieldsValue.Len() { 195 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); ok { 196 return res 197 } 198 } 199 return o.putIdx(idx, val, throw) 200 } else { 201 if res, ok := o._setForeignStr(name, nil, val, o.val, throw); !ok { 202 o.val.runtime.typeErrorResult(throw, "Can't set property '%s' on Go slice", name) 203 return false 204 } else { 205 return res 206 } 207 } 208 } 209 210 func (o *objectGoArrayReflect) setForeignIdx(idx valueInt, val, receiver Value, throw bool) (bool, bool) { 211 return o._setForeignIdx(idx, trueValIfPresent(o._hasIdx(idx)), val, receiver, throw) 212 } 213 214 func (o *objectGoArrayReflect) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { 215 return o._setForeignStr(name, trueValIfPresent(o.hasOwnPropertyStr(name)), val, receiver, throw) 216 } 217 218 func (o *objectGoArrayReflect) hasOwnPropertyIdx(idx valueInt) bool { 219 return o._hasIdx(idx) 220 } 221 222 func (o *objectGoArrayReflect) hasOwnPropertyStr(name unistring.String) bool { 223 if o._hasStr(name) || name == "length" { 224 return true 225 } 226 return o.objectGoReflect._has(name.String()) 227 } 228 229 func (o *objectGoArrayReflect) defineOwnPropertyIdx(idx valueInt, descr PropertyDescriptor, throw bool) bool { 230 if i := toIntStrict(int64(idx)); i >= 0 { 231 if !o.val.runtime.checkHostObjectPropertyDescr(idx.string(), descr, throw) { 232 return false 233 } 234 val := descr.Value 235 if val == nil { 236 val = _undefined 237 } 238 return o.putIdx(i, val, throw) 239 } 240 o.val.runtime.typeErrorResult(throw, "Cannot define property '%d' on a Go slice", idx) 241 return false 242 } 243 244 func (o *objectGoArrayReflect) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 245 if idx := strToGoIdx(name); idx >= 0 { 246 if !o.val.runtime.checkHostObjectPropertyDescr(name, descr, throw) { 247 return false 248 } 249 val := descr.Value 250 if val == nil { 251 val = _undefined 252 } 253 return o.putIdx(idx, val, throw) 254 } 255 o.val.runtime.typeErrorResult(throw, "Cannot define property '%s' on a Go slice", name) 256 return false 257 } 258 259 func (o *objectGoArrayReflect) toPrimitive() Value { 260 return o.toPrimitiveString() 261 } 262 263 func (o *objectGoArrayReflect) _deleteIdx(idx int) { 264 if idx < o.fieldsValue.Len() { 265 if cv := o.valueCache.get(idx); cv != nil { 266 copyReflectValueWrapper(cv) 267 o.valueCache[idx] = nil 268 } 269 270 o.fieldsValue.Index(idx).Set(reflect.Zero(o.fieldsValue.Type().Elem())) 271 } 272 } 273 274 func (o *objectGoArrayReflect) deleteStr(name unistring.String, throw bool) bool { 275 if idx := strToGoIdx(name); idx >= 0 { 276 o._deleteIdx(idx) 277 return true 278 } 279 280 return o.objectGoReflect.deleteStr(name, throw) 281 } 282 283 func (o *objectGoArrayReflect) deleteIdx(i valueInt, throw bool) bool { 284 idx := toIntStrict(int64(i)) 285 if idx >= 0 { 286 o._deleteIdx(idx) 287 } 288 return true 289 } 290 291 type goArrayReflectPropIter struct { 292 o *objectGoArrayReflect 293 idx, limit int 294 } 295 296 func (i *goArrayReflectPropIter) next() (propIterItem, iterNextFunc) { 297 if i.idx < i.limit && i.idx < i.o.fieldsValue.Len() { 298 name := strconv.Itoa(i.idx) 299 i.idx++ 300 return propIterItem{name: asciiString(name), enumerable: _ENUM_TRUE}, i.next 301 } 302 303 return i.o.objectGoReflect.iterateStringKeys()() 304 } 305 306 func (o *objectGoArrayReflect) stringKeys(all bool, accum []Value) []Value { 307 for i := 0; i < o.fieldsValue.Len(); i++ { 308 accum = append(accum, asciiString(strconv.Itoa(i))) 309 } 310 311 return o.objectGoReflect.stringKeys(all, accum) 312 } 313 314 func (o *objectGoArrayReflect) iterateStringKeys() iterNextFunc { 315 return (&goArrayReflectPropIter{ 316 o: o, 317 limit: o.fieldsValue.Len(), 318 }).next 319 } 320 321 func (o *objectGoArrayReflect) sortLen() int { 322 return o.fieldsValue.Len() 323 } 324 325 func (o *objectGoArrayReflect) sortGet(i int) Value { 326 return o.getIdx(valueInt(i), nil) 327 } 328 329 func (o *objectGoArrayReflect) swap(i int, j int) { 330 vi := o.fieldsValue.Index(i) 331 vj := o.fieldsValue.Index(j) 332 tmp := reflect.New(o.fieldsValue.Type().Elem()).Elem() 333 tmp.Set(vi) 334 vi.Set(vj) 335 vj.Set(tmp) 336 337 cachedI := o.valueCache.get(i) 338 cachedJ := o.valueCache.get(j) 339 if cachedI != nil { 340 cachedI.setReflectValue(vj) 341 o.valueCache.put(j, cachedI) 342 } else { 343 if j < len(o.valueCache) { 344 o.valueCache[j] = nil 345 } 346 } 347 348 if cachedJ != nil { 349 cachedJ.setReflectValue(vi) 350 o.valueCache.put(i, cachedJ) 351 } else { 352 if i < len(o.valueCache) { 353 o.valueCache[i] = nil 354 } 355 } 356 }