source: webkit/trunk/JavaScriptCore/kjs/JSObject.cpp@ 37929

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

2008-10-28 Kevin McCullough <[email protected]>

Reviewed by Dan Bernstein.

-Removed unused includes.
Apparent .4% speedup in Sunspider

  • kjs/JSObject.cpp:
  • kjs/interpreter.cpp:
  • 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_structureID->mark();
71
72 unsigned storageSize = m_structureID->propertyMap().storageSize();
73 for (unsigned i = 0; i < storageSize; ++i) {
74 JSValue* 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, JSValue* 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 JSObject* proto = value->getObject();
108
109 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
110 if (!proto && !value->isNull())
111 return;
112
113 while (proto) {
114 if (proto == this) {
115 throwError(exec, GeneralError, "cyclic __proto__ value");
116 return;
117 }
118 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
119 }
120
121 setPrototype(value);
122 return;
123 }
124
125 // Check if there are any setters or getters in the prototype chain
126 JSValue* prototype;
127 for (JSObject* obj = this; !obj->structureID()->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_structureID->propertyMap().get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
137 return;
138
139 for (JSObject* obj = this; ; obj = asObject(prototype)) {
140 if (JSValue* 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 ArgList 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, JSValue* value)
171{
172 PutPropertySlot slot;
173 put(exec, Identifier::from(exec, propertyName), value, slot);
174}
175
176void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
177{
178 putDirect(propertyName, value, attributes);
179}
180
181void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* 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_structureID->propertyMap().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 JSValue* callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
230{
231 JSValue* 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 JSValue* 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, JSValue*& result)
252{
253 result = defaultValue(exec, PreferNumber);
254 number = result->toNumber(exec);
255 return !result->isString();
256}
257
258// ECMA 8.6.2.6
259JSValue* 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 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
264 return value;
265 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
266 return value;
267 } else {
268 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
269 return value;
270 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
271 return value;
272 }
273
274 ASSERT(!exec->hadException());
275
276 return throwError(exec, TypeError, "No default value");
277}
278
279const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
280{
281 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
282 if (const HashTable* propHashTable = info->propHashTable(exec)) {
283 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
284 return entry;
285 }
286 }
287 return 0;
288}
289
290void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
291{
292 JSValue* object = getDirect(propertyName);
293 if (object && object->isGetterSetter()) {
294 ASSERT(m_structureID->hasGetterSetterProperties());
295 asGetterSetter(object)->setGetter(getterFunction);
296 return;
297 }
298
299 PutPropertySlot slot;
300 GetterSetter* getterSetter = new (exec) GetterSetter;
301 putDirect(propertyName, getterSetter, None, true, slot);
302
303 // putDirect will change our StructureID if we add a new property. For
304 // getters and setters, though, we also need to change our StructureID
305 // if we override an existing non-getter or non-setter.
306 if (slot.type() != PutPropertySlot::NewProperty) {
307 if (!m_structureID->isDictionary()) {
308 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
309 setStructureID(structureID.release());
310 }
311 }
312
313 m_structureID->setHasGetterSetterProperties(true);
314 getterSetter->setGetter(getterFunction);
315}
316
317void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
318{
319 JSValue* object = getDirect(propertyName);
320 if (object && object->isGetterSetter()) {
321 ASSERT(m_structureID->hasGetterSetterProperties());
322 asGetterSetter(object)->setSetter(setterFunction);
323 return;
324 }
325
326 PutPropertySlot slot;
327 GetterSetter* getterSetter = new (exec) GetterSetter;
328 putDirect(propertyName, getterSetter, None, true, slot);
329
330 // putDirect will change our StructureID if we add a new property. For
331 // getters and setters, though, we also need to change our StructureID
332 // if we override an existing non-getter or non-setter.
333 if (slot.type() != PutPropertySlot::NewProperty) {
334 if (!m_structureID->isDictionary()) {
335 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
336 setStructureID(structureID.release());
337 }
338 }
339
340 m_structureID->setHasGetterSetterProperties(true);
341 getterSetter->setSetter(setterFunction);
342}
343
344JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
345{
346 JSObject* object = this;
347 while (true) {
348 JSValue* value = object->getDirect(propertyName);
349 if (value) {
350 if (!value->isGetterSetter())
351 return jsUndefined();
352 JSObject* functionObject = asGetterSetter(value)->getter();
353 if (!functionObject)
354 return jsUndefined();
355 return functionObject;
356 }
357
358 if (!object->prototype() || !object->prototype()->isObject())
359 return jsUndefined();
360 object = asObject(object->prototype());
361 }
362}
363
364JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
365{
366 JSObject* object = this;
367 while (true) {
368 JSValue* value = object->getDirect(propertyName);
369 if (value) {
370 if (!value->isGetterSetter())
371 return jsUndefined();
372 JSObject* functionObject = asGetterSetter(value)->setter();
373 if (!functionObject)
374 return jsUndefined();
375 return functionObject;
376 }
377
378 if (!object->prototype() || !object->prototype()->isObject())
379 return jsUndefined();
380 object = asObject(object->prototype());
381 }
382}
383
384bool JSObject::hasInstance(ExecState* exec, JSValue* value, JSValue* proto)
385{
386 if (!proto->isObject()) {
387 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
388 return false;
389 }
390
391 if (!value->isObject())
392 return false;
393
394 JSObject* object = asObject(value);
395 while ((object = object->prototype()->getObject())) {
396 if (object == proto)
397 return true;
398 }
399 return false;
400}
401
402bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
403{
404 unsigned attributes;
405 if (!getPropertyAttributes(exec, propertyName, attributes))
406 return false;
407 return !(attributes & DontEnum);
408}
409
410bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
411{
412 if (m_structureID->propertyMap().get(propertyName, attributes) != WTF::notFound)
413 return true;
414
415 // Look in the static hashtable of properties
416 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
417 if (entry) {
418 attributes = entry->attributes();
419 return true;
420 }
421
422 return false;
423}
424
425void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
426{
427 m_structureID->getEnumerablePropertyNames(exec, propertyNames, this);
428}
429
430bool JSObject::toBoolean(ExecState*) const
431{
432 return true;
433}
434
435double JSObject::toNumber(ExecState* exec) const
436{
437 JSValue* primitive = toPrimitive(exec, PreferNumber);
438 if (exec->hadException()) // should be picked up soon in nodes.cpp
439 return 0.0;
440 return primitive->toNumber(exec);
441}
442
443UString JSObject::toString(ExecState* exec) const
444{
445 JSValue* primitive = toPrimitive(exec, PreferString);
446 if (exec->hadException())
447 return "";
448 return primitive->toString(exec);
449}
450
451JSObject* JSObject::toObject(ExecState*) const
452{
453 return const_cast<JSObject*>(this);
454}
455
456JSObject* JSObject::toThisObject(ExecState*) const
457{
458 return const_cast<JSObject*>(this);
459}
460
461JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
462{
463 return 0;
464}
465
466void JSObject::removeDirect(const Identifier& propertyName)
467{
468 size_t offset;
469 if (m_structureID->isDictionary()) {
470 offset = m_structureID->propertyMap().remove(propertyName);
471 if (offset != WTF::notFound) {
472 m_propertyStorage[offset] = jsUndefined();
473 m_structureID->clearEnumerationCache();
474 }
475 return;
476 }
477
478 RefPtr<StructureID> structureID = StructureID::toDictionaryTransition(m_structureID);
479 offset = structureID->propertyMap().remove(propertyName);
480 if (offset != WTF::notFound)
481 m_propertyStorage[offset] = jsUndefined();
482 setStructureID(structureID.release());
483}
484
485void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
486{
487 putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
488}
489
490void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
491{
492 putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
493}
494
495NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
496{
497 if (JSObject* getterFunction = asGetterSetter(*location)->getter())
498 slot.setGetterSlot(getterFunction);
499 else
500 slot.setUndefined();
501}
502
503StructureID* JSObject::createInheritorID()
504{
505 m_inheritorID = JSObject::createStructureID(this);
506 return m_inheritorID.get();
507}
508
509void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
510{
511 allocatePropertyStorageInline(oldSize, newSize);
512}
513
514JSObject* constructEmptyObject(ExecState* exec)
515{
516 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
517}
518
519} // namespace JSC
Note: See TracBrowser for help on using the repository browser.