source: webkit/trunk/JavaScriptCore/runtime/JSObject.cpp@ 43037

Last change on this file since 43037 was 43037, checked in by [email protected], 16 years ago

Clean up ArgList to be a trivial type

Reviewed by Gavin Barraclough

Separate out old ArgList logic to handle buffering and marking arguments
into a distinct MarkedArgumentBuffer type. ArgList becomes a trivial
struct of a pointer and length.

  • Property svn:eol-style set to native
File size: 16.1 KB
Line 
1/*
2 * Copyright (C) 1999-2001 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel ([email protected])
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "JSObject.h"
26
27#include "DatePrototype.h"
28#include "ErrorConstructor.h"
29#include "GetterSetter.h"
30#include "JSGlobalObject.h"
31#include "NativeErrorConstructor.h"
32#include "ObjectPrototype.h"
33#include "PropertyNameArray.h"
34#include "Lookup.h"
35#include "Nodes.h"
36#include "Operations.h"
37#include <math.h>
38#include <wtf/Assertions.h>
39
40#define JSOBJECT_MARK_TRACING 0
41
42#if JSOBJECT_MARK_TRACING
43
44#define JSOBJECT_MARK_BEGIN() \
45 static int markStackDepth = 0; \
46 for (int i = 0; i < markStackDepth; i++) \
47 putchar('-'); \
48 printf("%s (%p)\n", className().UTF8String().c_str(), this); \
49 markStackDepth++; \
50
51#define JSOBJECT_MARK_END() \
52 markStackDepth--;
53
54#else // JSOBJECT_MARK_TRACING
55
56#define JSOBJECT_MARK_BEGIN()
57#define JSOBJECT_MARK_END()
58
59#endif // JSOBJECT_MARK_TRACING
60
61namespace JSC {
62
63ASSERT_CLASS_FITS_IN_CELL(JSObject);
64
65void JSObject::mark()
66{
67 JSOBJECT_MARK_BEGIN();
68
69 JSCell::mark();
70 m_structure->mark();
71
72 size_t storageSize = m_structure->propertyStorageSize();
73 for (size_t i = 0; i < storageSize; ++i) {
74 JSValuePtr v = m_propertyStorage[i];
75 if (!v.marked())
76 v.mark();
77 }
78
79 JSOBJECT_MARK_END();
80}
81
82UString JSObject::className() const
83{
84 const ClassInfo* info = classInfo();
85 if (info)
86 return info->className;
87 return "Object";
88}
89
90bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
91{
92 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
93}
94
95static void throwSetterError(ExecState* exec)
96{
97 throwError(exec, TypeError, "setting a property that has only a getter");
98}
99
100// ECMA 8.6.2.2
101void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
102{
103 ASSERT(value);
104 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
105
106 if (propertyName == exec->propertyNames().underscoreProto) {
107 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
108 if (!value.isObject() && !value.isNull())
109 return;
110
111 JSValuePtr nextPrototypeValue = value;
112 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
113 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
114 if (nextPrototype == this) {
115 throwError(exec, GeneralError, "cyclic __proto__ value");
116 return;
117 }
118 nextPrototypeValue = nextPrototype->prototype();
119 }
120
121 setPrototype(value);
122 return;
123 }
124
125 // Check if there are any setters or getters in the prototype chain
126 JSValuePtr prototype;
127 for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
128 prototype = obj->prototype();
129 if (prototype.isNull()) {
130 putDirect(propertyName, value, 0, true, slot);
131 return;
132 }
133 }
134
135 unsigned attributes;
136 if ((m_structure->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
137 return;
138
139 for (JSObject* obj = this; ; obj = asObject(prototype)) {
140 if (JSValuePtr gs = obj->getDirect(propertyName)) {
141 if (gs.isGetterSetter()) {
142 JSObject* setterFunc = asGetterSetter(gs)->setter();
143 if (!setterFunc) {
144 throwSetterError(exec);
145 return;
146 }
147
148 CallData callData;
149 CallType callType = setterFunc->getCallData(callData);
150 MarkedArgumentBuffer args;
151 args.append(value);
152 call(exec, setterFunc, callType, callData, this, args);
153 return;
154 }
155
156 // If there's an existing property on the object or one of its
157 // prototypes it should be replaced, so break here.
158 break;
159 }
160
161 prototype = obj->prototype();
162 if (prototype.isNull())
163 break;
164 }
165
166 putDirect(propertyName, value, 0, true, slot);
167 return;
168}
169
170void JSObject::put(ExecState* exec, unsigned propertyName, JSValuePtr value)
171{
172 PutPropertySlot slot;
173 put(exec, Identifier::from(exec, propertyName), value, slot);
174}
175
176void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValuePtr value, unsigned attributes)
177{
178 putDirect(propertyName, value, attributes);
179}
180
181void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValuePtr value, unsigned attributes)
182{
183 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
184}
185
186bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
187{
188 PropertySlot slot;
189 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
190}
191
192bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
193{
194 PropertySlot slot;
195 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
196}
197
198// ECMA 8.6.2.5
199bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
200{
201 unsigned attributes;
202 if (m_structure->get(propertyName, attributes) != WTF::notFound) {
203 if ((attributes & DontDelete))
204 return false;
205 removeDirect(propertyName);
206 return true;
207 }
208
209 // Look in the static hashtable of properties
210 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
211 if (entry && entry->attributes() & DontDelete)
212 return false; // this builtin property can't be deleted
213
214 // FIXME: Should the code here actually do some deletion?
215 return true;
216}
217
218bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
219{
220 PropertySlot slot;
221 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
222}
223
224bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
225{
226 return deleteProperty(exec, Identifier::from(exec, propertyName));
227}
228
229static ALWAYS_INLINE JSValuePtr callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
230{
231 JSValuePtr function = object->get(exec, propertyName);
232 CallData callData;
233 CallType callType = function.getCallData(callData);
234 if (callType == CallTypeNone)
235 return exec->exception();
236
237 // Prevent "toString" and "valueOf" from observing execution if an exception
238 // is pending.
239 if (exec->hadException())
240 return exec->exception();
241
242 JSValuePtr result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
243 ASSERT(!result.isGetterSetter());
244 if (exec->hadException())
245 return exec->exception();
246 if (result.isObject())
247 return noValue();
248 return result;
249}
250
251bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValuePtr& result)
252{
253 result = defaultValue(exec, PreferNumber);
254 number = result.toNumber(exec);
255 return !result.isString();
256}
257
258// ECMA 8.6.2.6
259JSValuePtr JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
260{
261 // Must call toString first for Date objects.
262 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
263 JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
264 if (value)
265 return value;
266 value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
267 if (value)
268 return value;
269 } else {
270 JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
271 if (value)
272 return value;
273 value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
274 if (value)
275 return value;
276 }
277
278 ASSERT(!exec->hadException());
279
280 return throwError(exec, TypeError, "No default value");
281}
282
283const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
284{
285 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
286 if (const HashTable* propHashTable = info->propHashTable(exec)) {
287 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
288 return entry;
289 }
290 }
291 return 0;
292}
293
294void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
295{
296 JSValuePtr object = getDirect(propertyName);
297 if (object && object.isGetterSetter()) {
298 ASSERT(m_structure->hasGetterSetterProperties());
299 asGetterSetter(object)->setGetter(getterFunction);
300 return;
301 }
302
303 PutPropertySlot slot;
304 GetterSetter* getterSetter = new (exec) GetterSetter;
305 putDirect(propertyName, getterSetter, None, true, slot);
306
307 // putDirect will change our Structure if we add a new property. For
308 // getters and setters, though, we also need to change our Structure
309 // if we override an existing non-getter or non-setter.
310 if (slot.type() != PutPropertySlot::NewProperty) {
311 if (!m_structure->isDictionary()) {
312 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
313 setStructure(structure.release());
314 }
315 }
316
317 m_structure->setHasGetterSetterProperties(true);
318 getterSetter->setGetter(getterFunction);
319}
320
321void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
322{
323 JSValuePtr object = getDirect(propertyName);
324 if (object && object.isGetterSetter()) {
325 ASSERT(m_structure->hasGetterSetterProperties());
326 asGetterSetter(object)->setSetter(setterFunction);
327 return;
328 }
329
330 PutPropertySlot slot;
331 GetterSetter* getterSetter = new (exec) GetterSetter;
332 putDirect(propertyName, getterSetter, None, true, slot);
333
334 // putDirect will change our Structure if we add a new property. For
335 // getters and setters, though, we also need to change our Structure
336 // if we override an existing non-getter or non-setter.
337 if (slot.type() != PutPropertySlot::NewProperty) {
338 if (!m_structure->isDictionary()) {
339 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
340 setStructure(structure.release());
341 }
342 }
343
344 m_structure->setHasGetterSetterProperties(true);
345 getterSetter->setSetter(setterFunction);
346}
347
348JSValuePtr JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
349{
350 JSObject* object = this;
351 while (true) {
352 if (JSValuePtr value = object->getDirect(propertyName)) {
353 if (!value.isGetterSetter())
354 return jsUndefined();
355 JSObject* functionObject = asGetterSetter(value)->getter();
356 if (!functionObject)
357 return jsUndefined();
358 return functionObject;
359 }
360
361 if (!object->prototype() || !object->prototype().isObject())
362 return jsUndefined();
363 object = asObject(object->prototype());
364 }
365}
366
367JSValuePtr JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
368{
369 JSObject* object = this;
370 while (true) {
371 if (JSValuePtr value = object->getDirect(propertyName)) {
372 if (!value.isGetterSetter())
373 return jsUndefined();
374 JSObject* functionObject = asGetterSetter(value)->setter();
375 if (!functionObject)
376 return jsUndefined();
377 return functionObject;
378 }
379
380 if (!object->prototype() || !object->prototype().isObject())
381 return jsUndefined();
382 object = asObject(object->prototype());
383 }
384}
385
386bool JSObject::hasInstance(ExecState* exec, JSValuePtr value, JSValuePtr proto)
387{
388 if (!proto.isObject()) {
389 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
390 return false;
391 }
392
393 if (!value.isObject())
394 return false;
395
396 JSObject* object = asObject(value);
397 while ((object = object->prototype().getObject())) {
398 if (proto == object)
399 return true;
400 }
401 return false;
402}
403
404bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
405{
406 unsigned attributes;
407 if (!getPropertyAttributes(exec, propertyName, attributes))
408 return false;
409 return !(attributes & DontEnum);
410}
411
412bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
413{
414 if (m_structure->get(propertyName, attributes) != WTF::notFound)
415 return true;
416
417 // Look in the static hashtable of properties
418 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
419 if (entry) {
420 attributes = entry->attributes();
421 return true;
422 }
423
424 return false;
425}
426
427void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
428{
429 m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
430}
431
432bool JSObject::toBoolean(ExecState*) const
433{
434 return true;
435}
436
437double JSObject::toNumber(ExecState* exec) const
438{
439 JSValuePtr primitive = toPrimitive(exec, PreferNumber);
440 if (exec->hadException()) // should be picked up soon in Nodes.cpp
441 return 0.0;
442 return primitive.toNumber(exec);
443}
444
445UString JSObject::toString(ExecState* exec) const
446{
447 JSValuePtr primitive = toPrimitive(exec, PreferString);
448 if (exec->hadException())
449 return "";
450 return primitive.toString(exec);
451}
452
453JSObject* JSObject::toObject(ExecState*) const
454{
455 return const_cast<JSObject*>(this);
456}
457
458JSObject* JSObject::toThisObject(ExecState*) const
459{
460 return const_cast<JSObject*>(this);
461}
462
463JSObject* JSObject::unwrappedObject()
464{
465 return this;
466}
467
468void JSObject::removeDirect(const Identifier& propertyName)
469{
470 size_t offset;
471 if (m_structure->isDictionary()) {
472 offset = m_structure->removePropertyWithoutTransition(propertyName);
473 if (offset != WTF::notFound)
474 m_propertyStorage[offset] = jsUndefined();
475 return;
476 }
477
478 RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
479 if (offset != WTF::notFound)
480 m_propertyStorage[offset] = jsUndefined();
481 setStructure(structure.release());
482}
483
484void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
485{
486 putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
487}
488
489void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
490{
491 putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
492}
493
494NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValuePtr* location)
495{
496 if (JSObject* getterFunction = asGetterSetter(*location)->getter())
497 slot.setGetterSlot(getterFunction);
498 else
499 slot.setUndefined();
500}
501
502Structure* JSObject::createInheritorID()
503{
504 m_inheritorID = JSObject::createStructure(this);
505 return m_inheritorID.get();
506}
507
508void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
509{
510 allocatePropertyStorageInline(oldSize, newSize);
511}
512
513JSObject* constructEmptyObject(ExecState* exec)
514{
515 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
516}
517
518} // namespace JSC
Note: See TracBrowser for help on using the repository browser.