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

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

2008-09-15 Maciej Stachowiak <[email protected]>

Reviewed by Cameron Zwarich.


  • fix layout test failure introduced by fix for 20849


(The failing test was fast/js/delete-then-put.html)

  • kjs/JSObject.cpp: (JSC::JSObject::removeDirect): Clear enumeration cache in the dictionary case.
  • kjs/JSObject.h: (JSC::JSObject::putDirect): Ditto.
  • kjs/StructureID.h: (JSC::StructureID::clearEnumerationCache): Inline to handle the clear.
  • Property svn:eol-style set to native
File size: 17.0 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()->propertyMap().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().getOffset(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().getOffset(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->propertyMap().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->propertyMap().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->propertyMap().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->propertyMap().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::implementsHasInstance() const
388{
389 return false;
390}
391
392bool JSObject::hasInstance(ExecState* exec, JSValue* value, JSValue* proto)
393{
394 if (!proto->isObject()) {
395 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
396 return false;
397 }
398
399 if (!value->isObject())
400 return false;
401
402 JSObject* object = static_cast<JSObject*>(value);
403 while ((object = object->prototype()->getObject())) {
404 if (object == proto)
405 return true;
406 }
407 return false;
408}
409
410bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
411{
412 unsigned attributes;
413 if (!getPropertyAttributes(exec, propertyName, attributes))
414 return false;
415 return !(attributes & DontEnum);
416}
417
418bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
419{
420 if (m_structureID->propertyMap().getOffset(propertyName, attributes) != WTF::notFound)
421 return true;
422
423 // Look in the static hashtable of properties
424 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
425 if (entry) {
426 attributes = entry->attributes;
427 return true;
428 }
429
430 return false;
431}
432
433void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
434{
435 m_structureID->getEnumerablePropertyNames(propertyNames);
436
437 // Add properties from the static hashtables of properties
438 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
439 const HashTable* table = info->propHashTable(exec);
440 if (!table)
441 continue;
442 table->initializeIfNeeded(exec);
443 ASSERT(table->table);
444 int hashSizeMask = table->hashSizeMask;
445 const HashEntry* entry = table->table;
446 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
447 if (entry->key && !(entry->attributes & DontEnum))
448 propertyNames.add(entry->key);
449 }
450 }
451
452 if (prototype()->isObject())
453 static_cast<JSObject*>(prototype())->getPropertyNames(exec, propertyNames);
454}
455
456bool JSObject::toBoolean(ExecState*) const
457{
458 return true;
459}
460
461double JSObject::toNumber(ExecState* exec) const
462{
463 JSValue* primitive = toPrimitive(exec, PreferNumber);
464 if (exec->hadException()) // should be picked up soon in nodes.cpp
465 return 0.0;
466 return primitive->toNumber(exec);
467}
468
469UString JSObject::toString(ExecState* exec) const
470{
471 JSValue* primitive = toPrimitive(exec, PreferString);
472 if (exec->hadException())
473 return "";
474 return primitive->toString(exec);
475}
476
477JSObject* JSObject::toObject(ExecState*) const
478{
479 return const_cast<JSObject*>(this);
480}
481
482JSObject* JSObject::toThisObject(ExecState*) const
483{
484 return const_cast<JSObject*>(this);
485}
486
487JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
488{
489 return 0;
490}
491
492void JSObject::removeDirect(const Identifier& propertyName)
493{
494 if (m_structureID->isDictionary()) {
495 m_structureID->propertyMap().remove(propertyName, m_propertyStorage);
496 m_structureID->clearEnumerationCache();
497 return;
498 }
499
500 RefPtr<StructureID> structureID = StructureID::toDictionaryTransition(m_structureID);
501 structureID->propertyMap().remove(propertyName, m_propertyStorage);
502 setStructureID(structureID.release());
503}
504
505void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
506{
507 putDirect(Identifier(exec, function->name(exec)), function, attr);
508}
509
510NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
511{
512 if (JSObject* getterFunction = static_cast<GetterSetter*>(*location)->getter())
513 slot.setGetterSlot(getterFunction);
514 else
515 slot.setUndefined();
516}
517
518StructureID* JSObject::createInheritorID()
519{
520 m_inheritorID = StructureID::create(this);
521 return m_inheritorID.get();
522}
523
524void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
525{
526 JSValue** oldPropertStorage = m_propertyStorage;
527 m_propertyStorage = new JSValue*[newSize];
528
529 for (unsigned i = 0; i < oldSize; ++i)
530 m_propertyStorage[i] = oldPropertStorage[i];
531
532 if (oldPropertStorage != m_inlineStorage)
533 delete oldPropertStorage;
534}
535
536JSObject* constructEmptyObject(ExecState* exec)
537{
538 return new (exec) JSObject(exec->lexicalGlobalObject()->objectPrototype());
539}
540
541} // namespace JSC
Note: See TracBrowser for help on using the repository browser.