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 | |
53 | namespace JSC { |
54 | |
55 | EncodedJSValue 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 | |
62 | const ClassInfo JSFunction::s_info = { "Function" , &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSFunction) }; |
63 | const ClassInfo JSStrictFunction::s_info = { "Function" , &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSStrictFunction) }; |
64 | const ClassInfo JSSloppyFunction::s_info = { "Function" , &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSSloppyFunction) }; |
65 | const ClassInfo JSArrowFunction::s_info = { "Function" , &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSArrowFunction) }; |
66 | |
67 | bool JSFunction::isHostFunctionNonInline() const |
68 | { |
69 | return isHostFunction(); |
70 | } |
71 | |
72 | Structure* 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 | |
83 | JSFunction* JSFunction::create(VM& vm, FunctionExecutable* executable, JSScope* scope) |
84 | { |
85 | return create(vm, executable, scope, selectStructureForNewFuncExp(scope->globalObject(vm), executable)); |
86 | } |
87 | |
88 | JSFunction* 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 | |
95 | JSFunction* 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 | |
105 | JSFunction* 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 | |
115 | JSFunction::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 | |
124 | void JSFunction::finishCreation(VM& vm) |
125 | { |
126 | Base::finishCreation(vm); |
127 | ASSERT(jsDynamicCast<JSFunction*>(vm, this)); |
128 | ASSERT(type() == JSFunctionType); |
129 | } |
130 | |
131 | void 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 | |
143 | FunctionRareData* 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 | |
156 | JSObject* 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 | |
180 | FunctionRareData* 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 | |
197 | FunctionRareData* 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 | |
207 | String 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 | |
219 | String 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 | |
229 | const 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 | |
243 | const SourceCode* JSFunction::sourceCode() const |
244 | { |
245 | if (isHostOrBuiltinFunction()) |
246 | return 0; |
247 | return &jsExecutable()->source(); |
248 | } |
249 | |
250 | void 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 | |
260 | CallType 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 | |
272 | class RetrieveArgumentsFunctor { |
273 | public: |
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 | |
296 | private: |
297 | VM& m_vm; |
298 | JSObject* m_targetCallee; |
299 | mutable JSValue m_result; |
300 | }; |
301 | |
302 | static 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 | |
310 | EncodedJSValue 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 | |
319 | class RetrieveCallerFunctionFunctor { |
320 | public: |
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 | |
355 | private: |
356 | JSObject* m_targetCallee; |
357 | mutable bool m_hasFoundFrame; |
358 | mutable bool m_hasSkippedToCallerFrame; |
359 | mutable JSValue m_result; |
360 | }; |
361 | |
362 | static 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 | |
370 | EncodedJSValue 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 | |
421 | bool 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 | |
481 | void 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 | |
509 | bool 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 | |
551 | bool 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 | |
573 | bool 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]] |
632 | ConstructType 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 | |
652 | String 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 | |
686 | void 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 | |
715 | void 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 | |
728 | void 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 | |
742 | void 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 | |
768 | JSFunction::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 | |
781 | JSFunction::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 | |
792 | JSFunction::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 | |
804 | JSFunction::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 | |
816 | JSFunction::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 |
839 | void 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 | |