1package goja
2
3import (
4	"fmt"
5)
6
7func (r *Runtime) builtin_Object(args []Value, proto *Object) *Object {
8	if len(args) > 0 {
9		arg := args[0]
10		if arg != _undefined && arg != _null {
11			return arg.ToObject(r)
12		}
13	}
14	return r.newBaseObject(proto, classObject).val
15}
16
17func (r *Runtime) object_getPrototypeOf(call FunctionCall) Value {
18	o := call.Argument(0).ToObject(r)
19	p := o.self.proto()
20	if p == nil {
21		return _null
22	}
23	return p
24}
25
26func (r *Runtime) valuePropToDescriptorObject(desc Value) Value {
27	if desc == nil {
28		return _undefined
29	}
30	var writable, configurable, enumerable, accessor bool
31	var get, set *Object
32	var value Value
33	if v, ok := desc.(*valueProperty); ok {
34		writable = v.writable
35		configurable = v.configurable
36		enumerable = v.enumerable
37		accessor = v.accessor
38		value = v.value
39		get = v.getterFunc
40		set = v.setterFunc
41	} else {
42		writable = true
43		configurable = true
44		enumerable = true
45		value = desc
46	}
47
48	ret := r.NewObject()
49	obj := ret.self
50	if !accessor {
51		obj.setOwnStr("value", value, false)
52		obj.setOwnStr("writable", r.toBoolean(writable), false)
53	} else {
54		if get != nil {
55			obj.setOwnStr("get", get, false)
56		} else {
57			obj.setOwnStr("get", _undefined, false)
58		}
59		if set != nil {
60			obj.setOwnStr("set", set, false)
61		} else {
62			obj.setOwnStr("set", _undefined, false)
63		}
64	}
65	obj.setOwnStr("enumerable", r.toBoolean(enumerable), false)
66	obj.setOwnStr("configurable", r.toBoolean(configurable), false)
67
68	return ret
69}
70
71func (r *Runtime) object_getOwnPropertyDescriptor(call FunctionCall) Value {
72	o := call.Argument(0).ToObject(r)
73	propName := toPropertyKey(call.Argument(1))
74	return r.valuePropToDescriptorObject(o.getOwnProp(propName))
75}
76
77func (r *Runtime) object_getOwnPropertyDescriptors(call FunctionCall) Value {
78	o := call.Argument(0).ToObject(r)
79	ownKeys := o.self.ownPropertyKeys(true, nil)
80	result := r.newBaseObject(r.global.ObjectPrototype, classObject).val
81	for _, key := range ownKeys {
82		descriptor := r.valuePropToDescriptorObject(o.getOwnProp(key))
83		if descriptor != _undefined {
84			createDataPropertyOrThrow(result, key, descriptor)
85		}
86	}
87	return result
88}
89
90func (r *Runtime) object_getOwnPropertyNames(call FunctionCall) Value {
91	obj := call.Argument(0).ToObject(r)
92
93	return r.newArrayValues(obj.self.ownKeys(true, nil))
94}
95
96func (r *Runtime) object_getOwnPropertySymbols(call FunctionCall) Value {
97	obj := call.Argument(0).ToObject(r)
98	return r.newArrayValues(obj.self.ownSymbols(true, nil))
99}
100
101func (r *Runtime) toValueProp(v Value) *valueProperty {
102	if v == nil || v == _undefined {
103		return nil
104	}
105	obj := r.toObject(v)
106	getter := obj.self.getStr("get", nil)
107	setter := obj.self.getStr("set", nil)
108	writable := obj.self.getStr("writable", nil)
109	value := obj.self.getStr("value", nil)
110	if (getter != nil || setter != nil) && (value != nil || writable != nil) {
111		r.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
112	}
113
114	ret := &valueProperty{}
115	if writable != nil && writable.ToBoolean() {
116		ret.writable = true
117	}
118	if e := obj.self.getStr("enumerable", nil); e != nil && e.ToBoolean() {
119		ret.enumerable = true
120	}
121	if c := obj.self.getStr("configurable", nil); c != nil && c.ToBoolean() {
122		ret.configurable = true
123	}
124	ret.value = value
125
126	if getter != nil && getter != _undefined {
127		o := r.toObject(getter)
128		if _, ok := o.self.assertCallable(); !ok {
129			r.typeErrorResult(true, "getter must be a function")
130		}
131		ret.getterFunc = o
132	}
133
134	if setter != nil && setter != _undefined {
135		o := r.toObject(v)
136		if _, ok := o.self.assertCallable(); !ok {
137			r.typeErrorResult(true, "setter must be a function")
138		}
139		ret.setterFunc = o
140	}
141
142	if ret.getterFunc != nil || ret.setterFunc != nil {
143		ret.accessor = true
144	}
145
146	return ret
147}
148
149func (r *Runtime) toPropertyDescriptor(v Value) (ret PropertyDescriptor) {
150	if o, ok := v.(*Object); ok {
151		descr := o.self
152
153		// Save the original descriptor for reference
154		ret.jsDescriptor = o
155
156		ret.Value = descr.getStr("value", nil)
157
158		if p := descr.getStr("writable", nil); p != nil {
159			ret.Writable = ToFlag(p.ToBoolean())
160		}
161		if p := descr.getStr("enumerable", nil); p != nil {
162			ret.Enumerable = ToFlag(p.ToBoolean())
163		}
164		if p := descr.getStr("configurable", nil); p != nil {
165			ret.Configurable = ToFlag(p.ToBoolean())
166		}
167
168		ret.Getter = descr.getStr("get", nil)
169		ret.Setter = descr.getStr("set", nil)
170
171		if ret.Getter != nil && ret.Getter != _undefined {
172			if _, ok := r.toObject(ret.Getter).self.assertCallable(); !ok {
173				r.typeErrorResult(true, "getter must be a function")
174			}
175		}
176
177		if ret.Setter != nil && ret.Setter != _undefined {
178			if _, ok := r.toObject(ret.Setter).self.assertCallable(); !ok {
179				r.typeErrorResult(true, "setter must be a function")
180			}
181		}
182
183		if (ret.Getter != nil || ret.Setter != nil) && (ret.Value != nil || ret.Writable != FLAG_NOT_SET) {
184			r.typeErrorResult(true, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute")
185		}
186	} else {
187		r.typeErrorResult(true, "Property description must be an object: %s", v.String())
188	}
189
190	return
191}
192
193func (r *Runtime) _defineProperties(o *Object, p Value) {
194	type propItem struct {
195		name Value
196		prop PropertyDescriptor
197	}
198	props := p.ToObject(r)
199	names := props.self.ownPropertyKeys(false, nil)
200	list := make([]propItem, 0, len(names))
201	for _, itemName := range names {
202		list = append(list, propItem{
203			name: itemName,
204			prop: r.toPropertyDescriptor(props.get(itemName, nil)),
205		})
206	}
207	for _, prop := range list {
208		o.defineOwnProperty(prop.name, prop.prop, true)
209	}
210}
211
212func (r *Runtime) object_create(call FunctionCall) Value {
213	var proto *Object
214	if arg := call.Argument(0); arg != _null {
215		if o, ok := arg.(*Object); ok {
216			proto = o
217		} else {
218			r.typeErrorResult(true, "Object prototype may only be an Object or null: %s", arg.String())
219		}
220	}
221	o := r.newBaseObject(proto, classObject).val
222
223	if props := call.Argument(1); props != _undefined {
224		r._defineProperties(o, props)
225	}
226
227	return o
228}
229
230func (r *Runtime) object_defineProperty(call FunctionCall) (ret Value) {
231	if obj, ok := call.Argument(0).(*Object); ok {
232		descr := r.toPropertyDescriptor(call.Argument(2))
233		obj.defineOwnProperty(toPropertyKey(call.Argument(1)), descr, true)
234		ret = call.Argument(0)
235	} else {
236		r.typeErrorResult(true, "Object.defineProperty called on non-object")
237	}
238	return
239}
240
241func (r *Runtime) object_defineProperties(call FunctionCall) Value {
242	obj := r.toObject(call.Argument(0))
243	r._defineProperties(obj, call.Argument(1))
244	return obj
245}
246
247func (r *Runtime) object_seal(call FunctionCall) Value {
248	// ES6
249	arg := call.Argument(0)
250	if obj, ok := arg.(*Object); ok {
251		descr := PropertyDescriptor{
252			Writable:     FLAG_TRUE,
253			Enumerable:   FLAG_TRUE,
254			Configurable: FLAG_FALSE,
255		}
256		for _, key := range obj.self.ownPropertyKeys(true, nil) {
257			v := obj.getOwnProp(key)
258			if prop, ok := v.(*valueProperty); ok {
259				if !prop.configurable {
260					continue
261				}
262				prop.configurable = false
263			} else {
264				descr.Value = v
265				obj.defineOwnProperty(key, descr, true)
266			}
267		}
268		obj.self.preventExtensions(false)
269		return obj
270	}
271	return arg
272}
273
274func (r *Runtime) object_freeze(call FunctionCall) Value {
275	arg := call.Argument(0)
276	if obj, ok := arg.(*Object); ok {
277		descr := PropertyDescriptor{
278			Writable:     FLAG_FALSE,
279			Enumerable:   FLAG_TRUE,
280			Configurable: FLAG_FALSE,
281		}
282		for _, key := range obj.self.ownPropertyKeys(true, nil) {
283			v := obj.getOwnProp(key)
284			if prop, ok := v.(*valueProperty); ok {
285				prop.configurable = false
286				if prop.value != nil {
287					prop.writable = false
288				}
289			} else {
290				descr.Value = v
291				obj.defineOwnProperty(key, descr, true)
292			}
293		}
294		obj.self.preventExtensions(false)
295		return obj
296	} else {
297		// ES6 behavior
298		return arg
299	}
300}
301
302func (r *Runtime) object_preventExtensions(call FunctionCall) (ret Value) {
303	arg := call.Argument(0)
304	if obj, ok := arg.(*Object); ok {
305		obj.self.preventExtensions(false)
306		return obj
307	}
308	// ES6
309	//r.typeErrorResult(true, "Object.preventExtensions called on non-object")
310	//panic("Unreachable")
311	return arg
312}
313
314func (r *Runtime) object_isSealed(call FunctionCall) Value {
315	if obj, ok := call.Argument(0).(*Object); ok {
316		if obj.self.isExtensible() {
317			return valueFalse
318		}
319		for _, key := range obj.self.ownPropertyKeys(true, nil) {
320			prop := obj.getOwnProp(key)
321			if prop, ok := prop.(*valueProperty); ok {
322				if prop.configurable {
323					return valueFalse
324				}
325			} else {
326				return valueFalse
327			}
328		}
329	}
330	return valueTrue
331}
332
333func (r *Runtime) object_isFrozen(call FunctionCall) Value {
334	if obj, ok := call.Argument(0).(*Object); ok {
335		if obj.self.isExtensible() {
336			return valueFalse
337		}
338		for _, key := range obj.self.ownPropertyKeys(true, nil) {
339			prop := obj.getOwnProp(key)
340			if prop, ok := prop.(*valueProperty); ok {
341				if prop.configurable || prop.value != nil && prop.writable {
342					return valueFalse
343				}
344			} else {
345				return valueFalse
346			}
347		}
348	}
349	return valueTrue
350}
351
352func (r *Runtime) object_isExtensible(call FunctionCall) Value {
353	if obj, ok := call.Argument(0).(*Object); ok {
354		if obj.self.isExtensible() {
355			return valueTrue
356		}
357		return valueFalse
358	} else {
359		// ES6
360		//r.typeErrorResult(true, "Object.isExtensible called on non-object")
361		return valueFalse
362	}
363}
364
365func (r *Runtime) object_keys(call FunctionCall) Value {
366	obj := call.Argument(0).ToObject(r)
367
368	return r.newArrayValues(obj.self.ownKeys(false, nil))
369}
370
371func (r *Runtime) object_entries(call FunctionCall) Value {
372	obj := call.Argument(0).ToObject(r)
373
374	var values []Value
375	iter := &enumerableIter{
376		wrapped: obj.self.enumerateOwnKeys(),
377	}
378
379	for item, next := iter.next(); next != nil; item, next = next() {
380		v := nilSafe(obj.self.getStr(item.name, nil))
381		values = append(values, r.newArrayValues([]Value{stringValueFromRaw(item.name), v}))
382	}
383
384	return r.newArrayValues(values)
385}
386
387func (r *Runtime) object_values(call FunctionCall) Value {
388	obj := call.Argument(0).ToObject(r)
389
390	var values []Value
391	iter := &enumerableIter{
392		wrapped: obj.self.enumerateOwnKeys(),
393	}
394
395	for item, next := iter.next(); next != nil; item, next = next() {
396		values = append(values, nilSafe(obj.self.getStr(item.name, nil)))
397	}
398
399	return r.newArrayValues(values)
400}
401
402func (r *Runtime) objectproto_hasOwnProperty(call FunctionCall) Value {
403	p := toPropertyKey(call.Argument(0))
404	o := call.This.ToObject(r)
405	if o.hasOwnProperty(p) {
406		return valueTrue
407	} else {
408		return valueFalse
409	}
410}
411
412func (r *Runtime) objectproto_isPrototypeOf(call FunctionCall) Value {
413	if v, ok := call.Argument(0).(*Object); ok {
414		o := call.This.ToObject(r)
415		for {
416			v = v.self.proto()
417			if v == nil {
418				break
419			}
420			if v == o {
421				return valueTrue
422			}
423		}
424	}
425	return valueFalse
426}
427
428func (r *Runtime) objectproto_propertyIsEnumerable(call FunctionCall) Value {
429	p := toPropertyKey(call.Argument(0))
430	o := call.This.ToObject(r)
431	pv := o.getOwnProp(p)
432	if pv == nil {
433		return valueFalse
434	}
435	if prop, ok := pv.(*valueProperty); ok {
436		if !prop.enumerable {
437			return valueFalse
438		}
439	}
440	return valueTrue
441}
442
443func (r *Runtime) objectproto_toString(call FunctionCall) Value {
444	switch o := call.This.(type) {
445	case valueNull:
446		return stringObjectNull
447	case valueUndefined:
448		return stringObjectUndefined
449	default:
450		obj := o.ToObject(r)
451		var clsName string
452		if isArray(obj) {
453			clsName = classArray
454		} else {
455			clsName = obj.self.className()
456		}
457		if tag := obj.self.getSym(SymToStringTag, nil); tag != nil {
458			if str, ok := tag.(valueString); ok {
459				clsName = str.String()
460			}
461		}
462		return newStringValue(fmt.Sprintf("[object %s]", clsName))
463	}
464}
465
466func (r *Runtime) objectproto_toLocaleString(call FunctionCall) Value {
467	toString := toMethod(r.getVStr(call.This, "toString"))
468	return toString(FunctionCall{This: call.This})
469}
470
471func (r *Runtime) objectproto_getProto(call FunctionCall) Value {
472	proto := call.This.ToObject(r).self.proto()
473	if proto != nil {
474		return proto
475	}
476	return _null
477}
478
479func (r *Runtime) objectproto_setProto(call FunctionCall) Value {
480	o := call.This
481	r.checkObjectCoercible(o)
482	proto := r.toProto(call.Argument(0))
483	if o, ok := o.(*Object); ok {
484		o.self.setProto(proto, true)
485	}
486
487	return _undefined
488}
489
490func (r *Runtime) objectproto_valueOf(call FunctionCall) Value {
491	return call.This.ToObject(r)
492}
493
494func (r *Runtime) object_assign(call FunctionCall) Value {
495	to := call.Argument(0).ToObject(r)
496	if len(call.Arguments) > 1 {
497		for _, arg := range call.Arguments[1:] {
498			if arg != _undefined && arg != _null {
499				source := arg.ToObject(r)
500				for _, key := range source.self.ownPropertyKeys(true, nil) {
501					p := source.getOwnProp(key)
502					if p == nil {
503						continue
504					}
505					if v, ok := p.(*valueProperty); ok {
506						if !v.enumerable {
507							continue
508						}
509						p = v.get(source)
510					}
511					to.setOwn(key, p, true)
512				}
513			}
514		}
515	}
516
517	return to
518}
519
520func (r *Runtime) object_is(call FunctionCall) Value {
521	return r.toBoolean(call.Argument(0).SameAs(call.Argument(1)))
522}
523
524func (r *Runtime) toProto(proto Value) *Object {
525	if proto != _null {
526		if obj, ok := proto.(*Object); ok {
527			return obj
528		} else {
529			panic(r.NewTypeError("Object prototype may only be an Object or null: %s", proto))
530		}
531	}
532	return nil
533}
534
535func (r *Runtime) object_setPrototypeOf(call FunctionCall) Value {
536	o := call.Argument(0)
537	r.checkObjectCoercible(o)
538	proto := r.toProto(call.Argument(1))
539	if o, ok := o.(*Object); ok {
540		o.self.setProto(proto, true)
541	}
542
543	return o
544}
545
546func (r *Runtime) initObject() {
547	o := r.global.ObjectPrototype.self
548	o._putProp("toString", r.newNativeFunc(r.objectproto_toString, nil, "toString", nil, 0), true, false, true)
549	o._putProp("toLocaleString", r.newNativeFunc(r.objectproto_toLocaleString, nil, "toLocaleString", nil, 0), true, false, true)
550	o._putProp("valueOf", r.newNativeFunc(r.objectproto_valueOf, nil, "valueOf", nil, 0), true, false, true)
551	o._putProp("hasOwnProperty", r.newNativeFunc(r.objectproto_hasOwnProperty, nil, "hasOwnProperty", nil, 1), true, false, true)
552	o._putProp("isPrototypeOf", r.newNativeFunc(r.objectproto_isPrototypeOf, nil, "isPrototypeOf", nil, 1), true, false, true)
553	o._putProp("propertyIsEnumerable", r.newNativeFunc(r.objectproto_propertyIsEnumerable, nil, "propertyIsEnumerable", nil, 1), true, false, true)
554	o.defineOwnPropertyStr(__proto__, PropertyDescriptor{
555		Getter:       r.newNativeFunc(r.objectproto_getProto, nil, "get __proto__", nil, 0),
556		Setter:       r.newNativeFunc(r.objectproto_setProto, nil, "set __proto__", nil, 1),
557		Configurable: FLAG_TRUE,
558	}, true)
559
560	r.global.Object = r.newNativeFuncConstruct(r.builtin_Object, classObject, r.global.ObjectPrototype, 1)
561	o = r.global.Object.self
562	o._putProp("assign", r.newNativeFunc(r.object_assign, nil, "assign", nil, 2), true, false, true)
563	o._putProp("defineProperty", r.newNativeFunc(r.object_defineProperty, nil, "defineProperty", nil, 3), true, false, true)
564	o._putProp("defineProperties", r.newNativeFunc(r.object_defineProperties, nil, "defineProperties", nil, 2), true, false, true)
565	o._putProp("entries", r.newNativeFunc(r.object_entries, nil, "entries", nil, 1), true, false, true)
566	o._putProp("getOwnPropertyDescriptor", r.newNativeFunc(r.object_getOwnPropertyDescriptor, nil, "getOwnPropertyDescriptor", nil, 2), true, false, true)
567	o._putProp("getOwnPropertyDescriptors", r.newNativeFunc(r.object_getOwnPropertyDescriptors, nil, "getOwnPropertyDescriptors", nil, 1), true, false, true)
568	o._putProp("getPrototypeOf", r.newNativeFunc(r.object_getPrototypeOf, nil, "getPrototypeOf", nil, 1), true, false, true)
569	o._putProp("is", r.newNativeFunc(r.object_is, nil, "is", nil, 2), true, false, true)
570	o._putProp("getOwnPropertyNames", r.newNativeFunc(r.object_getOwnPropertyNames, nil, "getOwnPropertyNames", nil, 1), true, false, true)
571	o._putProp("getOwnPropertySymbols", r.newNativeFunc(r.object_getOwnPropertySymbols, nil, "getOwnPropertySymbols", nil, 1), true, false, true)
572	o._putProp("create", r.newNativeFunc(r.object_create, nil, "create", nil, 2), true, false, true)
573	o._putProp("seal", r.newNativeFunc(r.object_seal, nil, "seal", nil, 1), true, false, true)
574	o._putProp("freeze", r.newNativeFunc(r.object_freeze, nil, "freeze", nil, 1), true, false, true)
575	o._putProp("preventExtensions", r.newNativeFunc(r.object_preventExtensions, nil, "preventExtensions", nil, 1), true, false, true)
576	o._putProp("isSealed", r.newNativeFunc(r.object_isSealed, nil, "isSealed", nil, 1), true, false, true)
577	o._putProp("isFrozen", r.newNativeFunc(r.object_isFrozen, nil, "isFrozen", nil, 1), true, false, true)
578	o._putProp("isExtensible", r.newNativeFunc(r.object_isExtensible, nil, "isExtensible", nil, 1), true, false, true)
579	o._putProp("keys", r.newNativeFunc(r.object_keys, nil, "keys", nil, 1), true, false, true)
580	o._putProp("setPrototypeOf", r.newNativeFunc(r.object_setPrototypeOf, nil, "setPrototypeOf", nil, 2), true, false, true)
581	o._putProp("values", r.newNativeFunc(r.object_values, nil, "values", nil, 1), true, false, true)
582
583	r.addToGlobal("Object", r.global.Object)
584}
585