]>
Commit | Line | Data |
---|---|---|
9dae56ea A |
1 | /* |
2 | * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) | |
3 | * Copyright (C) 2001 Peter Kelly (pmk@post.com) | |
f9bf01c6 | 4 | * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
9dae56ea A |
5 | * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) |
6 | * Copyright (C) 2007 Maks Orlovich | |
7 | * | |
8 | * This library is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Library General Public | |
10 | * License as published by the Free Software Foundation; either | |
11 | * version 2 of the License, or (at your option) any later version. | |
12 | * | |
13 | * This library is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * Library General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU Library General Public License | |
19 | * along with this library; see the file COPYING.LIB. If not, write to | |
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
21 | * Boston, MA 02110-1301, USA. | |
22 | * | |
23 | */ | |
24 | ||
25 | #include "config.h" | |
26 | #include "JSFunction.h" | |
27 | ||
81345200 | 28 | #include "Arguments.h" |
9dae56ea A |
29 | #include "CodeBlock.h" |
30 | #include "CommonIdentifiers.h" | |
31 | #include "CallFrame.h" | |
14957cd0 | 32 | #include "ExceptionHelpers.h" |
9dae56ea | 33 | #include "FunctionPrototype.h" |
6fe7ccc8 A |
34 | #include "GetterSetter.h" |
35 | #include "JSArray.h" | |
81345200 A |
36 | #include "JSBoundFunction.h" |
37 | #include "JSFunctionInlines.h" | |
9dae56ea | 38 | #include "JSGlobalObject.h" |
81345200 | 39 | #include "JSNameScope.h" |
14957cd0 | 40 | #include "JSNotAnObject.h" |
9dae56ea | 41 | #include "Interpreter.h" |
93a37866 | 42 | #include "ObjectConstructor.h" |
9dae56ea | 43 | #include "ObjectPrototype.h" |
81345200 | 44 | #include "JSCInlines.h" |
9dae56ea A |
45 | #include "Parser.h" |
46 | #include "PropertyNameArray.h" | |
81345200 | 47 | #include "StackVisitor.h" |
9dae56ea A |
48 | |
49 | namespace JSC { | |
81345200 | 50 | |
14957cd0 A |
51 | EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) |
52 | { | |
53 | return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); | |
54 | } | |
9dae56ea | 55 | |
6fe7ccc8 | 56 | const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFunction) }; |
9dae56ea | 57 | |
f9bf01c6 A |
58 | bool JSFunction::isHostFunctionNonInline() const |
59 | { | |
60 | return isHostFunction(); | |
61 | } | |
62 | ||
81345200 | 63 | JSFunction* JSFunction::create(VM& vm, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor) |
f9bf01c6 | 64 | { |
6fe7ccc8 A |
65 | NativeExecutable* executable; |
66 | #if !ENABLE(JIT) | |
67 | UNUSED_PARAM(intrinsic); | |
68 | #else | |
81345200 | 69 | if (intrinsic != NoIntrinsic && vm.canUseJIT()) { |
6fe7ccc8 | 70 | ASSERT(nativeConstructor == callHostFunctionAsConstructor); |
81345200 | 71 | executable = vm.getHostFunction(nativeFunction, intrinsic); |
6fe7ccc8 A |
72 | } else |
73 | #endif | |
81345200 | 74 | executable = vm.getHostFunction(nativeFunction, nativeConstructor); |
6fe7ccc8 | 75 | |
81345200 | 76 | JSFunction* function = new (NotNull, allocateCell<JSFunction>(vm.heap)) JSFunction(vm, globalObject, globalObject->functionStructure()); |
6fe7ccc8 | 77 | // Can't do this during initialization because getHostFunction might do a GC allocation. |
81345200 | 78 | function->finishCreation(vm, executable, length, name); |
6fe7ccc8 | 79 | return function; |
f9bf01c6 A |
80 | } |
81 | ||
93a37866 | 82 | void JSFunction::destroy(JSCell* cell) |
ba379fdc | 83 | { |
93a37866 | 84 | static_cast<JSFunction*>(cell)->JSFunction::~JSFunction(); |
ba379fdc A |
85 | } |
86 | ||
81345200 A |
87 | JSFunction::JSFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure) |
88 | : Base(vm, structure) | |
93a37866 | 89 | , m_executable() |
81345200 | 90 | , m_scope(vm, this, globalObject) |
93a37866 A |
91 | // We initialize blind so that changes to the prototype after function creation but before |
92 | // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the | |
93 | // watchpoint will start watching and any changes will both force deoptimization and disable | |
94 | // future attempts to optimize. This is necessary because we are guaranteed that the | |
95 | // allocation profile is changed exactly once prior to optimizations kicking in. We could be | |
96 | // smarter and count the number of times the prototype is clobbered and only optimize if it | |
97 | // was clobbered exactly once, but that seems like overkill. In almost all cases it will be | |
98 | // clobbered once, and if it's clobbered more than once, that will probably only occur | |
99 | // before we started optimizing, anyway. | |
81345200 A |
100 | , m_allocationProfileWatchpoint(ClearWatchpoint) |
101 | { | |
102 | } | |
103 | ||
104 | void JSFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name) | |
9dae56ea | 105 | { |
81345200 A |
106 | Base::finishCreation(vm); |
107 | ASSERT(inherits(info())); | |
108 | m_executable.set(vm, this, executable); | |
109 | putDirect(vm, vm.propertyNames->name, jsString(&vm, name), DontDelete | ReadOnly | DontEnum); | |
110 | putDirect(vm, vm.propertyNames->length, jsNumber(length), DontDelete | ReadOnly | DontEnum); | |
111 | } | |
112 | ||
113 | void JSFunction::addNameScopeIfNeeded(VM& vm) | |
114 | { | |
115 | FunctionExecutable* executable = jsCast<FunctionExecutable*>(m_executable.get()); | |
116 | if (!functionNameIsInScope(executable->name(), executable->functionMode())) | |
117 | return; | |
118 | if (!functionNameScopeIsDynamic(executable->usesEval(), executable->isStrictMode())) | |
119 | return; | |
120 | m_scope.set(vm, this, JSNameScope::create(vm, m_scope->globalObject(), executable->name(), this, ReadOnly | DontDelete, m_scope.get())); | |
9dae56ea A |
121 | } |
122 | ||
81345200 | 123 | JSFunction* JSFunction::createBuiltinFunction(VM& vm, FunctionExecutable* executable, JSGlobalObject* globalObject) |
9dae56ea | 124 | { |
81345200 A |
125 | JSFunction* function = create(vm, executable, globalObject); |
126 | function->putDirect(vm, vm.propertyNames->name, jsString(&vm, executable->name().string()), DontDelete | ReadOnly | DontEnum); | |
127 | function->putDirect(vm, vm.propertyNames->length, jsNumber(executable->parameterCount()), DontDelete | ReadOnly | DontEnum); | |
128 | return function; | |
14957cd0 | 129 | } |
f9bf01c6 | 130 | |
93a37866 | 131 | ObjectAllocationProfile* JSFunction::createAllocationProfile(ExecState* exec, size_t inlineCapacity) |
14957cd0 | 132 | { |
93a37866 A |
133 | VM& vm = exec->vm(); |
134 | JSObject* prototype = jsDynamicCast<JSObject*>(get(exec, vm.propertyNames->prototype)); | |
135 | if (!prototype) | |
136 | prototype = globalObject()->objectPrototype(); | |
137 | m_allocationProfile.initialize(globalObject()->vm(), this, prototype, inlineCapacity); | |
138 | return &m_allocationProfile; | |
9dae56ea A |
139 | } |
140 | ||
93a37866 | 141 | String JSFunction::name(ExecState* exec) |
9dae56ea | 142 | { |
93a37866 | 143 | return get(exec, exec->vm().propertyNames->name).toWTFString(exec); |
14957cd0 A |
144 | } |
145 | ||
93a37866 | 146 | String JSFunction::displayName(ExecState* exec) |
14957cd0 | 147 | { |
93a37866 | 148 | JSValue displayName = getDirect(exec->vm(), exec->vm().propertyNames->displayName); |
14957cd0 | 149 | |
6fe7ccc8 | 150 | if (displayName && isJSString(displayName)) |
14957cd0 A |
151 | return asString(displayName)->tryGetValue(); |
152 | ||
93a37866 | 153 | return String(); |
14957cd0 A |
154 | } |
155 | ||
93a37866 | 156 | const String JSFunction::calculatedDisplayName(ExecState* exec) |
14957cd0 | 157 | { |
93a37866 | 158 | const String explicitName = displayName(exec); |
14957cd0 A |
159 | |
160 | if (!explicitName.isEmpty()) | |
161 | return explicitName; | |
162 | ||
93a37866 | 163 | const String actualName = name(exec); |
81345200 | 164 | if (!actualName.isEmpty() || isHostOrBuiltinFunction()) |
6fe7ccc8 A |
165 | return actualName; |
166 | ||
93a37866 | 167 | return jsExecutable()->inferredName().string(); |
6fe7ccc8 A |
168 | } |
169 | ||
170 | const SourceCode* JSFunction::sourceCode() const | |
171 | { | |
81345200 | 172 | if (isHostOrBuiltinFunction()) |
6fe7ccc8 A |
173 | return 0; |
174 | return &jsExecutable()->source(); | |
14957cd0 | 175 | } |
81345200 | 176 | |
6fe7ccc8 | 177 | void JSFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) |
14957cd0 | 178 | { |
6fe7ccc8 | 179 | JSFunction* thisObject = jsCast<JSFunction*>(cell); |
81345200 | 180 | ASSERT_GC_OBJECT_INHERITS(thisObject, info()); |
14957cd0 | 181 | COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); |
6fe7ccc8 A |
182 | ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); |
183 | Base::visitChildren(thisObject, visitor); | |
14957cd0 | 184 | |
93a37866 A |
185 | visitor.append(&thisObject->m_scope); |
186 | visitor.append(&thisObject->m_executable); | |
187 | thisObject->m_allocationProfile.visitAggregate(visitor); | |
9dae56ea A |
188 | } |
189 | ||
6fe7ccc8 | 190 | CallType JSFunction::getCallData(JSCell* cell, CallData& callData) |
9dae56ea | 191 | { |
6fe7ccc8 A |
192 | JSFunction* thisObject = jsCast<JSFunction*>(cell); |
193 | if (thisObject->isHostFunction()) { | |
194 | callData.native.function = thisObject->nativeFunction(); | |
ba379fdc A |
195 | return CallTypeHost; |
196 | } | |
6fe7ccc8 | 197 | callData.js.functionExecutable = thisObject->jsExecutable(); |
93a37866 | 198 | callData.js.scope = thisObject->scope(); |
9dae56ea A |
199 | return CallTypeJS; |
200 | } | |
201 | ||
81345200 A |
202 | class RetrieveArgumentsFunctor { |
203 | public: | |
204 | RetrieveArgumentsFunctor(JSFunction* functionObj) | |
205 | : m_targetCallee(jsDynamicCast<JSObject*>(functionObj)) | |
206 | , m_result(jsNull()) | |
207 | { | |
208 | } | |
209 | ||
210 | JSValue result() const { return m_result; } | |
211 | ||
212 | StackVisitor::Status operator()(StackVisitor& visitor) | |
213 | { | |
214 | JSObject* callee = visitor->callee(); | |
215 | if (callee != m_targetCallee) | |
216 | return StackVisitor::Continue; | |
217 | ||
218 | m_result = JSValue(visitor->createArguments()); | |
219 | return StackVisitor::Done; | |
220 | } | |
221 | ||
222 | private: | |
223 | JSObject* m_targetCallee; | |
224 | JSValue m_result; | |
225 | }; | |
226 | ||
227 | static JSValue retrieveArguments(ExecState* exec, JSFunction* functionObj) | |
228 | { | |
229 | RetrieveArgumentsFunctor functor(functionObj); | |
230 | exec->iterate(functor); | |
231 | return functor.result(); | |
232 | } | |
233 | ||
234 | EncodedJSValue JSFunction::argumentsGetter(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) | |
9dae56ea | 235 | { |
6fe7ccc8 | 236 | JSFunction* thisObj = jsCast<JSFunction*>(slotBase); |
ba379fdc | 237 | ASSERT(!thisObj->isHostFunction()); |
81345200 A |
238 | |
239 | return JSValue::encode(retrieveArguments(exec, thisObj)); | |
9dae56ea A |
240 | } |
241 | ||
81345200 A |
242 | class RetrieveCallerFunctionFunctor { |
243 | public: | |
244 | RetrieveCallerFunctionFunctor(JSFunction* functionObj) | |
245 | : m_targetCallee(jsDynamicCast<JSObject*>(functionObj)) | |
246 | , m_hasFoundFrame(false) | |
247 | , m_hasSkippedToCallerFrame(false) | |
248 | , m_result(jsNull()) | |
249 | { | |
250 | } | |
251 | ||
252 | JSValue result() const { return m_result; } | |
253 | ||
254 | StackVisitor::Status operator()(StackVisitor& visitor) | |
255 | { | |
256 | JSObject* callee = visitor->callee(); | |
257 | ||
258 | if (callee && callee->inherits(JSBoundFunction::info())) | |
259 | return StackVisitor::Continue; | |
260 | ||
261 | if (!m_hasFoundFrame && (callee != m_targetCallee)) | |
262 | return StackVisitor::Continue; | |
263 | ||
264 | m_hasFoundFrame = true; | |
265 | if (!m_hasSkippedToCallerFrame) { | |
266 | m_hasSkippedToCallerFrame = true; | |
267 | return StackVisitor::Continue; | |
268 | } | |
269 | ||
270 | if (callee) | |
271 | m_result = callee; | |
272 | return StackVisitor::Done; | |
273 | } | |
274 | ||
275 | private: | |
276 | JSObject* m_targetCallee; | |
277 | bool m_hasFoundFrame; | |
278 | bool m_hasSkippedToCallerFrame; | |
279 | JSValue m_result; | |
280 | }; | |
281 | ||
282 | static JSValue retrieveCallerFunction(ExecState* exec, JSFunction* functionObj) | |
283 | { | |
284 | RetrieveCallerFunctionFunctor functor(functionObj); | |
285 | exec->iterate(functor); | |
286 | return functor.result(); | |
287 | } | |
288 | ||
289 | EncodedJSValue JSFunction::callerGetter(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName) | |
9dae56ea | 290 | { |
6fe7ccc8 | 291 | JSFunction* thisObj = jsCast<JSFunction*>(slotBase); |
ba379fdc | 292 | ASSERT(!thisObj->isHostFunction()); |
81345200 | 293 | JSValue caller = retrieveCallerFunction(exec, thisObj); |
6fe7ccc8 A |
294 | |
295 | // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller. | |
81345200 A |
296 | if (!caller.isObject() || !asObject(caller)->inherits(JSFunction::info())) |
297 | return JSValue::encode(caller); | |
6fe7ccc8 | 298 | JSFunction* function = jsCast<JSFunction*>(caller); |
81345200 A |
299 | if (function->isHostOrBuiltinFunction() || !function->jsExecutable()->isStrictMode()) |
300 | return JSValue::encode(caller); | |
301 | return JSValue::encode(throwTypeError(exec, ASCIILiteral("Function.caller used to retrieve strict caller"))); | |
9dae56ea A |
302 | } |
303 | ||
81345200 | 304 | EncodedJSValue JSFunction::lengthGetter(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) |
9dae56ea | 305 | { |
6fe7ccc8 | 306 | JSFunction* thisObj = jsCast<JSFunction*>(slotBase); |
ba379fdc | 307 | ASSERT(!thisObj->isHostFunction()); |
81345200 | 308 | return JSValue::encode(jsNumber(thisObj->jsExecutable()->parameterCount())); |
14957cd0 A |
309 | } |
310 | ||
81345200 | 311 | EncodedJSValue JSFunction::nameGetter(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName) |
93a37866 A |
312 | { |
313 | JSFunction* thisObj = jsCast<JSFunction*>(slotBase); | |
314 | ASSERT(!thisObj->isHostFunction()); | |
81345200 | 315 | return JSValue::encode(thisObj->jsExecutable()->nameValue()); |
93a37866 A |
316 | } |
317 | ||
81345200 | 318 | bool JSFunction::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
14957cd0 | 319 | { |
81345200 A |
320 | JSFunction* thisObject = jsCast<JSFunction*>(object); |
321 | if (thisObject->isHostOrBuiltinFunction()) | |
6fe7ccc8 | 322 | return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); |
ba379fdc | 323 | |
9dae56ea | 324 | if (propertyName == exec->propertyNames().prototype) { |
93a37866 | 325 | VM& vm = exec->vm(); |
81345200 A |
326 | unsigned attributes; |
327 | PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes); | |
93a37866 A |
328 | if (!isValidOffset(offset)) { |
329 | JSObject* prototype = constructEmptyObject(exec); | |
330 | prototype->putDirect(vm, exec->propertyNames().constructor, thisObject, DontEnum); | |
331 | thisObject->putDirect(vm, exec->propertyNames().prototype, prototype, DontDelete | DontEnum); | |
81345200 | 332 | offset = thisObject->getDirectOffset(vm, exec->propertyNames().prototype, attributes); |
93a37866 | 333 | ASSERT(isValidOffset(offset)); |
6fe7ccc8 | 334 | } |
9dae56ea | 335 | |
81345200 | 336 | slot.setValue(thisObject, attributes, thisObject->getDirect(offset), offset); |
9dae56ea A |
337 | } |
338 | ||
339 | if (propertyName == exec->propertyNames().arguments) { | |
6fe7ccc8 A |
340 | if (thisObject->jsExecutable()->isStrictMode()) { |
341 | bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); | |
342 | if (!result) { | |
81345200 | 343 | thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); |
6fe7ccc8 A |
344 | result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); |
345 | ASSERT(result); | |
346 | } | |
347 | return result; | |
14957cd0 | 348 | } |
81345200 | 349 | slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, argumentsGetter); |
9dae56ea A |
350 | return true; |
351 | } | |
352 | ||
353 | if (propertyName == exec->propertyNames().length) { | |
81345200 | 354 | slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, lengthGetter); |
9dae56ea A |
355 | return true; |
356 | } | |
357 | ||
93a37866 | 358 | if (propertyName == exec->propertyNames().name) { |
81345200 | 359 | slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, nameGetter); |
93a37866 A |
360 | return true; |
361 | } | |
362 | ||
9dae56ea | 363 | if (propertyName == exec->propertyNames().caller) { |
6fe7ccc8 A |
364 | if (thisObject->jsExecutable()->isStrictMode()) { |
365 | bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); | |
366 | if (!result) { | |
81345200 | 367 | thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); |
6fe7ccc8 A |
368 | result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); |
369 | ASSERT(result); | |
370 | } | |
371 | return result; | |
14957cd0 | 372 | } |
81345200 | 373 | slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, callerGetter); |
9dae56ea A |
374 | return true; |
375 | } | |
376 | ||
6fe7ccc8 | 377 | return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); |
9dae56ea A |
378 | } |
379 | ||
93a37866 | 380 | void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
f9bf01c6 | 381 | { |
6fe7ccc8 | 382 | JSFunction* thisObject = jsCast<JSFunction*>(object); |
81345200 A |
383 | if (!thisObject->isHostOrBuiltinFunction() && (mode == IncludeDontEnumProperties)) { |
384 | VM& vm = exec->vm(); | |
14957cd0 | 385 | // Make sure prototype has been reified. |
81345200 A |
386 | PropertySlot slot(thisObject); |
387 | thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, exec, vm.propertyNames->prototype, slot); | |
14957cd0 | 388 | |
81345200 A |
389 | propertyNames.add(vm.propertyNames->arguments); |
390 | propertyNames.add(vm.propertyNames->caller); | |
391 | propertyNames.add(vm.propertyNames->length); | |
392 | propertyNames.add(vm.propertyNames->name); | |
f9bf01c6 | 393 | } |
93a37866 | 394 | Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode); |
f9bf01c6 A |
395 | } |
396 | ||
93a37866 | 397 | void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) |
9dae56ea | 398 | { |
6fe7ccc8 | 399 | JSFunction* thisObject = jsCast<JSFunction*>(cell); |
81345200 | 400 | if (thisObject->isHostOrBuiltinFunction()) { |
6fe7ccc8 | 401 | Base::put(thisObject, exec, propertyName, value, slot); |
ba379fdc A |
402 | return; |
403 | } | |
14957cd0 A |
404 | if (propertyName == exec->propertyNames().prototype) { |
405 | // Make sure prototype has been reified, such that it can only be overwritten | |
406 | // following the rules set out in ECMA-262 8.12.9. | |
81345200 A |
407 | PropertySlot slot(thisObject); |
408 | thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, propertyName, slot); | |
93a37866 | 409 | thisObject->m_allocationProfile.clear(); |
81345200 | 410 | thisObject->m_allocationProfileWatchpoint.fireAll(); |
93a37866 | 411 | // Don't allow this to be cached, since a [[Put]] must clear m_allocationProfile. |
81345200 | 412 | PutPropertySlot dontCache(thisObject); |
93a37866 A |
413 | Base::put(thisObject, exec, propertyName, value, dontCache); |
414 | return; | |
14957cd0 | 415 | } |
6fe7ccc8 A |
416 | if (thisObject->jsExecutable()->isStrictMode() && (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().caller)) { |
417 | // This will trigger the property to be reified, if this is not already the case! | |
418 | bool okay = thisObject->hasProperty(exec, propertyName); | |
419 | ASSERT_UNUSED(okay, okay); | |
420 | Base::put(thisObject, exec, propertyName, value, slot); | |
421 | return; | |
14957cd0 | 422 | } |
93a37866 | 423 | if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().name || propertyName == exec->propertyNames().caller) { |
6fe7ccc8 A |
424 | if (slot.isStrictMode()) |
425 | throwTypeError(exec, StrictModeReadonlyPropertyWriteError); | |
9dae56ea | 426 | return; |
6fe7ccc8 A |
427 | } |
428 | Base::put(thisObject, exec, propertyName, value, slot); | |
9dae56ea A |
429 | } |
430 | ||
93a37866 | 431 | bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) |
9dae56ea | 432 | { |
6fe7ccc8 A |
433 | JSFunction* thisObject = jsCast<JSFunction*>(cell); |
434 | // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty. | |
81345200 | 435 | if (!thisObject->isHostOrBuiltinFunction() && !exec->vm().isInDefineOwnProperty() |
6fe7ccc8 A |
436 | && (propertyName == exec->propertyNames().arguments |
437 | || propertyName == exec->propertyNames().length | |
93a37866 | 438 | || propertyName == exec->propertyNames().name |
6fe7ccc8 A |
439 | || propertyName == exec->propertyNames().prototype |
440 | || propertyName == exec->propertyNames().caller)) | |
441 | return false; | |
442 | return Base::deleteProperty(thisObject, exec, propertyName); | |
443 | } | |
444 | ||
81345200 | 445 | bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) |
6fe7ccc8 A |
446 | { |
447 | JSFunction* thisObject = jsCast<JSFunction*>(object); | |
81345200 | 448 | if (thisObject->isHostOrBuiltinFunction()) |
6fe7ccc8 A |
449 | return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); |
450 | ||
451 | if (propertyName == exec->propertyNames().prototype) { | |
452 | // Make sure prototype has been reified, such that it can only be overwritten | |
453 | // following the rules set out in ECMA-262 8.12.9. | |
81345200 A |
454 | PropertySlot slot(thisObject); |
455 | thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, propertyName, slot); | |
93a37866 | 456 | thisObject->m_allocationProfile.clear(); |
81345200 | 457 | thisObject->m_allocationProfileWatchpoint.fireAll(); |
6fe7ccc8 A |
458 | return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); |
459 | } | |
460 | ||
461 | bool valueCheck; | |
462 | if (propertyName == exec->propertyNames().arguments) { | |
463 | if (thisObject->jsExecutable()->isStrictMode()) { | |
81345200 A |
464 | PropertySlot slot(thisObject); |
465 | if (!Base::getOwnPropertySlot(thisObject, exec, propertyName, slot)) | |
466 | thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); | |
6fe7ccc8 A |
467 | return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); |
468 | } | |
81345200 | 469 | valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), retrieveArguments(exec, thisObject)); |
6fe7ccc8 A |
470 | } else if (propertyName == exec->propertyNames().caller) { |
471 | if (thisObject->jsExecutable()->isStrictMode()) { | |
81345200 A |
472 | PropertySlot slot(thisObject); |
473 | if (!Base::getOwnPropertySlot(thisObject, exec, propertyName, slot)) | |
474 | thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); | |
6fe7ccc8 A |
475 | return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); |
476 | } | |
81345200 | 477 | valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), retrieveCallerFunction(exec, thisObject)); |
6fe7ccc8 A |
478 | } else if (propertyName == exec->propertyNames().length) |
479 | valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), jsNumber(thisObject->jsExecutable()->parameterCount())); | |
93a37866 A |
480 | else if (propertyName == exec->propertyNames().name) |
481 | valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), thisObject->jsExecutable()->nameValue()); | |
6fe7ccc8 A |
482 | else |
483 | return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException); | |
484 | ||
485 | if (descriptor.configurablePresent() && descriptor.configurable()) { | |
486 | if (throwException) | |
81345200 | 487 | exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to configurable attribute of unconfigurable property."))); |
6fe7ccc8 A |
488 | return false; |
489 | } | |
490 | if (descriptor.enumerablePresent() && descriptor.enumerable()) { | |
491 | if (throwException) | |
81345200 | 492 | exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."))); |
6fe7ccc8 A |
493 | return false; |
494 | } | |
495 | if (descriptor.isAccessorDescriptor()) { | |
496 | if (throwException) | |
81345200 | 497 | exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); |
6fe7ccc8 A |
498 | return false; |
499 | } | |
500 | if (descriptor.writablePresent() && descriptor.writable()) { | |
501 | if (throwException) | |
81345200 | 502 | exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."))); |
9dae56ea | 503 | return false; |
6fe7ccc8 A |
504 | } |
505 | if (!valueCheck) { | |
506 | if (throwException) | |
81345200 | 507 | exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property."))); |
6fe7ccc8 A |
508 | return false; |
509 | } | |
510 | return true; | |
9dae56ea A |
511 | } |
512 | ||
513 | // ECMA 13.2.2 [[Construct]] | |
6fe7ccc8 | 514 | ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData) |
9dae56ea | 515 | { |
6fe7ccc8 A |
516 | JSFunction* thisObject = jsCast<JSFunction*>(cell); |
517 | if (thisObject->isHostFunction()) { | |
518 | constructData.native.function = thisObject->nativeConstructor(); | |
519 | return ConstructTypeHost; | |
520 | } | |
521 | constructData.js.functionExecutable = thisObject->jsExecutable(); | |
93a37866 | 522 | constructData.js.scope = thisObject->scope(); |
9dae56ea A |
523 | return ConstructTypeJS; |
524 | } | |
6fe7ccc8 | 525 | |
93a37866 | 526 | String getCalculatedDisplayName(CallFrame* callFrame, JSObject* object) |
6fe7ccc8 A |
527 | { |
528 | if (JSFunction* function = jsDynamicCast<JSFunction*>(object)) | |
529 | return function->calculatedDisplayName(callFrame); | |
530 | if (InternalFunction* function = jsDynamicCast<InternalFunction*>(object)) | |
531 | return function->calculatedDisplayName(callFrame); | |
93a37866 | 532 | return ""; |
6fe7ccc8 | 533 | } |
9dae56ea | 534 | |
9dae56ea | 535 | } // namespace JSC |