github.com/dop251/goja@v0.0.0-20240220182346-e401ed450204/object_template.go (about) 1 package goja 2 3 import ( 4 "fmt" 5 "github.com/dop251/goja/unistring" 6 "math" 7 "reflect" 8 "sort" 9 ) 10 11 type templatePropFactory func(*Runtime) Value 12 13 type objectTemplate struct { 14 propNames []unistring.String 15 props map[unistring.String]templatePropFactory 16 17 symProps map[*Symbol]templatePropFactory 18 symPropNames []*Symbol 19 20 protoFactory func(*Runtime) *Object 21 } 22 23 type templatedObject struct { 24 baseObject 25 tmpl *objectTemplate 26 27 protoMaterialised bool 28 } 29 30 type templatedFuncObject struct { 31 templatedObject 32 33 f func(FunctionCall) Value 34 construct func(args []Value, newTarget *Object) *Object 35 } 36 37 // This type exists because Array.prototype is supposed to be an array itself and I could not find 38 // a different way of implementing it without either introducing another layer of interfaces or hoisting 39 // the templates to baseObject both of which would have had a negative effect on the performance. 40 // The implementation is as simple as possible and is not optimised in any way, but I very much doubt anybody 41 // uses Array.prototype as an actual array. 42 type templatedArrayObject struct { 43 templatedObject 44 } 45 46 func newObjectTemplate() *objectTemplate { 47 return &objectTemplate{ 48 props: make(map[unistring.String]templatePropFactory), 49 } 50 } 51 52 func (t *objectTemplate) putStr(name unistring.String, f templatePropFactory) { 53 t.props[name] = f 54 t.propNames = append(t.propNames, name) 55 } 56 57 func (t *objectTemplate) putSym(s *Symbol, f templatePropFactory) { 58 if t.symProps == nil { 59 t.symProps = make(map[*Symbol]templatePropFactory) 60 } 61 t.symProps[s] = f 62 t.symPropNames = append(t.symPropNames, s) 63 } 64 65 func (r *Runtime) newTemplatedObject(tmpl *objectTemplate, obj *Object) *templatedObject { 66 if obj == nil { 67 obj = &Object{runtime: r} 68 } 69 o := &templatedObject{ 70 baseObject: baseObject{ 71 class: classObject, 72 val: obj, 73 extensible: true, 74 }, 75 tmpl: tmpl, 76 } 77 obj.self = o 78 o.init() 79 return o 80 } 81 82 func (o *templatedObject) materialiseProto() { 83 if !o.protoMaterialised { 84 if o.tmpl.protoFactory != nil { 85 o.prototype = o.tmpl.protoFactory(o.val.runtime) 86 } 87 o.protoMaterialised = true 88 } 89 } 90 91 func (o *templatedObject) getStr(name unistring.String, receiver Value) Value { 92 ownProp := o.getOwnPropStr(name) 93 if ownProp == nil { 94 o.materialiseProto() 95 } 96 return o.getStrWithOwnProp(ownProp, name, receiver) 97 } 98 99 func (o *templatedObject) getSym(s *Symbol, receiver Value) Value { 100 ownProp := o.getOwnPropSym(s) 101 if ownProp == nil { 102 o.materialiseProto() 103 } 104 return o.getWithOwnProp(ownProp, s, receiver) 105 } 106 107 func (o *templatedObject) getOwnPropStr(p unistring.String) Value { 108 if v, exists := o.values[p]; exists { 109 return v 110 } 111 if f := o.tmpl.props[p]; f != nil { 112 v := f(o.val.runtime) 113 o.values[p] = v 114 return v 115 } 116 return nil 117 } 118 119 func (o *templatedObject) materialiseSymbols() { 120 if o.symValues == nil { 121 o.symValues = newOrderedMap(nil) 122 for _, p := range o.tmpl.symPropNames { 123 o.symValues.set(p, o.tmpl.symProps[p](o.val.runtime)) 124 } 125 } 126 } 127 128 func (o *templatedObject) getOwnPropSym(s *Symbol) Value { 129 if o.symValues == nil && o.tmpl.symProps[s] == nil { 130 return nil 131 } 132 o.materialiseSymbols() 133 return o.baseObject.getOwnPropSym(s) 134 } 135 136 func (o *templatedObject) materialisePropNames() { 137 if o.propNames == nil { 138 o.propNames = append(([]unistring.String)(nil), o.tmpl.propNames...) 139 } 140 } 141 142 func (o *templatedObject) setOwnStr(p unistring.String, v Value, throw bool) bool { 143 existing := o.getOwnPropStr(p) // materialise property (in case it's an accessor) 144 if existing == nil { 145 o.materialiseProto() 146 o.materialisePropNames() 147 } 148 return o.baseObject.setOwnStr(p, v, throw) 149 } 150 151 func (o *templatedObject) setOwnSym(name *Symbol, val Value, throw bool) bool { 152 o.materialiseSymbols() 153 o.materialiseProto() 154 return o.baseObject.setOwnSym(name, val, throw) 155 } 156 157 func (o *templatedObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) { 158 ownProp := o.getOwnPropStr(name) 159 if ownProp == nil { 160 o.materialiseProto() 161 } 162 return o._setForeignStr(name, ownProp, val, receiver, throw) 163 } 164 165 func (o *templatedObject) proto() *Object { 166 o.materialiseProto() 167 return o.prototype 168 } 169 170 func (o *templatedObject) setProto(proto *Object, throw bool) bool { 171 o.protoMaterialised = true 172 ret := o.baseObject.setProto(proto, throw) 173 if ret { 174 o.protoMaterialised = true 175 } 176 return ret 177 } 178 179 func (o *templatedObject) setForeignIdx(name valueInt, val, receiver Value, throw bool) (bool, bool) { 180 return o.setForeignStr(name.string(), val, receiver, throw) 181 } 182 183 func (o *templatedObject) setForeignSym(name *Symbol, val, receiver Value, throw bool) (bool, bool) { 184 o.materialiseProto() 185 o.materialiseSymbols() 186 return o.baseObject.setForeignSym(name, val, receiver, throw) 187 } 188 189 func (o *templatedObject) hasPropertyStr(name unistring.String) bool { 190 if o.val.self.hasOwnPropertyStr(name) { 191 return true 192 } 193 o.materialiseProto() 194 if o.prototype != nil { 195 return o.prototype.self.hasPropertyStr(name) 196 } 197 return false 198 } 199 200 func (o *templatedObject) hasPropertySym(s *Symbol) bool { 201 if o.hasOwnPropertySym(s) { 202 return true 203 } 204 o.materialiseProto() 205 if o.prototype != nil { 206 return o.prototype.self.hasPropertySym(s) 207 } 208 return false 209 } 210 211 func (o *templatedObject) hasOwnPropertyStr(name unistring.String) bool { 212 if v, exists := o.values[name]; exists { 213 return v != nil 214 } 215 216 _, exists := o.tmpl.props[name] 217 return exists 218 } 219 220 func (o *templatedObject) hasOwnPropertySym(s *Symbol) bool { 221 if o.symValues != nil { 222 return o.symValues.has(s) 223 } 224 _, exists := o.tmpl.symProps[s] 225 return exists 226 } 227 228 func (o *templatedObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 229 existingVal := o.getOwnPropStr(name) 230 if v, ok := o._defineOwnProperty(name, existingVal, descr, throw); ok { 231 o.values[name] = v 232 if existingVal == nil { 233 o.materialisePropNames() 234 names := copyNamesIfNeeded(o.propNames, 1) 235 o.propNames = append(names, name) 236 } 237 return true 238 } 239 return false 240 } 241 242 func (o *templatedObject) defineOwnPropertySym(s *Symbol, descr PropertyDescriptor, throw bool) bool { 243 o.materialiseSymbols() 244 return o.baseObject.defineOwnPropertySym(s, descr, throw) 245 } 246 247 func (o *templatedObject) deleteStr(name unistring.String, throw bool) bool { 248 if val := o.getOwnPropStr(name); val != nil { 249 if !o.checkDelete(name, val, throw) { 250 return false 251 } 252 o.materialisePropNames() 253 o._delete(name) 254 if _, exists := o.tmpl.props[name]; exists { 255 o.values[name] = nil // white hole 256 } 257 } 258 return true 259 } 260 261 func (o *templatedObject) deleteSym(s *Symbol, throw bool) bool { 262 o.materialiseSymbols() 263 return o.baseObject.deleteSym(s, throw) 264 } 265 266 func (o *templatedObject) materialiseProps() { 267 for name, f := range o.tmpl.props { 268 if _, exists := o.values[name]; !exists { 269 o.values[name] = f(o.val.runtime) 270 } 271 } 272 o.materialisePropNames() 273 } 274 275 func (o *templatedObject) iterateStringKeys() iterNextFunc { 276 o.materialiseProps() 277 return o.baseObject.iterateStringKeys() 278 } 279 280 func (o *templatedObject) iterateSymbols() iterNextFunc { 281 o.materialiseSymbols() 282 return o.baseObject.iterateSymbols() 283 } 284 285 func (o *templatedObject) stringKeys(all bool, keys []Value) []Value { 286 if all { 287 o.materialisePropNames() 288 } else { 289 o.materialiseProps() 290 } 291 return o.baseObject.stringKeys(all, keys) 292 } 293 294 func (o *templatedObject) symbols(all bool, accum []Value) []Value { 295 o.materialiseSymbols() 296 return o.baseObject.symbols(all, accum) 297 } 298 299 func (o *templatedObject) keys(all bool, accum []Value) []Value { 300 return o.symbols(all, o.stringKeys(all, accum)) 301 } 302 303 func (r *Runtime) newTemplatedFuncObject(tmpl *objectTemplate, obj *Object, f func(FunctionCall) Value, ctor func([]Value, *Object) *Object) *templatedFuncObject { 304 if obj == nil { 305 obj = &Object{runtime: r} 306 } 307 o := &templatedFuncObject{ 308 templatedObject: templatedObject{ 309 baseObject: baseObject{ 310 class: classFunction, 311 val: obj, 312 extensible: true, 313 }, 314 tmpl: tmpl, 315 }, 316 f: f, 317 construct: ctor, 318 } 319 obj.self = o 320 o.init() 321 return o 322 } 323 324 func (f *templatedFuncObject) source() String { 325 return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString())) 326 } 327 328 func (f *templatedFuncObject) export(*objectExportCtx) interface{} { 329 return f.f 330 } 331 332 func (f *templatedFuncObject) assertCallable() (func(FunctionCall) Value, bool) { 333 if f.f != nil { 334 return f.f, true 335 } 336 return nil, false 337 } 338 339 func (f *templatedFuncObject) vmCall(vm *vm, n int) { 340 var nf nativeFuncObject 341 nf.f = f.f 342 nf.vmCall(vm, n) 343 } 344 345 func (f *templatedFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object { 346 return f.construct 347 } 348 349 func (f *templatedFuncObject) exportType() reflect.Type { 350 return reflectTypeFunc 351 } 352 353 func (f *templatedFuncObject) typeOf() String { 354 return stringFunction 355 } 356 357 func (f *templatedFuncObject) hasInstance(v Value) bool { 358 return hasInstance(f.val, v) 359 } 360 361 func (r *Runtime) newTemplatedArrayObject(tmpl *objectTemplate, obj *Object) *templatedArrayObject { 362 if obj == nil { 363 obj = &Object{runtime: r} 364 } 365 o := &templatedArrayObject{ 366 templatedObject: templatedObject{ 367 baseObject: baseObject{ 368 class: classArray, 369 val: obj, 370 extensible: true, 371 }, 372 tmpl: tmpl, 373 }, 374 } 375 obj.self = o 376 o.init() 377 return o 378 } 379 380 func (a *templatedArrayObject) getLenProp() *valueProperty { 381 lenProp, _ := a.getOwnPropStr("length").(*valueProperty) 382 if lenProp == nil { 383 panic(a.val.runtime.NewTypeError("missing length property")) 384 } 385 return lenProp 386 } 387 388 func (a *templatedArrayObject) _setOwnIdx(idx uint32) { 389 lenProp := a.getLenProp() 390 l := uint32(lenProp.value.ToInteger()) 391 if idx >= l { 392 lenProp.value = intToValue(int64(idx) + 1) 393 } 394 } 395 396 func (a *templatedArrayObject) setLength(l uint32, throw bool) bool { 397 lenProp := a.getLenProp() 398 oldLen := uint32(lenProp.value.ToInteger()) 399 if l == oldLen { 400 return true 401 } 402 if !lenProp.writable { 403 a.val.runtime.typeErrorResult(throw, "length is not writable") 404 return false 405 } 406 ret := true 407 if l < oldLen { 408 a.materialisePropNames() 409 a.fixPropOrder() 410 i := sort.Search(a.idxPropCount, func(idx int) bool { 411 return strToArrayIdx(a.propNames[idx]) >= l 412 }) 413 for j := a.idxPropCount - 1; j >= i; j-- { 414 if !a.deleteStr(a.propNames[j], false) { 415 l = strToArrayIdx(a.propNames[j]) + 1 416 ret = false 417 break 418 } 419 } 420 } 421 lenProp.value = intToValue(int64(l)) 422 return ret 423 } 424 425 func (a *templatedArrayObject) setOwnStr(name unistring.String, value Value, throw bool) bool { 426 if name == "length" { 427 return a.setLength(a.val.runtime.toLengthUint32(value), throw) 428 } 429 if !a.templatedObject.setOwnStr(name, value, throw) { 430 return false 431 } 432 if idx := strToArrayIdx(name); idx != math.MaxUint32 { 433 a._setOwnIdx(idx) 434 } 435 return true 436 } 437 438 func (a *templatedArrayObject) setOwnIdx(p valueInt, v Value, throw bool) bool { 439 if !a.templatedObject.setOwnStr(p.string(), v, throw) { 440 return false 441 } 442 if idx := toIdx(p); idx != math.MaxUint32 { 443 a._setOwnIdx(idx) 444 } 445 return true 446 } 447 448 func (a *templatedArrayObject) defineOwnPropertyStr(name unistring.String, descr PropertyDescriptor, throw bool) bool { 449 if name == "length" { 450 return a.val.runtime.defineArrayLength(a.getLenProp(), descr, a.setLength, throw) 451 } 452 if !a.templatedObject.defineOwnPropertyStr(name, descr, throw) { 453 return false 454 } 455 if idx := strToArrayIdx(name); idx != math.MaxUint32 { 456 a._setOwnIdx(idx) 457 } 458 return true 459 } 460 461 func (a *templatedArrayObject) defineOwnPropertyIdx(p valueInt, desc PropertyDescriptor, throw bool) bool { 462 if !a.templatedObject.defineOwnPropertyStr(p.string(), desc, throw) { 463 return false 464 } 465 if idx := toIdx(p); idx != math.MaxUint32 { 466 a._setOwnIdx(idx) 467 } 468 return true 469 }