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