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

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

JavaScriptCore:

2008-09-08 Sam Weinig <[email protected]>

Reviewed by Maciej Stachowiak and Oliver Hunt.

Split storage of properties out of the PropertyMap and into the JSObject
to allow sharing PropertyMap on the StructureID. In order to get this
function correctly, the StructureID's transition mappings were changed to
transition based on property name and attribute pairs, instead of just
property name.

  • Removes the single property optimization now that the PropertyMap is shared. This will be replaced by in-lining some values on the JSObject.

This is a wash on Sunspider and a 6.7% win on the v8 test suite.

  • JavaScriptCore.base.exp:
  • VM/CTI.cpp: (JSC::CTI::privateCompileGetByIdSelf): Get the storage directly off the JSObject. (JSC::CTI::privateCompileGetByIdProto): Ditto. (JSC::CTI::privateCompileGetByIdChain): Ditto. (JSC::CTI::privateCompilePutByIdReplace): Ditto.
  • kjs/JSObject.cpp: (JSC::JSObject::mark): Mark the PropertyStorage. (JSC::JSObject::put): Update to get the propertyMap of the StructureID. (JSC::JSObject::deleteProperty): Ditto. (JSC::JSObject::defineGetter): Return early if the property is already a getter/setter. (JSC::JSObject::defineSetter): Ditto. (JSC::JSObject::getPropertyAttributes): Update to get the propertyMap of the StructureID (JSC::JSObject::getPropertyNames): Ditto. (JSC::JSObject::removeDirect): Ditto.
  • kjs/JSObject.h: Remove PropertyMap and add PropertyStorage. (JSC::JSObject::propertyStorage): return the PropertyStorage. (JSC::JSObject::getDirect): Update to get the propertyMap of the StructureID. (JSC::JSObject::getDirectLocation): Ditto. (JSC::JSObject::offsetForLocation): Compute location directly. (JSC::JSObject::hasCustomProperties): Update to get the propertyMap of the StructureID. (JSC::JSObject::hasGetterSetterProperties): Ditto. (JSC::JSObject::getDirectOffset): Get by indexing into PropertyStorage. (JSC::JSObject::putDirectOffset): Put by indexing into PropertyStorage. (JSC::JSObject::getOwnPropertySlotForWrite): Update to get the propertyMap of the StructureID. (JSC::JSObject::getOwnPropertySlot): Ditto. (JSC::JSObject::putDirect): Move putting into the StructureID unless the property already exists.
  • kjs/PropertyMap.cpp: Use the propertyStorage as the storage for the JSValues. (JSC::PropertyMap::checkConsistency): (JSC::PropertyMap::operator=): (JSC::PropertyMap::~PropertyMap): (JSC::PropertyMap::get): (JSC::PropertyMap::getLocation): (JSC::PropertyMap::put): (JSC::PropertyMap::getOffset): (JSC::PropertyMap::insert): (JSC::PropertyMap::expand): (JSC::PropertyMap::rehash): (JSC::PropertyMap::createTable): (JSC::PropertyMap::resizePropertyStorage): Resize the storage to match the size of the map (JSC::PropertyMap::remove): (JSC::PropertyMap::getEnumerablePropertyNames):
  • kjs/PropertyMap.h: (JSC::PropertyMapEntry::PropertyMapEntry): (JSC::PropertyMap::isEmpty): (JSC::PropertyMap::size): (JSC::PropertyMap::makingCount): (JSC::PropertyMap::PropertyMap):
  • kjs/StructureID.cpp: (JSC::StructureID::addPropertyTransition): Transitions now are based off the property name and attributes. (JSC::StructureID::toDictionaryTransition): Copy the map. (JSC::StructureID::changePrototypeTransition): Copy the map. (JSC::StructureID::getterSetterTransition): Copy the map. (JSC::StructureID::~StructureID):
  • kjs/StructureID.h: (JSC::TransitionTableHash::hash): Custom hash for transition map. (JSC::TransitionTableHash::equal): Ditto. (JSC::TransitionTableHashTraits::emptyValue): Custom traits for transition map (JSC::TransitionTableHashTraits::constructDeletedValue): Ditto. (JSC::TransitionTableHashTraits::isDeletedValue): Ditto. (JSC::StructureID::propertyMap): Added.

JavaScriptGlue:

2008-09-08 Sam Weinig <[email protected]>

Reviewed by Maciej Stachowiak and Oliver Hunt.

