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