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
38
39
40
41
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)
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
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 }
470
View as plain text