]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSFunction.cpp
JavaScriptCore-1218.0.1.tar.gz
[apple/javascriptcore.git] / runtime / JSFunction.cpp
1 /*
2 * Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
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
28 #include "CodeBlock.h"
29 #include "CommonIdentifiers.h"
30 #include "CallFrame.h"
31 #include "ExceptionHelpers.h"
32 #include "FunctionPrototype.h"
33 #include "GetterSetter.h"
34 #include "JSArray.h"
35 #include "JSGlobalObject.h"
36 #include "JSNotAnObject.h"
37 #include "Interpreter.h"
38 #include "ObjectConstructor.h"
39 #include "ObjectPrototype.h"
40 #include "Operations.h"
41 #include "Parser.h"
42 #include "PropertyNameArray.h"
43
44 using namespace WTF;
45 using namespace Unicode;
46
47 namespace JSC {
48 EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec)
49 {
50 return throwVMError(exec, createNotAConstructorError(exec, exec->callee()));
51 }
52
53 const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFunction) };
54
55 bool JSFunction::isHostFunctionNonInline() const
56 {
57 return isHostFunction();
58 }
59
60 JSFunction* JSFunction::create(ExecState* exec, JSGlobalObject* globalObject, int length, const String& name, NativeFunction nativeFunction, Intrinsic intrinsic, NativeFunction nativeConstructor)
61 {
62 NativeExecutable* executable;
63 #if !ENABLE(JIT)
64 UNUSED_PARAM(intrinsic);
65 #else
66 if (intrinsic != NoIntrinsic && exec->vm().canUseJIT()) {
67 ASSERT(nativeConstructor == callHostFunctionAsConstructor);
68 executable = exec->vm().getHostFunction(nativeFunction, intrinsic);
69 } else
70 #endif
71 executable = exec->vm().getHostFunction(nativeFunction, nativeConstructor);
72
73 JSFunction* function = new (NotNull, allocateCell<JSFunction>(*exec->heap())) JSFunction(exec, globalObject, globalObject->functionStructure());
74 // Can't do this during initialization because getHostFunction might do a GC allocation.
75 function->finishCreation(exec, executable, length, name);
76 return function;
77 }
78
79 void JSFunction::destroy(JSCell* cell)
80 {
81 static_cast<JSFunction*>(cell)->JSFunction::~JSFunction();
82 }
83
84 JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure)
85 : Base(exec->vm(), structure)
86 , m_executable()
87 , m_scope(exec->vm(), this, globalObject)
88 // We initialize blind so that changes to the prototype after function creation but before
89 // the optimizer kicks in don't disable optimizations. Once the optimizer kicks in, the
90 // watchpoint will start watching and any changes will both force deoptimization and disable
91 // future attempts to optimize. This is necessary because we are guaranteed that the
92 // allocation profile is changed exactly once prior to optimizations kicking in. We could be
93 // smarter and count the number of times the prototype is clobbered and only optimize if it
94 // was clobbered exactly once, but that seems like overkill. In almost all cases it will be
95 // clobbered once, and if it's clobbered more than once, that will probably only occur
96 // before we started optimizing, anyway.
97 , m_allocationProfileWatchpoint(InitializedBlind)
98 {
99 }
100
101 void JSFunction::finishCreation(ExecState* exec, NativeExecutable* executable, int length, const String& name)
102 {
103 Base::finishCreation(exec->vm());
104 ASSERT(inherits(&s_info));
105 m_executable.set(exec->vm(), this, executable);
106 putDirect(exec->vm(), exec->vm().propertyNames->name, jsString(exec, name), DontDelete | ReadOnly | DontEnum);
107 putDirect(exec->vm(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
108 }
109
110 ObjectAllocationProfile* JSFunction::createAllocationProfile(ExecState* exec, size_t inlineCapacity)
111 {
112 VM& vm = exec->vm();
113 JSObject* prototype = jsDynamicCast<JSObject*>(get(exec, vm.propertyNames->prototype));
114 if (!prototype)
115 prototype = globalObject()->objectPrototype();
116 m_allocationProfile.initialize(globalObject()->vm(), this, prototype, inlineCapacity);
117 return &m_allocationProfile;
118 }
119
120 String JSFunction::name(ExecState* exec)
121 {
122 return get(exec, exec->vm().propertyNames->name).toWTFString(exec);
123 }
124
125 String JSFunction::displayName(ExecState* exec)
126 {
127 JSValue displayName = getDirect(exec->vm(), exec->vm().propertyNames->displayName);
128
129 if (displayName && isJSString(displayName))
130 return asString(displayName)->tryGetValue();
131
132 return String();
133 }
134
135 const String JSFunction::calculatedDisplayName(ExecState* exec)
136 {
137 const String explicitName = displayName(exec);
138
139 if (!explicitName.isEmpty())
140 return explicitName;
141
142 const String actualName = name(exec);
143 if (!actualName.isEmpty() || isHostFunction())
144 return actualName;
145
146 return jsExecutable()->inferredName().string();
147 }
148
149 const SourceCode* JSFunction::sourceCode() const
150 {
151 if (isHostFunction())
152 return 0;
153 return &jsExecutable()->source();
154 }
155
156 void JSFunction::visitChildren(JSCell* cell, SlotVisitor& visitor)
157 {
158 JSFunction* thisObject = jsCast<JSFunction*>(cell);
159 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
160 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
161 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
162 Base::visitChildren(thisObject, visitor);
163
164 visitor.append(&thisObject->m_scope);
165 visitor.append(&thisObject->m_executable);
166 thisObject->m_allocationProfile.visitAggregate(visitor);
167 }
168
169 CallType JSFunction::getCallData(JSCell* cell, CallData& callData)
170 {
171 JSFunction* thisObject = jsCast<JSFunction*>(cell);
172 if (thisObject->isHostFunction()) {
173 callData.native.function = thisObject->nativeFunction();
174 return CallTypeHost;
175 }
176 callData.js.functionExecutable = thisObject->jsExecutable();
177 callData.js.scope = thisObject->scope();
178 return CallTypeJS;
179 }
180
181 JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, PropertyName)
182 {
183 JSFunction* thisObj = jsCast<JSFunction*>(slotBase);
184 ASSERT(!thisObj->isHostFunction());
185 return exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObj);
186 }
187
188 JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, PropertyName)
189 {
190 JSFunction* thisObj = jsCast<JSFunction*>(slotBase);
191 ASSERT(!thisObj->isHostFunction());
192 JSValue caller = exec->interpreter()->retrieveCallerFromVMCode(exec, thisObj);
193
194 // See ES5.1 15.3.5.4 - Function.caller may not be used to retrieve a strict caller.
195 if (!caller.isObject() || !asObject(caller)->inherits(&JSFunction::s_info))
196 return caller;
197 JSFunction* function = jsCast<JSFunction*>(caller);
198 if (function->isHostFunction() || !function->jsExecutable()->isStrictMode())
199 return caller;
200 return throwTypeError(exec, ASCIILiteral("Function.caller used to retrieve strict caller"));
201 }
202
203 JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, PropertyName)
204 {
205 JSFunction* thisObj = jsCast<JSFunction*>(slotBase);
206 ASSERT(!thisObj->isHostFunction());
207 return jsNumber(thisObj->jsExecutable()->parameterCount());
208 }
209
210 JSValue JSFunction::nameGetter(ExecState*, JSValue slotBase, PropertyName)
211 {
212 JSFunction* thisObj = jsCast<JSFunction*>(slotBase);
213 ASSERT(!thisObj->isHostFunction());
214 return thisObj->jsExecutable()->nameValue();
215 }
216
217 bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
218 {
219 JSFunction* thisObject = jsCast<JSFunction*>(cell);
220 if (thisObject->isHostFunction())
221 return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
222
223 if (propertyName == exec->propertyNames().prototype) {
224 VM& vm = exec->vm();
225 PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName);
226 if (!isValidOffset(offset)) {
227 JSObject* prototype = constructEmptyObject(exec);
228 prototype->putDirect(vm, exec->propertyNames().constructor, thisObject, DontEnum);
229 thisObject->putDirect(vm, exec->propertyNames().prototype, prototype, DontDelete | DontEnum);
230 offset = thisObject->getDirectOffset(vm, exec->propertyNames().prototype);
231 ASSERT(isValidOffset(offset));
232 }
233
234 slot.setValue(thisObject, thisObject->getDirect(offset), offset);
235 }
236
237 if (propertyName == exec->propertyNames().arguments) {
238 if (thisObject->jsExecutable()->isStrictMode()) {
239 bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
240 if (!result) {
241 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
242 result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
243 ASSERT(result);
244 }
245 return result;
246 }
247 slot.setCacheableCustom(thisObject, argumentsGetter);
248 return true;
249 }
250
251 if (propertyName == exec->propertyNames().length) {
252 slot.setCacheableCustom(thisObject, lengthGetter);
253 return true;
254 }
255
256 if (propertyName == exec->propertyNames().name) {
257 slot.setCacheableCustom(thisObject, nameGetter);
258 return true;
259 }
260
261 if (propertyName == exec->propertyNames().caller) {
262 if (thisObject->jsExecutable()->isStrictMode()) {
263 bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
264 if (!result) {
265 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
266 result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
267 ASSERT(result);
268 }
269 return result;
270 }
271 slot.setCacheableCustom(thisObject, callerGetter);
272 return true;
273 }
274
275 return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
276 }
277
278 bool JSFunction::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
279 {
280 JSFunction* thisObject = jsCast<JSFunction*>(object);
281 if (thisObject->isHostFunction())
282 return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
283
284 if (propertyName == exec->propertyNames().prototype) {
285 PropertySlot slot;
286 thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot);
287 return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
288 }
289
290 if (propertyName == exec->propertyNames().arguments) {
291 if (thisObject->jsExecutable()->isStrictMode()) {
292 bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
293 if (!result) {
294 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
295 result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
296 ASSERT(result);
297 }
298 return result;
299 }
300 descriptor.setDescriptor(exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObject), ReadOnly | DontEnum | DontDelete);
301 return true;
302 }
303
304 if (propertyName == exec->propertyNames().length) {
305 descriptor.setDescriptor(jsNumber(thisObject->jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete);
306 return true;
307 }
308
309 if (propertyName == exec->propertyNames().name) {
310 descriptor.setDescriptor(thisObject->jsExecutable()->nameValue(), ReadOnly | DontEnum | DontDelete);
311 return true;
312 }
313
314 if (propertyName == exec->propertyNames().caller) {
315 if (thisObject->jsExecutable()->isStrictMode()) {
316 bool result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
317 if (!result) {
318 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
319 result = Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
320 ASSERT(result);
321 }
322 return result;
323 }
324 descriptor.setDescriptor(exec->interpreter()->retrieveCallerFromVMCode(exec, thisObject), ReadOnly | DontEnum | DontDelete);
325 return true;
326 }
327
328 return Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor);
329 }
330
331 void JSFunction::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
332 {
333 JSFunction* thisObject = jsCast<JSFunction*>(object);
334 if (!thisObject->isHostFunction() && (mode == IncludeDontEnumProperties)) {
335 // Make sure prototype has been reified.
336 PropertySlot slot;
337 thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, exec->propertyNames().prototype, slot);
338
339 propertyNames.add(exec->propertyNames().arguments);
340 propertyNames.add(exec->propertyNames().caller);
341 propertyNames.add(exec->propertyNames().length);
342 propertyNames.add(exec->propertyNames().name);
343 }
344 Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
345 }
346
347 void JSFunction::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
348 {
349 JSFunction* thisObject = jsCast<JSFunction*>(cell);
350 if (thisObject->isHostFunction()) {
351 Base::put(thisObject, exec, propertyName, value, slot);
352 return;
353 }
354 if (propertyName == exec->propertyNames().prototype) {
355 // Make sure prototype has been reified, such that it can only be overwritten
356 // following the rules set out in ECMA-262 8.12.9.
357 PropertySlot slot;
358 thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot);
359 thisObject->m_allocationProfile.clear();
360 thisObject->m_allocationProfileWatchpoint.notifyWrite();
361 // Don't allow this to be cached, since a [[Put]] must clear m_allocationProfile.
362 PutPropertySlot dontCache;
363 Base::put(thisObject, exec, propertyName, value, dontCache);
364 return;
365 }
366 if (thisObject->jsExecutable()->isStrictMode() && (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().caller)) {
367 // This will trigger the property to be reified, if this is not already the case!
368 bool okay = thisObject->hasProperty(exec, propertyName);
369 ASSERT_UNUSED(okay, okay);
370 Base::put(thisObject, exec, propertyName, value, slot);
371 return;
372 }
373 if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().name || propertyName == exec->propertyNames().caller) {
374 if (slot.isStrictMode())
375 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
376 return;
377 }
378 Base::put(thisObject, exec, propertyName, value, slot);
379 }
380
381 bool JSFunction::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
382 {
383 JSFunction* thisObject = jsCast<JSFunction*>(cell);
384 // For non-host functions, don't let these properties by deleted - except by DefineOwnProperty.
385 if (!thisObject->isHostFunction() && !exec->vm().isInDefineOwnProperty()
386 && (propertyName == exec->propertyNames().arguments
387 || propertyName == exec->propertyNames().length
388 || propertyName == exec->propertyNames().name
389 || propertyName == exec->propertyNames().prototype
390 || propertyName == exec->propertyNames().caller))
391 return false;
392 return Base::deleteProperty(thisObject, exec, propertyName);
393 }
394
395 bool JSFunction::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool throwException)
396 {
397 JSFunction* thisObject = jsCast<JSFunction*>(object);
398 if (thisObject->isHostFunction())
399 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
400
401 if (propertyName == exec->propertyNames().prototype) {
402 // Make sure prototype has been reified, such that it can only be overwritten
403 // following the rules set out in ECMA-262 8.12.9.
404 PropertySlot slot;
405 thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, propertyName, slot);
406 thisObject->m_allocationProfile.clear();
407 thisObject->m_allocationProfileWatchpoint.notifyWrite();
408 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
409 }
410
411 bool valueCheck;
412 if (propertyName == exec->propertyNames().arguments) {
413 if (thisObject->jsExecutable()->isStrictMode()) {
414 if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor))
415 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
416 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
417 }
418 valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), exec->interpreter()->retrieveArgumentsFromVMCode(exec, thisObject));
419 } else if (propertyName == exec->propertyNames().caller) {
420 if (thisObject->jsExecutable()->isStrictMode()) {
421 if (!Base::getOwnPropertyDescriptor(thisObject, exec, propertyName, descriptor))
422 thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor);
423 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
424 }
425 valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), exec->interpreter()->retrieveCallerFromVMCode(exec, thisObject));
426 } else if (propertyName == exec->propertyNames().length)
427 valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), jsNumber(thisObject->jsExecutable()->parameterCount()));
428 else if (propertyName == exec->propertyNames().name)
429 valueCheck = !descriptor.value() || sameValue(exec, descriptor.value(), thisObject->jsExecutable()->nameValue());
430 else
431 return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
432
433 if (descriptor.configurablePresent() && descriptor.configurable()) {
434 if (throwException)
435 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
436 return false;
437 }
438 if (descriptor.enumerablePresent() && descriptor.enumerable()) {
439 if (throwException)
440 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property.")));
441 return false;
442 }
443 if (descriptor.isAccessorDescriptor()) {
444 if (throwException)
445 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property.")));
446 return false;
447 }
448 if (descriptor.writablePresent() && descriptor.writable()) {
449 if (throwException)
450 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property.")));
451 return false;
452 }
453 if (!valueCheck) {
454 if (throwException)
455 throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property.")));
456 return false;
457 }
458 return true;
459 }
460
461 // ECMA 13.2.2 [[Construct]]
462 ConstructType JSFunction::getConstructData(JSCell* cell, ConstructData& constructData)
463 {
464 JSFunction* thisObject = jsCast<JSFunction*>(cell);
465 if (thisObject->isHostFunction()) {
466 constructData.native.function = thisObject->nativeConstructor();
467 return ConstructTypeHost;
468 }
469 constructData.js.functionExecutable = thisObject->jsExecutable();
470 constructData.js.scope = thisObject->scope();
471 return ConstructTypeJS;
472 }
473
474 String getCalculatedDisplayName(CallFrame* callFrame, JSObject* object)
475 {
476 if (JSFunction* function = jsDynamicCast<JSFunction*>(object))
477 return function->calculatedDisplayName(callFrame);
478 if (InternalFunction* function = jsDynamicCast<InternalFunction*>(object))
479 return function->calculatedDisplayName(callFrame);
480 return "";
481 }
482
483 } // namespace JSC