1package goja
2
3import (
4	"reflect"
5
6	"github.com/dop251/goja/unistring"
7)
8
9type baseFuncObject struct {
10	baseObject
11
12	lenProp valueProperty
13}
14
15type baseJsFuncObject struct {
16	baseFuncObject
17
18	stash  *stash
19	prg    *Program
20	src    string
21	strict bool
22}
23
24type funcObject struct {
25	baseJsFuncObject
26}
27
28type arrowFuncObject struct {
29	baseJsFuncObject
30	this      Value
31	newTarget Value
32}
33
34type nativeFuncObject struct {
35	baseFuncObject
36
37	f         func(FunctionCall) Value
38	construct func(args []Value, newTarget *Object) *Object
39}
40
41type boundFuncObject struct {
42	nativeFuncObject
43	wrapped *Object
44}
45
46func (f *nativeFuncObject) export(*objectExportCtx) interface{} {
47	return f.f
48}
49
50func (f *nativeFuncObject) exportType() reflect.Type {
51	return reflect.TypeOf(f.f)
52}
53
54func (f *funcObject) _addProto(n unistring.String) Value {
55	if n == "prototype" {
56		if _, exists := f.values[n]; !exists {
57			return f.addPrototype()
58		}
59	}
60	return nil
61}
62
63func (f *funcObject) getStr(p unistring.String, receiver Value) Value {
64	return f.getStrWithOwnProp(f.getOwnPropStr(p), p, receiver)
65}
66
67func (f *funcObject) getOwnPropStr(name unistring.String) Value {
68	if v := f._addProto(name); v != nil {
69		return v
70	}
71
72	return f.baseObject.getOwnPropStr(name)
73}
74
75func (f *funcObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
76	f._addProto(name)
77	return f.baseObject.setOwnStr(name, val, throw)
78}
79
80func (f *funcObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
81	return f._setForeignStr(name, f.getOwnPropStr(name), val, receiver, throw)
82}
83
84func (f *funcObject) deleteStr(name unistring.String, throw bool) bool {
85	f._addProto(name)
86	return f.baseObject.deleteStr(name, throw)
87}
88
89func (f *funcObject) addPrototype() Value {
90	proto := f.val.runtime.NewObject()
91	proto.self._putProp("constructor", f.val, true, false, true)
92	return f._putProp("prototype", proto, true, false, false)
93}
94
95func (f *funcObject) hasOwnPropertyStr(name unistring.String) bool {
96	if r := f.baseObject.hasOwnPropertyStr(name); r {
97		return true
98	}
99
100	if name == "prototype" {
101		return true
102	}
103	return false
104}
105
106func (f *funcObject) ownKeys(all bool, accum []Value) []Value {
107	if all {
108		if _, exists := f.values["prototype"]; !exists {
109			accum = append(accum, asciiString("prototype"))
110		}
111	}
112	return f.baseFuncObject.ownKeys(all, accum)
113}
114
115func (f *funcObject) construct(args []Value, newTarget *Object) *Object {
116	if newTarget == nil {
117		newTarget = f.val
118	}
119	proto := newTarget.self.getStr("prototype", nil)
120	var protoObj *Object
121	if p, ok := proto.(*Object); ok {
122		protoObj = p
123	} else {
124		protoObj = f.val.runtime.global.ObjectPrototype
125	}
126
127	obj := f.val.runtime.newBaseObject(protoObj, classObject).val
128	ret := f.call(FunctionCall{
129		This:      obj,
130		Arguments: args,
131	}, newTarget)
132
133	if ret, ok := ret.(*Object); ok {
134		return ret
135	}
136	return obj
137}
138
139func (f *funcObject) Call(call FunctionCall) Value {
140	return f.call(call, nil)
141}
142
143func (f *arrowFuncObject) Call(call FunctionCall) Value {
144	return f._call(call, f.newTarget, f.this)
145}
146
147func (f *baseJsFuncObject) _call(call FunctionCall, newTarget, this Value) Value {
148	vm := f.val.runtime.vm
149	pc := vm.pc
150
151	vm.stack.expand(vm.sp + len(call.Arguments) + 1)
152	vm.stack[vm.sp] = f.val
153	vm.sp++
154	vm.stack[vm.sp] = this
155	vm.sp++
156	for _, arg := range call.Arguments {
157		if arg != nil {
158			vm.stack[vm.sp] = arg
159		} else {
160			vm.stack[vm.sp] = _undefined
161		}
162		vm.sp++
163	}
164
165	vm.pc = -1
166	vm.pushCtx()
167	vm.args = len(call.Arguments)
168	vm.prg = f.prg
169	vm.stash = f.stash
170	vm.newTarget = newTarget
171	vm.pc = 0
172	vm.run()
173	vm.pc = pc
174	vm.halt = false
175	return vm.pop()
176
177}
178
179func (f *funcObject) call(call FunctionCall, newTarget Value) Value {
180	return f._call(call, newTarget, nilSafe(call.This))
181}
182
183func (f *funcObject) export(*objectExportCtx) interface{} {
184	return f.Call
185}
186
187func (f *funcObject) exportType() reflect.Type {
188	return reflect.TypeOf(f.Call)
189}
190
191func (f *funcObject) assertCallable() (func(FunctionCall) Value, bool) {
192	return f.Call, true
193}
194
195func (f *funcObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
196	return f.construct
197}
198
199func (f *arrowFuncObject) exportType() reflect.Type {
200	return reflect.TypeOf(f.Call)
201}
202
203func (f *arrowFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
204	return f.Call, true
205}
206
207func (f *baseFuncObject) init(name unistring.String, length int) {
208	f.baseObject.init()
209
210	f._putProp("name", stringValueFromRaw(name), false, false, true)
211
212	f.lenProp.configurable = true
213	f.lenProp.value = valueInt(length)
214	f._put("length", &f.lenProp)
215}
216
217func (f *baseFuncObject) hasInstance(v Value) bool {
218	if v, ok := v.(*Object); ok {
219		o := f.val.self.getStr("prototype", nil)
220		if o1, ok := o.(*Object); ok {
221			for {
222				v = v.self.proto()
223				if v == nil {
224					return false
225				}
226				if o1 == v {
227					return true
228				}
229			}
230		} else {
231			f.val.runtime.typeErrorResult(true, "prototype is not an object")
232		}
233	}
234
235	return false
236}
237
238func (f *nativeFuncObject) defaultConstruct(ccall func(ConstructorCall) *Object, args []Value, newTarget *Object) *Object {
239	proto := f.getStr("prototype", nil)
240	var protoObj *Object
241	if p, ok := proto.(*Object); ok {
242		protoObj = p
243	} else {
244		protoObj = f.val.runtime.global.ObjectPrototype
245	}
246	obj := f.val.runtime.newBaseObject(protoObj, classObject).val
247	ret := ccall(ConstructorCall{
248		This:      obj,
249		Arguments: args,
250		NewTarget: newTarget,
251	})
252
253	if ret != nil {
254		return ret
255	}
256	return obj
257}
258
259func (f *nativeFuncObject) assertCallable() (func(FunctionCall) Value, bool) {
260	if f.f != nil {
261		return f.f, true
262	}
263	return nil, false
264}
265
266func (f *nativeFuncObject) assertConstructor() func(args []Value, newTarget *Object) *Object {
267	return f.construct
268}
269
270func (f *boundFuncObject) getStr(p unistring.String, receiver Value) Value {
271	return f.getStrWithOwnProp(f.getOwnPropStr(p), p, receiver)
272}
273
274func (f *boundFuncObject) getOwnPropStr(name unistring.String) Value {
275	if name == "caller" || name == "arguments" {
276		return f.val.runtime.global.throwerProperty
277	}
278
279	return f.nativeFuncObject.getOwnPropStr(name)
280}
281
282func (f *boundFuncObject) deleteStr(name unistring.String, throw bool) bool {
283	if name == "caller" || name == "arguments" {
284		return true
285	}
286	return f.nativeFuncObject.deleteStr(name, throw)
287}
288
289func (f *boundFuncObject) setOwnStr(name unistring.String, val Value, throw bool) bool {
290	if name == "caller" || name == "arguments" {
291		panic(f.val.runtime.NewTypeError("'caller' and 'arguments' are restricted function properties and cannot be accessed in this context."))
292	}
293	return f.nativeFuncObject.setOwnStr(name, val, throw)
294}
295
296func (f *boundFuncObject) setForeignStr(name unistring.String, val, receiver Value, throw bool) (bool, bool) {
297	return f._setForeignStr(name, f.getOwnPropStr(name), val, receiver, throw)
298}
299
300func (f *boundFuncObject) hasInstance(v Value) bool {
301	return instanceOfOperator(v, f.wrapped)
302}
303