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

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

2008-10-16 Sam Weinig <[email protected]>

Reviewed by Maciej Stachowiak.

Fix for https://bugs.webkit.org/show_bug.cgi?id=21683
Don't create intermediate StructureIDs for builtin objects

First step in reduce number of StructureIDs created when initializing the
JSGlobalObject.

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