]>
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 | ||
28 | #include "CodeBlock.h" | |
29 | #include "CommonIdentifiers.h" | |
30 | #include "CallFrame.h" | |
14957cd0 | 31 | #include "ExceptionHelpers.h" |
9dae56ea A |
32 | #include "FunctionPrototype.h" |
33 | #include "JSGlobalObject.h" | |
14957cd0 | 34 | #include "JSNotAnObject.h" |
9dae56ea A |
35 | #include "Interpreter.h" |
36 | #include "ObjectPrototype.h" | |
37 | #include "Parser.h" | |
38 | #include "PropertyNameArray.h" | |
39 | #include "ScopeChainMark.h" | |
40 | ||
41 | using namespace WTF; | |
42 | using namespace Unicode; | |
43 | ||
44 | namespace JSC { | |
14957cd0 A |
45 | EncodedJSValue JSC_HOST_CALL callHostFunctionAsConstructor(ExecState* exec) |
46 | { | |
47 | return throwVMError(exec, createNotAConstructorError(exec, exec->callee())); | |
48 | } | |
9dae56ea A |
49 | |
50 | ASSERT_CLASS_FITS_IN_CELL(JSFunction); | |
51 | ||
14957cd0 | 52 | const ClassInfo JSFunction::s_info = { "Function", &Base::s_info, 0, 0 }; |
9dae56ea | 53 | |
f9bf01c6 A |
54 | bool JSFunction::isHostFunctionNonInline() const |
55 | { | |
56 | return isHostFunction(); | |
57 | } | |
58 | ||
14957cd0 A |
59 | JSFunction::JSFunction(VPtrStealingHackType) |
60 | : Base(VPtrStealingHack) | |
f9bf01c6 A |
61 | { |
62 | } | |
63 | ||
14957cd0 A |
64 | JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeExecutable* thunk) |
65 | : Base(globalObject, structure) | |
66 | , m_executable(exec->globalData(), this, thunk) | |
67 | , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain()) | |
4e4e5a6f | 68 | { |
14957cd0 A |
69 | ASSERT(inherits(&s_info)); |
70 | putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); | |
71 | putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); | |
4e4e5a6f A |
72 | } |
73 | ||
14957cd0 A |
74 | JSFunction::JSFunction(ExecState* exec, JSGlobalObject* globalObject, Structure* structure, int length, const Identifier& name, NativeFunction func) |
75 | : Base(globalObject, structure) | |
76 | , m_scopeChain(exec->globalData(), this, globalObject->globalScopeChain()) | |
ba379fdc | 77 | { |
14957cd0 A |
78 | ASSERT(inherits(&s_info)); |
79 | ||
80 | // Can't do this during initialization because getHostFunction might do a GC allocation. | |
81 | m_executable.set(exec->globalData(), this, exec->globalData().getHostFunction(func)); | |
82 | ||
83 | putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); | |
84 | putDirect(exec->globalData(), exec->propertyNames().length, jsNumber(length), DontDelete | ReadOnly | DontEnum); | |
ba379fdc A |
85 | } |
86 | ||
14957cd0 A |
87 | JSFunction::JSFunction(ExecState* exec, FunctionExecutable* executable, ScopeChainNode* scopeChainNode) |
88 | : Base(scopeChainNode->globalObject.get(), scopeChainNode->globalObject->functionStructure()) | |
89 | , m_executable(exec->globalData(), this, executable) | |
90 | , m_scopeChain(exec->globalData(), this, scopeChainNode) | |
9dae56ea | 91 | { |
14957cd0 A |
92 | ASSERT(inherits(&s_info)); |
93 | const Identifier& name = static_cast<FunctionExecutable*>(m_executable.get())->name(); | |
94 | putDirect(exec->globalData(), exec->globalData().propertyNames->name, jsString(exec, name.isNull() ? "" : name.ustring()), DontDelete | ReadOnly | DontEnum); | |
9dae56ea A |
95 | } |
96 | ||
97 | JSFunction::~JSFunction() | |
98 | { | |
f9bf01c6 | 99 | ASSERT(vptr() == JSGlobalData::jsFunctionVPtr); |
14957cd0 | 100 | } |
f9bf01c6 | 101 | |
14957cd0 A |
102 | static const char* StrictModeCallerAccessError = "Cannot access caller property of a strict mode function"; |
103 | static const char* StrictModeArgumentsAccessError = "Cannot access arguments property of a strict mode function"; | |
104 | ||
105 | static void createDescriptorForThrowingProperty(ExecState* exec, PropertyDescriptor& descriptor, const char* message) | |
106 | { | |
107 | JSValue thrower = createTypeErrorFunction(exec, message); | |
108 | descriptor.setAccessorDescriptor(thrower, thrower, DontEnum | DontDelete | Getter | Setter); | |
9dae56ea A |
109 | } |
110 | ||
14957cd0 | 111 | const UString& JSFunction::name(ExecState* exec) |
9dae56ea | 112 | { |
14957cd0 A |
113 | return asString(getDirect(exec->globalData(), exec->globalData().propertyNames->name))->tryGetValue(); |
114 | } | |
115 | ||
116 | const UString JSFunction::displayName(ExecState* exec) | |
117 | { | |
118 | JSValue displayName = getDirect(exec->globalData(), exec->globalData().propertyNames->displayName); | |
119 | ||
120 | if (displayName && isJSString(&exec->globalData(), displayName)) | |
121 | return asString(displayName)->tryGetValue(); | |
122 | ||
123 | return UString(); | |
124 | } | |
125 | ||
126 | const UString JSFunction::calculatedDisplayName(ExecState* exec) | |
127 | { | |
128 | const UString explicitName = displayName(exec); | |
129 | ||
130 | if (!explicitName.isEmpty()) | |
131 | return explicitName; | |
132 | ||
133 | return name(exec); | |
134 | } | |
135 | ||
136 | void JSFunction::visitChildren(SlotVisitor& visitor) | |
137 | { | |
138 | ASSERT_GC_OBJECT_INHERITS(this, &s_info); | |
139 | COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); | |
140 | ASSERT(structure()->typeInfo().overridesVisitChildren()); | |
141 | Base::visitChildren(visitor); | |
142 | ||
143 | visitor.append(&m_scopeChain); | |
144 | if (m_executable) | |
145 | visitor.append(&m_executable); | |
9dae56ea A |
146 | } |
147 | ||
148 | CallType JSFunction::getCallData(CallData& callData) | |
149 | { | |
ba379fdc A |
150 | if (isHostFunction()) { |
151 | callData.native.function = nativeFunction(); | |
152 | return CallTypeHost; | |
153 | } | |
f9bf01c6 | 154 | callData.js.functionExecutable = jsExecutable(); |
14957cd0 | 155 | callData.js.scopeChain = scope(); |
9dae56ea A |
156 | return CallTypeJS; |
157 | } | |
158 | ||
4e4e5a6f | 159 | JSValue JSFunction::argumentsGetter(ExecState* exec, JSValue slotBase, const Identifier&) |
9dae56ea | 160 | { |
4e4e5a6f | 161 | JSFunction* thisObj = asFunction(slotBase); |
ba379fdc | 162 | ASSERT(!thisObj->isHostFunction()); |
9dae56ea A |
163 | return exec->interpreter()->retrieveArguments(exec, thisObj); |
164 | } | |
165 | ||
4e4e5a6f | 166 | JSValue JSFunction::callerGetter(ExecState* exec, JSValue slotBase, const Identifier&) |
9dae56ea | 167 | { |
4e4e5a6f | 168 | JSFunction* thisObj = asFunction(slotBase); |
ba379fdc | 169 | ASSERT(!thisObj->isHostFunction()); |
9dae56ea A |
170 | return exec->interpreter()->retrieveCaller(exec, thisObj); |
171 | } | |
172 | ||
14957cd0 | 173 | JSValue JSFunction::lengthGetter(ExecState*, JSValue slotBase, const Identifier&) |
9dae56ea | 174 | { |
4e4e5a6f | 175 | JSFunction* thisObj = asFunction(slotBase); |
ba379fdc | 176 | ASSERT(!thisObj->isHostFunction()); |
14957cd0 A |
177 | return jsNumber(thisObj->jsExecutable()->parameterCount()); |
178 | } | |
179 | ||
180 | static inline WriteBarrierBase<Unknown>* createPrototypeProperty(JSGlobalData& globalData, JSGlobalObject* globalObject, JSFunction* function) | |
181 | { | |
182 | ASSERT(!function->isHostFunction()); | |
183 | ||
184 | ExecState* exec = globalObject->globalExec(); | |
185 | if (WriteBarrierBase<Unknown>* location = function->getDirectLocation(globalData, exec->propertyNames().prototype)) | |
186 | return location; | |
187 | JSObject* prototype = constructEmptyObject(exec, globalObject->emptyObjectStructure()); | |
188 | prototype->putDirect(globalData, exec->propertyNames().constructor, function, DontEnum); | |
189 | function->putDirect(globalData, exec->propertyNames().prototype, prototype, DontDelete | DontEnum); | |
190 | return function->getDirectLocation(exec->globalData(), exec->propertyNames().prototype); | |
191 | } | |
192 | ||
193 | void JSFunction::preventExtensions(JSGlobalData& globalData) | |
194 | { | |
195 | if (!isHostFunction()) | |
196 | createPrototypeProperty(globalData, scope()->globalObject.get(), this); | |
197 | JSObject::preventExtensions(globalData); | |
9dae56ea A |
198 | } |
199 | ||
200 | bool JSFunction::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) | |
201 | { | |
ba379fdc A |
202 | if (isHostFunction()) |
203 | return Base::getOwnPropertySlot(exec, propertyName, slot); | |
204 | ||
9dae56ea | 205 | if (propertyName == exec->propertyNames().prototype) { |
14957cd0 | 206 | WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName); |
9dae56ea | 207 | |
14957cd0 A |
208 | if (!location) |
209 | location = createPrototypeProperty(exec->globalData(), scope()->globalObject.get(), this); | |
9dae56ea | 210 | |
14957cd0 | 211 | slot.setValue(this, location->get(), offsetForLocation(location)); |
9dae56ea A |
212 | } |
213 | ||
214 | if (propertyName == exec->propertyNames().arguments) { | |
14957cd0 A |
215 | if (jsExecutable()->isStrictMode()) { |
216 | throwTypeError(exec, "Can't access arguments object of a strict mode function"); | |
217 | slot.setValue(jsNull()); | |
218 | return true; | |
219 | } | |
220 | ||
4e4e5a6f | 221 | slot.setCacheableCustom(this, argumentsGetter); |
9dae56ea A |
222 | return true; |
223 | } | |
224 | ||
225 | if (propertyName == exec->propertyNames().length) { | |
4e4e5a6f | 226 | slot.setCacheableCustom(this, lengthGetter); |
9dae56ea A |
227 | return true; |
228 | } | |
229 | ||
230 | if (propertyName == exec->propertyNames().caller) { | |
14957cd0 A |
231 | if (jsExecutable()->isStrictMode()) { |
232 | throwTypeError(exec, StrictModeCallerAccessError); | |
233 | slot.setValue(jsNull()); | |
234 | return true; | |
235 | } | |
4e4e5a6f | 236 | slot.setCacheableCustom(this, callerGetter); |
9dae56ea A |
237 | return true; |
238 | } | |
239 | ||
240 | return Base::getOwnPropertySlot(exec, propertyName, slot); | |
241 | } | |
242 | ||
14957cd0 A |
243 | bool JSFunction::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) |
244 | { | |
245 | if (isHostFunction()) | |
246 | return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); | |
247 | ||
248 | if (propertyName == exec->propertyNames().prototype) { | |
249 | PropertySlot slot; | |
250 | getOwnPropertySlot(exec, propertyName, slot); | |
251 | return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); | |
252 | } | |
253 | ||
254 | if (propertyName == exec->propertyNames().arguments) { | |
255 | if (jsExecutable()->isStrictMode()) | |
256 | createDescriptorForThrowingProperty(exec, descriptor, StrictModeArgumentsAccessError); | |
257 | else | |
f9bf01c6 | 258 | descriptor.setDescriptor(exec->interpreter()->retrieveArguments(exec, this), ReadOnly | DontEnum | DontDelete); |
14957cd0 A |
259 | return true; |
260 | } | |
261 | ||
262 | if (propertyName == exec->propertyNames().length) { | |
263 | descriptor.setDescriptor(jsNumber(jsExecutable()->parameterCount()), ReadOnly | DontEnum | DontDelete); | |
264 | return true; | |
265 | } | |
266 | ||
267 | if (propertyName == exec->propertyNames().caller) { | |
268 | if (jsExecutable()->isStrictMode()) | |
269 | createDescriptorForThrowingProperty(exec, descriptor, StrictModeCallerAccessError); | |
270 | else | |
f9bf01c6 | 271 | descriptor.setDescriptor(exec->interpreter()->retrieveCaller(exec, this), ReadOnly | DontEnum | DontDelete); |
14957cd0 | 272 | return true; |
f9bf01c6 A |
273 | } |
274 | ||
14957cd0 A |
275 | return Base::getOwnPropertyDescriptor(exec, propertyName, descriptor); |
276 | } | |
277 | ||
f9bf01c6 A |
278 | void JSFunction::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
279 | { | |
280 | if (!isHostFunction() && (mode == IncludeDontEnumProperties)) { | |
14957cd0 A |
281 | // Make sure prototype has been reified. |
282 | PropertySlot slot; | |
283 | getOwnPropertySlot(exec, exec->propertyNames().prototype, slot); | |
284 | ||
f9bf01c6 A |
285 | propertyNames.add(exec->propertyNames().arguments); |
286 | propertyNames.add(exec->propertyNames().callee); | |
287 | propertyNames.add(exec->propertyNames().caller); | |
288 | propertyNames.add(exec->propertyNames().length); | |
289 | } | |
290 | Base::getOwnPropertyNames(exec, propertyNames, mode); | |
291 | } | |
292 | ||
ba379fdc | 293 | void JSFunction::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) |
9dae56ea | 294 | { |
ba379fdc A |
295 | if (isHostFunction()) { |
296 | Base::put(exec, propertyName, value, slot); | |
297 | return; | |
298 | } | |
14957cd0 A |
299 | if (propertyName == exec->propertyNames().prototype) { |
300 | // Make sure prototype has been reified, such that it can only be overwritten | |
301 | // following the rules set out in ECMA-262 8.12.9. | |
302 | PropertySlot slot; | |
303 | getOwnPropertySlot(exec, propertyName, slot); | |
304 | } | |
305 | if (jsExecutable()->isStrictMode()) { | |
306 | if (propertyName == exec->propertyNames().arguments) { | |
307 | throwTypeError(exec, StrictModeArgumentsAccessError); | |
308 | return; | |
309 | } | |
310 | if (propertyName == exec->propertyNames().caller) { | |
311 | throwTypeError(exec, StrictModeCallerAccessError); | |
312 | return; | |
313 | } | |
314 | } | |
9dae56ea A |
315 | if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) |
316 | return; | |
317 | Base::put(exec, propertyName, value, slot); | |
318 | } | |
319 | ||
320 | bool JSFunction::deleteProperty(ExecState* exec, const Identifier& propertyName) | |
321 | { | |
ba379fdc A |
322 | if (isHostFunction()) |
323 | return Base::deleteProperty(exec, propertyName); | |
9dae56ea A |
324 | if (propertyName == exec->propertyNames().arguments || propertyName == exec->propertyNames().length) |
325 | return false; | |
326 | return Base::deleteProperty(exec, propertyName); | |
327 | } | |
328 | ||
329 | // ECMA 13.2.2 [[Construct]] | |
330 | ConstructType JSFunction::getConstructData(ConstructData& constructData) | |
331 | { | |
ba379fdc A |
332 | if (isHostFunction()) |
333 | return ConstructTypeNone; | |
f9bf01c6 | 334 | constructData.js.functionExecutable = jsExecutable(); |
14957cd0 | 335 | constructData.js.scopeChain = scope(); |
9dae56ea A |
336 | return ConstructTypeJS; |
337 | } | |
338 | ||
9dae56ea | 339 | } // namespace JSC |