1/*
2 * Copyright (C) 1999-2002 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003-2019 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Cameron Zwarich ([email protected])
6 * Copyright (C) 2007 Maks Orlovich
7 * Copyright (C) 2015 Canon Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include "config.h"
27#include "JSFunction.h"
28
29#include "AsyncGeneratorPrototype.h"
30#include "BuiltinNames.h"
31#include "CatchScope.h"
32#include "ClonedArguments.h"
33#include "CodeBlock.h"
34#include "CommonIdentifiers.h"
35#include "CallFrame.h"
36#include "ExceptionHelpers.h"
37#include "FunctionPrototype.h"
38#include "GeneratorPrototype.h"
39#include "GetterSetter.h"
40#include "JSArray.h"
41#include "JSBoundFunction.h"
42#include "JSCInlines.h"
43#include "JSFunctionInlines.h"
44#include "JSGlobalObject.h"
45#include "Interpreter.h"
46#include "ObjectConstructor.h"
47#include "ObjectPrototype.h"
48#include "Parser.h"
49#include "PropertyNameArray.h"
50#include "StackVisitor.h"
51#include "WebAssemblyFunction.h"
52
53namespace JSC {
54
55EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(JSGlobalObject* globalObject, CallFrame* callFrame)
56{
57 VM& vm = globalObject->vm();
58 auto scope = DECLARE_THROW_SCOPE(vm);
59 return throwVMError(globalObject, scope, createNotAConstructorError(globalObject, callFrame->jsCallee()));
60}
61
62const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSFunction) };
63const ClassInfo JSStrictFunction::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStrictFunction) };
64const ClassInfo JSSloppyFunction::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSSloppyFunction) };
65const ClassInfo JSArrowFunction::s_info = { "Function", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrowFunction) };
66
67bool JSFunction::isHostFunctionNonInline() const
68{
69 return isHostFunction();
70}
71
72Structure* JSFunction::selectStructureForNewFuncExp(JSGlobalObject* globalObject, FunctionExecutable* executable)
73{
74 ASSERT(!executable->isHostFunction());
75 bool isBuiltin = executable->isBuiltinFunction();
76 if (executable->isArrowFunction())
77 return globalObject->arrowFunctionStructure(isBuiltin);
78 if (executable->isStrictMode())
79 return globalObject->strictFunctionStructure(isBuiltin);
80 return globalObject->sloppyFunctionStructure(isBuiltin);
81}
82
83JSFunction* JSFunction::create(VM& vm, FunctionExecutable* executable, JSScope* scope)
84{
85 return create(vm, executable, scope, selectStructureForNewFuncExp(scope->globalObject(vm), executable));
86}
87
88JSFunction* JSFunction::create(VM& vm, FunctionExecutable* executable, JSScope* scope, Structure* structure)
89{
90 JSFunction* result = createImpl(vm, executable, scope, structure);
91 executable->notifyCreation(vm, result, "Allocating a function");
92 return result;
93}
94
95JSFunction* JSFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor, const DOMJIT::Signature* signature)
96{
97 NativeExecutable* executable = vm.getHostFunction(nativeFunction, intrinsic, nativeConstructor, signature, name);
98 Structure* structure = globalObject->hostFunctionStructure();
99 JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, globalObject, structure);
100 // Can't do this during initialization because getHostFunction might do a GC allocation.
101 function->finishCreation(vm, executable, length, name);
102 return function;
103}
104
105JSFunction* JSFunction::createFunctionThatMasqueradesAsUndefined(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor, const DOMJIT::Signature* signature)
106{
107 NativeExecutable* executable = vm.getHostFunction(nativeFunction, intrinsic, nativeConstructor, signature, name);
108 Structure* structure = Structure::create(vm, globalObject, globalObject->objectPrototype(), TypeInfo(JSFunctionType, JSFunction::StructureFlags | MasqueradesAsUndefined), JSFunction::info());
109 globalObject->masqueradesAsUndefinedWatchpoint()->fireAll(globalObject->vm(), "Allocated masquerading object");
110 JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, globalObject, structure);
111 function->finishCreation(vm, executable, length, name);
112 return function;
113}
114
115JSFunction::JSFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure)
116 : Base(vm, globalObject, structure)
117 , m_executable()
118 , m_globalObject(vm, this, structure->globalObject())
119{
120 assertTypeInfoFlagInvariants();
121}
122
123
124void JSFunction::finishCreation(VM& vm)
125{
126 Base::finishCreation(vm);
127 ASSERT(jsDynamicCast<JSFunction*>(vm, this));
128 ASSERT(type() == JSFunctionType);
129}
130
131void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name)
132{
133 Base::finishCreation(vm);
134 ASSERT(inherits(vm, info()));
135 ASSERT(type() == JSFunctionType);
136 m_executable.set(vm, this, executable);
137 // Some NativeExecutable functions, like JSBoundFunction, decide to lazily allocate their name string.
138 if (!name.isNull())
139 putDirect(vm, vm.propertyNames->name, jsString(vm, name), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
140 putDirect(vm, vm.propertyNames->length, jsNumber(length), PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum);
141}
142
143FunctionRareData* JSFunction::allocateRareData(VM& vm)
144{
145 ASSERT(!m_rareData);
146 FunctionRareData* rareData = FunctionRareData::create(vm);
147
148 // A DFG compilation thread may be trying to read the rare data
149 // We want to ensure that it sees it properly allocated
150 WTF::storeStoreFence();
151
152 m_rareData.set(vm, this, rareData);
153 return m_rareData.get();
154}
155
156JSObject* JSFunction::prototypeForConstruction(VM& vm, JSGlobalObject* globalObject)
157{
158 // This code assumes getting the prototype is not effectful. That's only
159 // true when we can use the allocation profile.
160 ASSERT(canUseAllocationProfile());
161 auto scope = DECLARE_CATCH_SCOPE(vm);
162 JSValue prototype = get(globalObject, vm.propertyNames->prototype);
163 scope.releaseAssertNoException();
164 if (LIKELY(prototype.isObject()))
165 return asObject(prototype);
166
167 JSGlobalObject* thisGlobalObject = this->globalObject();
168 if (!isHostOrBuiltinFunction()) {
169 // https://tc39.github.io/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluatebody
170 if (isGeneratorWrapperParseMode(jsExecutable()->parseMode()))
171 return thisGlobalObject->generatorPrototype();
172
173 // https://tc39.github.io/ecma262/#sec-asyncgenerator-definitions-evaluatebody
174 if (isAsyncGeneratorWrapperParseMode(jsExecutable()->parseMode()))
175 return thisGlobalObject->asyncGeneratorPrototype();
176 }
177 return thisGlobalObject->objectPrototype();
178}
179
180FunctionRareData* JSFunction::allocateAndInitializeRareData(JSGlobalObject* globalObject, size_t inlineCapacity)
181{
182 ASSERT(!m_rareData);
183 ASSERT(canUseAllocationProfile());
184 VM& vm = globalObject->vm();
185 JSObject* prototype = prototypeForConstruction(vm, globalObject);
186 FunctionRareData* rareData = FunctionRareData::create(vm);
187 rareData->initializeObjectAllocationProfile(vm, this->globalObject(), prototype, inlineCapacity, this);
188
189 // A DFG compilation thread may be trying to read the rare data
190 // We want to ensure that it sees it properly allocated
191 WTF::storeStoreFence();
192
193 m_rareData.set(vm, this, rareData);
194 return m_rareData.get();
195}
196
197FunctionRareData* JSFunction::initializeRareData(JSGlobalObject* globalObject, size_t inlineCapacity)
198{
199 ASSERT(!!m_rareData);
200 ASSERT(canUseAllocationProfile());
201 VM& vm = globalObject->vm();
202 JSObject* prototype = prototypeForConstruction(vm, globalObject);
203 m_rareData->initializeObjectAllocationProfile(vm, this->globalObject(), prototype, inlineCapacity, this);
204 return m_rareData.get();
205}
206
207String JSFunction::name(VM& vm)
208{
209 if (isHostFunction()) {
210 NativeExecutable* executable = jsCast<NativeExecutable*>(this->executable());
211 return executable->name();
212 }
213 const Identifier identifier = jsExecutable()->name();
214 if (identifier == vm.propertyNames->builtinNames().starDefaultPrivateName())
215 return emptyString();
216 return identifier.string();
217}
218
219String JSFunction::displayName(VM& vm)
220{
221 JSValue displayName = getDirect(vm, vm.propertyNames->displayName);
222
223 if (displayName && isJSString(displayName))
224 return asString(displayName)->tryGetValue();
225
226 return String();
227}
228
229const String JSFunction::calculatedDisplayName(VM& vm)
230{
231 const String explicitName = displayName(vm);
232
233 if (!explicitName.isEmpty())
234 return explicitName;
235
236 const String actualName = name(vm);
237 if (!actualName.isEmpty() || isHostOrBuiltinFunction())
238 return actualName;
239
240 return jsExecutable()->ecmaName().string();
241}
242
243const SourceCode* JSFunction::sourceCode() const
244{
245 if (isHostOrBuiltinFunction())
246 return 0;
247 return &jsExecutable()->source();
248}
249
250void JSFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
251{
252 JSFunction* thisObject = jsCast<JSFunction*>(cell);
253 ASSERT_GC_OBJECT_INHERITS(thisObject, info());
254 Base::visitChildren(thisObject, visitor);
255
256 visitor.append(thisObject->m_executable);
257 visitor.append(thisObject->m_rareData);
258}
259
260CallType JSFunction::getCallData(JSCell* cell, CallData& callData)
261{
262 JSFunction* thisObject = jsCast<JSFunction*>(cell);
263 if (thisObject->isHostFunction()) {
264 callData.native.function = thisObject->nativeFunction();
265 return CallType::Host;
266 }
267 callData.js.functionExecutable = thisObject->jsExecutable();
268 callData.js.scope = thisObject->scope();
269 return CallType::JS;
270}
271
272class RetrieveArgumentsFunctor {
273public:
274 RetrieveArgumentsFunctor(VM& vm, JSFunction* functionObj)
275 : m_vm(vm)
276 , m_targetCallee(functionObj)
277 , m_result(jsNull())
278 {
279 }
280
281 JSValue result() const { return m_result; }
282
283 StackVisitor::Status operator()(StackVisitor& visitor) const
284 {
285 if (!visitor->callee().isCell())
286 return StackVisitor::Continue;
287
288 JSCell* callee = visitor->callee().asCell();
289 if (callee != m_targetCallee)
290 return StackVisitor::Continue;
291
292 m_result = JSValue(visitor->createArguments(m_vm));
293 return StackVisitor::Done;
294 }
295
296private:
297 VM& m_vm;
298 JSObject* m_targetCallee;
299 mutable JSValue m_result;
300};
301
302static JSValue retrieveArguments(VM& vm, CallFrame* callFrame, JSFunction* functionObj)
303{
304 RetrieveArgumentsFunctor functor(vm, functionObj);
305 if (callFrame)
306 callFrame->iterate(vm, functor);
307 return functor.result();
308}
309
310EncodedJSValue JSFunction::argumentsGetter(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName)
311{
312 VM& vm = globalObject->vm();
313 JSFunction* thisObj = jsCast<JSFunction*>(JSValue::decode(thisValue));
314 ASSERT(!thisObj->isHostFunction());
315
316 return JSValue::encode(retrieveArguments(vm, vm.topCallFrame, thisObj));
317}
318
319class RetrieveCallerFunctionFunctor {
320public:
321 RetrieveCallerFunctionFunctor(JSFunction* functionObj)
322 : m_targetCallee(functionObj)
323 , m_hasFoundFrame(false)
324 , m_hasSkippedToCallerFrame(false)
325 , m_result(jsNull())
326 {
327 }
328
329 JSValue result() const { return m_result; }
330
331 StackVisitor::Status operator()(StackVisitor& visitor) const
332 {
333 if (!visitor->callee().isCell())
334 return StackVisitor::Continue;
335
336 JSCell* callee = visitor->callee().asCell();
337
338 if (callee && callee->inherits<JSBoundFunction>(callee->vm()))
339 return StackVisitor::Continue;
340
341 if (!m_hasFoundFrame && (callee != m_targetCallee))
342 return StackVisitor::Continue;
343
344 m_hasFoundFrame = true;
345 if (!m_hasSkippedToCallerFrame) {
346 m_hasSkippedToCallerFrame = true;
347 return StackVisitor::Continue;
348 }
349
350 if (callee)
351 m_result = callee;
352 return StackVisitor::Done;
353 }
354
355private:
356 JSObject* m_targetCallee;
357 mutable bool m_hasFoundFrame;
358 mutable bool m_hasSkippedToCallerFrame;
359 mutable JSValue m_result;
360};
361
362static JSValue retrieveCallerFunction(VM& vm, CallFrame* callFrame, JSFunction* functionObj)
363{
364 RetrieveCallerFunctionFunctor functor(functionObj);
365 if (callFrame)
366 callFrame->iterate(vm, functor);
367 return functor.result();
368}
369
370EncodedJSValue JSFunction::callerGetter(JSGlobalObject* globalObject, EncodedJSValue thisValue, PropertyName)
371{
372 VM& vm = globalObject->vm();
373 auto scope = DECLARE_THROW_SCOPE(vm);
374
375 JSFunction* thisObj = jsCast<JSFunction*>(JSValue::decode(thisValue));
376 ASSERT(!thisObj->isHostFunction());
377 JSValue caller = retrieveCallerFunction(vm, vm.topCallFrame, thisObj);
378
379 // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller.
380 if (!caller.isObject() || !asObject(caller)->inherits<JSFunction>(vm)) {
381 // It isn't a JSFunction, but if it is a JSCallee from a program or eval call or an internal constructor, return null.
382 if (jsDynamicCast<JSCallee*>(vm, caller) || jsDynamicCast<InternalFunction*>(vm, caller))
383 return JSValue::encode(jsNull());
384 return JSValue::encode(caller);
385 }
386 JSFunction* function = jsCast<JSFunction*>(caller);
387
388 // Firefox returns null for native code callers, so we match that behavior.
389 if (function->isHostOrBuiltinFunction())
390 return JSValue::encode(jsNull());
391 SourceParseMode parseMode = function->jsExecutable()->parseMode();
392 switch (parseMode) {
393 case SourceParseMode::GeneratorBodyMode:
394 case SourceParseMode::AsyncGeneratorBodyMode:
395 return JSValue::encode(throwTypeError(globalObject, scope, "Function.caller used to retrieve generator body"_s));
396 case SourceParseMode::AsyncFunctionBodyMode:
397 case SourceParseMode::AsyncArrowFunctionBodyMode:
398 return JSValue::encode(throwTypeError(globalObject, scope, "Function.caller used to retrieve async function body"_s));
399 case SourceParseMode::NormalFunctionMode:
400 case SourceParseMode::GeneratorWrapperFunctionMode:
401 case SourceParseMode::GetterMode:
402 case SourceParseMode::SetterMode:
403 case SourceParseMode::MethodMode:
404 case SourceParseMode::ArrowFunctionMode:
405 case SourceParseMode::AsyncFunctionMode:
406 case SourceParseMode::AsyncMethodMode:
407 case SourceParseMode::AsyncArrowFunctionMode:
408 case SourceParseMode::ProgramMode:
409 case SourceParseMode::ModuleAnalyzeMode:
410 case SourceParseMode::ModuleEvaluateMode:
411 case SourceParseMode::AsyncGeneratorWrapperFunctionMode:
412 case SourceParseMode::AsyncGeneratorWrapperMethodMode:
413 case SourceParseMode::GeneratorWrapperMethodMode:
414 if (!function->jsExecutable()->isStrictMode())
415 return JSValue::encode(caller);
416 return JSValue::encode(throwTypeError(globalObject, scope, "Function.caller used to retrieve strict caller"_s));
417 }
418 RELEASE_ASSERT_NOT_REACHED();
419}
420
421bool JSFunction::getOwnPropertySlot(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, PropertySlot& slot)
422{
423 VM& vm = globalObject->vm();
424 JSFunction* thisObject = jsCast<JSFunction*>(object);
425 if (thisObject->isHostOrBuiltinFunction()) {
426 thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
427 return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
428 }
429
430 if (propertyName == vm.propertyNames->prototype && thisObject->jsExecutable()->hasPrototypeProperty() && !thisObject->jsExecutable()->isClassConstructorFunction()) {
431 // NOTE: class constructors define the prototype property in bytecode using
432 // defineOwnProperty, which ends up calling into this code (see our defineOwnProperty
433 // implementation below). The bytecode will end up doing the proper definition
434 // with the property being non-writable/non-configurable. However, we must ignore
435 // the initial materialization of the property so that the defineOwnProperty call
436 // from bytecode succeeds. Otherwise, the materialization here would prevent the
437 // defineOwnProperty from being able to overwrite the property.
438 unsigned attributes;
439 PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes);
440 if (!isValidOffset(offset)) {
441 JSObject* prototype = nullptr;
442 if (isGeneratorWrapperParseMode(thisObject->jsExecutable()->parseMode())) {
443 // Unlike function instances, the object that is the value of the a GeneratorFunction's prototype
444 // property does not have a constructor property whose value is the GeneratorFunction instance.
445 // https://tc39.github.io/ecma262/#sec-generatorfunction-instances-prototype
446 prototype = constructEmptyObject(globalObject, thisObject->globalObject()->generatorPrototype());
447 } else if (isAsyncGeneratorWrapperParseMode(thisObject->jsExecutable()->parseMode()))
448 prototype = constructEmptyObject(globalObject, thisObject->globalObject()->asyncGeneratorPrototype());
449 else {
450 prototype = constructEmptyObject(globalObject);
451 prototype->putDirect(vm, vm.propertyNames->constructor, thisObject, static_cast<unsigned>(PropertyAttribute::DontEnum));
452 }
453
454 thisObject->putDirect(vm, vm.propertyNames->prototype, prototype, PropertyAttribute::DontDelete | PropertyAttribute::DontEnum);
455 offset = thisObject->getDirectOffset(vm, vm.propertyNames->prototype, attributes);
456 ASSERT(isValidOffset(offset));
457 }
458 slot.setValue(thisObject, attributes, thisObject->getDirect(offset), offset);
459 }
460
461 if (propertyName == vm.propertyNames->arguments) {
462 if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
463 return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
464
465 slot.setCacheableCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete, argumentsGetter);
466 return true;
467
468 } else if (propertyName == vm.propertyNames->caller) {
469 if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
470 return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
471
472 slot.setCacheableCustom(thisObject, PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::DontDelete, callerGetter);
473 return true;
474 }
475
476 thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
477
478 return Base::getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
479}
480
481void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, JSGlobalObject* globalObject, PropertyNameArray& propertyNames, EnumerationMode mode)
482{
483 JSFunction* thisObject = jsCast<JSFunction*>(object);
484 VM& vm = globalObject->vm();
485 if (mode.includeDontEnumProperties()) {
486 if (!thisObject->isHostOrBuiltinFunction()) {
487 // Make sure prototype has been reified.
488 PropertySlot slot(thisObject, PropertySlot::InternalMethodType::VMInquiry);
489 thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, globalObject, vm.propertyNames->prototype, slot);
490
491 if (thisObject->jsExecutable()->hasCallerAndArgumentsProperties()) {
492 propertyNames.add(vm.propertyNames->arguments);
493 propertyNames.add(vm.propertyNames->caller);
494 }
495 if (!thisObject->hasReifiedLength())
496 propertyNames.add(vm.propertyNames->length);
497 if (!thisObject->hasReifiedName())
498 propertyNames.add(vm.propertyNames->name);
499 } else {
500 if (thisObject->isBuiltinFunction() && !thisObject->hasReifiedLength())
501 propertyNames.add(vm.propertyNames->length);
502 if ((thisObject->isBuiltinFunction() || thisObject->inherits<JSBoundFunction>(vm)) && !thisObject->hasReifiedName())
503 propertyNames.add(vm.propertyNames->name);
504 }
505 }
506 Base::getOwnNonIndexPropertyNames(thisObject, globalObject, propertyNames, mode);
507}
508
509bool JSFunction::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
510{
511 VM& vm = globalObject->vm();
512 auto scope = DECLARE_THROW_SCOPE(vm);
513
514 JSFunction* thisObject = jsCast<JSFunction*>(cell);
515
516 if (UNLIKELY(isThisValueAltered(slot, thisObject)))
517 RELEASE_AND_RETURN(scope, ordinarySetSlow(globalObject, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode()));
518
519
520 if (thisObject->isHostOrBuiltinFunction()) {
521 PropertyStatus propertyType = thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
522 if (isLazy(propertyType))
523 slot.disableCaching();
524 RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
525 }
526
527 if (propertyName == vm.propertyNames->prototype) {
528 slot.disableCaching();
529 // Make sure prototype has been reified, such that it can only be overwritten
530 // following the rules set out in ECMA-262 8.12.9.
531 PropertySlot getSlot(thisObject, PropertySlot::InternalMethodType::VMInquiry);
532 thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, globalObject, propertyName, getSlot);
533 if (thisObject->m_rareData)
534 thisObject->m_rareData->clear("Store to prototype property of a function");
535 RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
536 }
537
538 if (propertyName == vm.propertyNames->arguments || propertyName == vm.propertyNames->caller) {
539 if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
540 RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
541
542 slot.disableCaching();
543 return typeError(globalObject, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
544 }
545 PropertyStatus propertyType = thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
546 if (isLazy(propertyType))
547 slot.disableCaching();
548 RELEASE_AND_RETURN(scope, Base::put(thisObject, globalObject, propertyName, value, slot));
549}
550
551bool JSFunction::deleteProperty(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName)
552{
553 VM& vm = globalObject->vm();
554 JSFunction* thisObject = jsCast<JSFunction*>(cell);
555 if (thisObject->isHostOrBuiltinFunction())
556 thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
557 else if (vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable) {
558 // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
559 FunctionExecutable* executable = thisObject->jsExecutable();
560
561 if ((propertyName == vm.propertyNames->caller || propertyName == vm.propertyNames->arguments) && executable->hasCallerAndArgumentsProperties())
562 return false;
563
564 if (propertyName == vm.propertyNames->prototype && !executable->isArrowFunction())
565 return false;
566
567 thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
568 }
569
570 return Base::deleteProperty(thisObject, globalObject, propertyName);
571}
572
573bool JSFunction::defineOwnProperty(JSObject* object, JSGlobalObject* globalObject, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
574{
575 VM& vm = globalObject->vm();
576 auto scope = DECLARE_THROW_SCOPE(vm);
577
578 JSFunction* thisObject = jsCast<JSFunction*>(object);
579 if (thisObject->isHostOrBuiltinFunction()) {
580 thisObject->reifyLazyPropertyForHostOrBuiltinIfNeeded(vm, globalObject, propertyName);
581 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
582 }
583
584 if (propertyName == vm.propertyNames->prototype) {
585 // Make sure prototype has been reified, such that it can only be overwritten
586 // following the rules set out in ECMA-262 8.12.9.
587 PropertySlot slot(thisObject, PropertySlot::InternalMethodType::VMInquiry);
588 thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, globalObject, propertyName, slot);
589 if (thisObject->m_rareData)
590 thisObject->m_rareData->clear("Store to prototype property of a function");
591 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
592 }
593
594 bool valueCheck;
595 if (propertyName == vm.propertyNames->arguments) {
596 if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
597 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
598
599 valueCheck = !descriptor.value();
600 if (!valueCheck) {
601 valueCheck = sameValue(globalObject, descriptor.value(), retrieveArguments(vm, vm.topCallFrame, thisObject));
602 RETURN_IF_EXCEPTION(scope, false);
603 }
604 } else if (propertyName == vm.propertyNames->caller) {
605 if (!thisObject->jsExecutable()->hasCallerAndArgumentsProperties())
606 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
607
608 valueCheck = !descriptor.value();
609 if (!valueCheck) {
610 valueCheck = sameValue(globalObject, descriptor.value(), retrieveCallerFunction(vm, vm.topCallFrame, thisObject));
611 RETURN_IF_EXCEPTION(scope, false);
612 }
613 } else {
614 thisObject->reifyLazyPropertyIfNeeded(vm, globalObject, propertyName);
615 RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, throwException));
616 }
617
618 if (descriptor.configurablePresent() && descriptor.configurable())
619 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeConfigurabilityError);
620 if (descriptor.enumerablePresent() && descriptor.enumerable())
621 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeEnumerabilityError);
622 if (descriptor.isAccessorDescriptor())
623 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeAccessMechanismError);
624 if (descriptor.writablePresent() && descriptor.writable())
625 return typeError(globalObject, scope, throwException, UnconfigurablePropertyChangeWritabilityError);
626 if (!valueCheck)
627 return typeError(globalObject, scope, throwException, ReadonlyPropertyChangeError);
628 return true;
629}
630
631// ECMA 13.2.2 [[Construct]]
632ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData)
633{
634 JSFunction* thisObject = jsCast<JSFunction*>(cell);
635
636 if (thisObject->isHostFunction()) {
637 if (thisObject->nativeConstructor() == callHostFunctionAsConstructor)
638 return ConstructType::None;
639 constructData.native.function = thisObject->nativeConstructor();
640 return ConstructType::Host;
641 }
642
643 FunctionExecutable* functionExecutable = thisObject->jsExecutable();
644 if (functionExecutable->constructAbility() == ConstructAbility::CannotConstruct)
645 return ConstructType::None;
646
647 constructData.js.functionExecutable = functionExecutable;
648 constructData.js.scope = thisObject->scope();
649 return ConstructType::JS;
650}
651
652String getCalculatedDisplayName(VM& vm, JSObject* object)
653{
654#if ENABLE(WEBASSEMBLY)
655 if (jsDynamicCast<JSToWasmICCallee*>(vm, object))
656 return "wasm-stub"_s;
657#endif
658
659 if (!jsDynamicCast<JSFunction*>(vm, object) && !jsDynamicCast<InternalFunction*>(vm, object))
660 return emptyString();
661
662 Structure* structure = object->structure(vm);
663 unsigned attributes;
664 // This function may be called when the mutator isn't running and we are lazily generating a stack trace.
665 PropertyOffset offset = structure->getConcurrently(vm.propertyNames->displayName.impl(), attributes);
666 if (offset != invalidOffset && !(attributes & (PropertyAttribute::Accessor | PropertyAttribute::CustomAccessorOrValue))) {
667 JSValue displayName = object->getDirect(offset);
668 if (displayName && displayName.isString())
669 return asString(displayName)->tryGetValue();
670 }
671
672 if (auto* function = jsDynamicCast<JSFunction*>(vm, object)) {
673 const String actualName = function->name(vm);
674 if (!actualName.isEmpty() || function->isHostOrBuiltinFunction())
675 return actualName;
676
677 return function->jsExecutable()->ecmaName().string();
678 }
679 if (auto* function = jsDynamicCast<InternalFunction*>(vm, object))
680 return function->name();
681
682
683 return emptyString();
684}
685
686void JSFunction::setFunctionName(JSGlobalObject* globalObject, JSValue value)
687{
688 VM& vm = globalObject->vm();
689 auto scope = DECLARE_THROW_SCOPE(vm);
690
691 // The "name" property may have been already been defined as part of a property list in an
692 // object literal (and therefore reified).
693 if (hasReifiedName())
694 return;
695
696 ASSERT(!isHostFunction());
697 ASSERT(jsExecutable()->ecmaName().isNull());
698 String name;
699 if (value.isSymbol()) {
700 PrivateName privateName = asSymbol(value)->privateName();
701 SymbolImpl& uid = privateName.uid();
702 if (uid.isNullSymbol())
703 name = emptyString();
704 else
705 name = makeString('[', String(&uid), ']');
706 } else {
707 JSString* jsStr = value.toString(globalObject);
708 RETURN_IF_EXCEPTION(scope, void());
709 name = jsStr->value(globalObject);
710 RETURN_IF_EXCEPTION(scope, void());
711 }
712 reifyName(vm, globalObject, name);
713}
714
715void JSFunction::reifyLength(VM& vm)
716{
717 FunctionRareData* rareData = this->rareData(vm);
718
719 ASSERT(!hasReifiedLength());
720 ASSERT(!isHostFunction());
721 JSValue initialValue = jsNumber(jsExecutable()->parameterCount());
722 unsigned initialAttributes = PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
723 const Identifier& identifier = vm.propertyNames->length;
724 rareData->setHasReifiedLength();
725 putDirect(vm, identifier, initialValue, initialAttributes);
726}
727
728void JSFunction::reifyName(VM& vm, JSGlobalObject* globalObject)
729{
730 const Identifier& ecmaName = jsExecutable()->ecmaName();
731 String name;
732 // https://tc39.github.io/ecma262/#sec-exports-runtime-semantics-evaluation
733 // When the ident is "*default*", we need to set "default" for the ecma name.
734 // This "*default*" name is never shown to users.
735 if (ecmaName == vm.propertyNames->builtinNames().starDefaultPrivateName())
736 name = vm.propertyNames->defaultKeyword.string();
737 else
738 name = ecmaName.string();
739 reifyName(vm, globalObject, name);
740}
741
742void JSFunction::reifyName(VM& vm, JSGlobalObject* globalObject, String name)
743{
744 FunctionRareData* rareData = this->rareData(vm);
745
746 ASSERT(!hasReifiedName());
747 ASSERT(!isHostFunction());
748 unsigned initialAttributes = PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
749 const Identifier& propID = vm.propertyNames->name;
750
751 if (globalObject->needsSiteSpecificQuirks()) {
752 auto illegalCharMatcher = [] (UChar ch) -> bool {
753 return ch == ' ' || ch == '|';
754 };
755 if (name.find(illegalCharMatcher) != notFound)
756 name = String();
757 }
758
759 if (jsExecutable()->isGetter())
760 name = makeString("get ", name);
761 else if (jsExecutable()->isSetter())
762 name = makeString("set ", name);
763
764 rareData->setHasReifiedName();
765 putDirect(vm, propID, jsString(vm, name), initialAttributes);
766}
767
768JSFunction::PropertyStatus JSFunction::reifyLazyPropertyIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
769{
770 if (isHostOrBuiltinFunction())
771 return PropertyStatus::Eager;
772 PropertyStatus lazyLength = reifyLazyLengthIfNeeded(vm, globalObject, propertyName);
773 if (isLazy(lazyLength))
774 return lazyLength;
775 PropertyStatus lazyName = reifyLazyNameIfNeeded(vm, globalObject, propertyName);
776 if (isLazy(lazyName))
777 return lazyName;
778 return PropertyStatus::Eager;
779}
780
781JSFunction::PropertyStatus JSFunction::reifyLazyPropertyForHostOrBuiltinIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
782{
783 ASSERT(isHostOrBuiltinFunction());
784 if (isBuiltinFunction()) {
785 PropertyStatus lazyLength = reifyLazyLengthIfNeeded(vm, globalObject, propertyName);
786 if (isLazy(lazyLength))
787 return lazyLength;
788 }
789 return reifyLazyBoundNameIfNeeded(vm, globalObject, propertyName);
790}
791
792JSFunction::PropertyStatus JSFunction::reifyLazyLengthIfNeeded(VM& vm, JSGlobalObject*, PropertyName propertyName)
793{
794 if (propertyName == vm.propertyNames->length) {
795 if (!hasReifiedLength()) {
796 reifyLength(vm);
797 return PropertyStatus::Reified;
798 }
799 return PropertyStatus::Lazy;
800 }
801 return PropertyStatus::Eager;
802}
803
804JSFunction::PropertyStatus JSFunction::reifyLazyNameIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
805{
806 if (propertyName == vm.propertyNames->name) {
807 if (!hasReifiedName()) {
808 reifyName(vm, globalObject);
809 return PropertyStatus::Reified;
810 }
811 return PropertyStatus::Lazy;
812 }
813 return PropertyStatus::Eager;
814}
815
816JSFunction::PropertyStatus JSFunction::reifyLazyBoundNameIfNeeded(VM& vm, JSGlobalObject* globalObject, PropertyName propertyName)
817{
818 const Identifier& nameIdent = vm.propertyNames->name;
819 if (propertyName != nameIdent)
820 return PropertyStatus::Eager;
821
822 if (hasReifiedName())
823 return PropertyStatus::Lazy;
824
825 if (isBuiltinFunction())
826 reifyName(vm, globalObject);
827 else if (this->inherits<JSBoundFunction>(vm)) {
828 FunctionRareData* rareData = this->rareData(vm);
829 const String& name = static_cast<NativeExecutable*>(m_executable.get())->name();
830 JSString* string = !name ? jsEmptyString(vm) : jsString(vm, makeString("bound ", name));
831 unsigned initialAttributes = PropertyAttribute::DontEnum | PropertyAttribute::ReadOnly;
832 rareData->setHasReifiedName();
833 putDirect(vm, nameIdent, string, initialAttributes);
834 }
835 return PropertyStatus::Reified;
836}
837
838#if !ASSERT_DISABLED
839void JSFunction::assertTypeInfoFlagInvariants()
840{
841 // If you change this, you'll need to update speculationFromClassInfo.
842 const ClassInfo* info = classInfo(vm());
843 if (!(inlineTypeFlags() & ImplementsDefaultHasInstance))
844 RELEASE_ASSERT(info == JSBoundFunction::info());
845 else
846 RELEASE_ASSERT(info != JSBoundFunction::info());
847}
848#endif
849
850} // namespace JSC
851