Add forwarding headers.

  • ForwardingHeaders/wtf/HashFunctions.h: Added.
  • ForwardingHeaders/wtf/HashTraits.h: Added.

WebCore:

2008-09-08 Sam Weinig <[email protected]>

Reviewed by Maciej Stachowiak and Oliver Hunt.

Add forwarding headers.

  • ForwardingHeaders/wtf/HashFunctions.h: Added.
  • Property svn:eol-style set to native
File size: 16.9 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 <profiler/Profiler.h>
39#include <wtf/Assertions.h>
40
41#define JSOBJECT_MARK_TRACING 0
42
43#if JSOBJECT_MARK_TRACING
44
45#define JSOBJECT_MARK_BEGIN() \
46 static int markStackDepth = 0; \
47 for (int i = 0; i < markStackDepth; i++) \
48 putchar('-'); \
49 printf("%s (%p)\n", className().UTF8String().c_str(), this); \
50 markStackDepth++; \
51
52#define JSOBJECT_MARK_END() \
53 markStackDepth--;
54
55#else // JSOBJECT_MARK_TRACING
56
57#define JSOBJECT_MARK_BEGIN()
58#define JSOBJECT_MARK_END()
59
60#endif // JSOBJECT_MARK_TRACING
61
62namespace JSC {
63
64ASSERT_CLASS_FITS_IN_CELL(JSObject);
65
66void JSObject::mark()
67{
68 JSOBJECT_MARK_BEGIN();
69
70 JSCell::mark();
71 m_structureID->mark();
72
73 unsigned storageSize = m_structureID->propertyMap().makingCount();
74 if (storageSize) {
75 for (unsigned i = 1; i <= storageSize; ++i) {
76 JSValue* v = m_propertyStorage[i];
77 if (!v->marked())
78 v->mark();
79 }
80 }
81
82 JSOBJECT_MARK_END();
83}
84
85UString JSObject::className() const
86{
87 const ClassInfo* info = classInfo();
88 if (info)
89 return info->className;
90 return "Object";
91}
92
93bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
94{
95 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
96}
97
98static void throwSetterError(ExecState* exec)
99{
100 throwError(exec, TypeError, "setting a property that has only a getter");
101}
102
103// ECMA 8.6.2.2
104void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
105{
106 ASSERT(value);
107 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
108
109 if (propertyName == exec->propertyNames().underscoreProto) {
110 JSObject* proto = value->getObject();
111
112 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
113 if (!proto && !value->isNull())
114 return;
115
116 while (proto) {
117 if (proto == this) {
118 throwError(exec, GeneralError, "cyclic __proto__ value");
119 return;
120 }
121 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
122 }
123
124 setPrototype(value);
125 return;
126 }
127
128 // Check if there are any setters or getters in the prototype chain
129 JSValue* prototype;
130 for (JSObject* obj = this; !obj->structureID()->propertyMap().hasGetterSetterProperties(); obj = static_cast<JSObject*>(prototype)) {
131 prototype = obj->prototype();
132 if (prototype->isNull()) {
133 putDirect(propertyName, value, 0, true, slot);
134 return;
135 }
136 }
137
138 unsigned attributes;
139 if (m_structureID->propertyMap().get(propertyName, attributes, m_propertyStorage) && attributes & ReadOnly)
140 return;
141
142 for (JSObject* obj = this; ; obj = static_cast<JSObject*>(prototype)) {
143 if (JSValue* gs = obj->structureID()->propertyMap().get(propertyName, obj->propertyStorage())) {
144 if (gs->isGetterSetter()) {
145 JSObject* setterFunc = static_cast<GetterSetter*>(gs)->setter();
146 if (!setterFunc) {
147 throwSetterError(exec);
148 return;
149 }
150
151 CallData callData;
152 CallType callType = setterFunc->getCallData(callData);
153 ArgList args;
154 args.append(value);
155 call(exec, setterFunc, callType, callData, this, args);
156 return;
157 }
158
159 // If there's an existing property on the object or one of its
160 // prototypes it should be replaced, so break here.
161 break;
162 }
163
164 prototype = obj->prototype();
165 if (prototype->isNull())
166 break;
167 }
168
169 putDirect(propertyName, value, 0, true, slot);
170 return;
171}
172
173void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
174{
175 PutPropertySlot slot;
176 put(exec, Identifier::from(exec, propertyName), value, slot);
177}
178
179void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
180{
181 putDirect(propertyName, value, attributes);
182}
183
184void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
185{
186 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
187}
188
189bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
190{
191 PropertySlot slot;
192 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
193}
194
195bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
196{
197 PropertySlot slot;
198 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
199}
200
201// ECMA 8.6.2.5
202bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
203{
204 unsigned attributes;
205 JSValue* v = m_structureID->propertyMap().get(propertyName, attributes, m_propertyStorage);
206 if (v) {
207 if ((attributes & DontDelete))
208 return false;
209 removeDirect(propertyName);
210 return true;
211 }
212
213 // Look in the static hashtable of properties
214 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
215 if (entry && entry->attributes & DontDelete)
216 return false; // this builtin property can't be deleted
217
218 // FIXME: Should the code here actually do some deletion?
219 return true;
220}
221
222bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
223{
224 PropertySlot slot;
225 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
226}
227
228bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
229{
230 return deleteProperty(exec, Identifier::from(exec, propertyName));
231}
232
233static ALWAYS_INLINE JSValue* callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
234{
235 JSValue* function = object->get(exec, propertyName);
236 CallData callData;
237 CallType callType = function->getCallData(callData);
238 if (callType == CallTypeNone)
239 return exec->exception();
240
241 // Prevent "toString" and "valueOf" from observing execution if an exception
242 // is pending.
243 if (exec->hadException())
244 return exec->exception();
245
246 JSValue* result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
247 ASSERT(!result->isGetterSetter());
248 if (exec->hadException())
249 return exec->exception();
250 if (result->isObject())
251 return 0;
252 return result;
253}
254
255bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
256{
257 result = defaultValue(exec, PreferNumber);
258 number = result->toNumber(exec);
259 return !result->isString();
260}
261
262// ECMA 8.6.2.6
263JSValue* JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
264{
265 // Must call toString first for Date objects.
266 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
267 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
268 return value;
269 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
270 return value;
271 } else {
272 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
273 return value;
274 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
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 JSValue* object = getDirect(propertyName);
297 if (object && object->isGetterSetter()) {
298 ASSERT(m_structureID->propertyMap().hasGetterSetterProperties());
299 GetterSetter* getterSetter = static_cast<GetterSetter*>(object);
300 getterSetter->setGetter(getterFunction);
301 return;
302 }
303
304 PutPropertySlot slot;
305 GetterSetter* getterSetter = new (exec) GetterSetter;
306 putDirect(propertyName, getterSetter, None, true, slot);
307
308 // putDirect will change our StructureID if we add a new property. For
309 // getters and setters, though, we also need to change our StructureID
310 // if we override an existing non-getter or non-setter.
311 if (slot.type() != PutPropertySlot::NewProperty) {
312 if (!m_structureID->isDictionary()) {
313 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
314 setStructureID(structureID.release());
315 }
316 }
317
318 m_structureID->propertyMap().setHasGetterSetterProperties(true);
319 getterSetter->setGetter(getterFunction);
320}
321
322void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
323{
324 JSValue* object = getDirect(propertyName);
325 if (object && object->isGetterSetter()) {
326 ASSERT(m_structureID->propertyMap().hasGetterSetterProperties());
327 GetterSetter* getterSetter = static_cast<GetterSetter*>(object);
328 getterSetter->setSetter(setterFunction);
329 return;
330 }
331
332 PutPropertySlot slot;
333 GetterSetter* getterSetter = new (exec) GetterSetter;
334 putDirect(propertyName, getterSetter, None, true, slot);
335
336 // putDirect will change our StructureID if we add a new property. For
337 // getters and setters, though, we also need to change our StructureID
338 // if we override an existing non-getter or non-setter.
339 if (slot.type() != PutPropertySlot::NewProperty) {
340 if (!m_structureID->isDictionary()) {
341 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
342 setStructureID(structureID.release());
343 }
344 }
345
346 m_structureID->propertyMap().setHasGetterSetterProperties(true);
347 getterSetter->setSetter(setterFunction);
348}
349
350JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
351{
352 JSObject* object = this;
353 while (true) {
354 JSValue* value = object->getDirect(propertyName);
355 if (value) {
356 if (!value->isGetterSetter())
357 return jsUndefined();
358 JSObject* functionObject = static_cast<GetterSetter*>(value)->getter();
359 if (!functionObject)
360 return jsUndefined();
361 return functionObject;
362 }
363
364 if (!object->prototype() || !object->prototype()->isObject())
365 return jsUndefined();
366 object = static_cast<JSObject*>(object->prototype());
367 }
368}
369
370JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
371{
372 JSObject* object = this;
373 while (true) {
374 JSValue* value = object->getDirect(propertyName);
375 if (value) {
376 if (!value->isGetterSetter())
377 return jsUndefined();
378 JSObject* functionObject = static_cast<GetterSetter*>(value)->setter();
379 if (!functionObject)
380 return jsUndefined();
381 return functionObject;
382 }
383
384 if (!object->prototype() || !object->prototype()->isObject())
385 return jsUndefined();
386 object = static_cast<JSObject*>(object->prototype());
387 }
388}
389
390bool JSObject::implementsHasInstance() const
391{
392 return false;
393}
394
395bool JSObject::hasInstance(ExecState* exec, JSValue* value)
396{
397 JSValue* proto = get(exec, exec->propertyNames().prototype);
398 if (!proto->isObject()) {
399 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
400 return false;
401 }
402
403 if (!value->isObject())
404 return false;
405
406 JSObject* object = static_cast<JSObject*>(value);
407 while ((object = object->prototype()->getObject())) {
408 if (object == proto)
409 return true;
410 }
411 return false;
412}
413
414bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
415{
416 unsigned attributes;
417 if (!getPropertyAttributes(exec, propertyName, attributes))
418 return false;
419 return !(attributes & DontEnum);
420}
421
422bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
423{
424 if (m_structureID->propertyMap().get(propertyName, attributes, m_propertyStorage))
425 return true;
426
427 // Look in the static hashtable of properties
428 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
429 if (entry) {
430 attributes = entry->attributes;
431 return true;
432 }
433
434 return false;
435}
436
437void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
438{
439 m_structureID->propertyMap().getEnumerablePropertyNames(propertyNames);
440
441 // Add properties from the static hashtables of properties
442 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
443 const HashTable* table = info->propHashTable(exec);
444 if (!table)
445 continue;
446 table->initializeIfNeeded(exec);
447 ASSERT(table->table);
448 int hashSizeMask = table->hashSizeMask;
449 const HashEntry* entry = table->table;
450 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
451 if (entry->key && !(entry->attributes & DontEnum))
452 propertyNames.add(entry->key);
453 }
454 }
455
456 if (prototype()->isObject())
457 static_cast<JSObject*>(prototype())->getPropertyNames(exec, propertyNames);
458}
459
460bool JSObject::toBoolean(ExecState*) const
461{
462 return true;
463}
464
465double JSObject::toNumber(ExecState* exec) const
466{
467 JSValue* primitive = toPrimitive(exec, PreferNumber);
468 if (exec->hadException()) // should be picked up soon in nodes.cpp
469 return 0.0;
470 return primitive->toNumber(exec);
471}
472
473UString JSObject::toString(ExecState* exec) const
474{
475 JSValue* primitive = toPrimitive(exec, PreferString);
476 if (exec->hadException())
477 return "";
478 return primitive->toString(exec);
479}
480
481JSObject* JSObject::toObject(ExecState*) const
482{
483 return const_cast<JSObject*>(this);
484}
485
486JSObject* JSObject::toThisObject(ExecState*) const
487{
488 return const_cast<JSObject*>(this);
489}
490
491JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
492{
493 return 0;
494}
495
496void JSObject::removeDirect(const Identifier& propertyName)
497{
498 if (m_structureID->isDictionary()) {
499 m_structureID->propertyMap().remove(propertyName, m_propertyStorage);
500 return;
501 }
502
503 RefPtr<StructureID> structureID = StructureID::toDictionaryTransition(m_structureID);
504 structureID->propertyMap().remove(propertyName, m_propertyStorage);
505 setStructureID(structureID.release());
506}
507
508void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
509{
510 putDirect(Identifier(exec, function->name(exec)), function, attr);
511}
512
513NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
514{
515 if (JSObject* getterFunction = static_cast<GetterSetter*>(*location)->getter())
516 slot.setGetterSlot(getterFunction);
517 else
518 slot.setUndefined();
519}
520
521StructureID* JSObject::createInheritorID()
522{
523 m_inheritorID = StructureID::create(this);
524 return m_inheritorID.get();
525}
526
527bool JSObject::isObject() const
528{
529 return true;
530}
531
532JSObject* constructEmptyObject(ExecState* exec)
533{
534 return new (exec) JSObject(exec->lexicalGlobalObject()->objectPrototype());
535}
536
537} // namespace JSC
Note: See TracBrowser for help on using the repository browser.