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

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

Re-land SNES fix, with correct assertion

RS=Maciej Stachowiak

  • Property svn:eol-style set to native
File size: 23.2 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, 2009 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 "PropertyDescriptor.h"
34#include "PropertyNameArray.h"
35#include "Lookup.h"
36#include "Nodes.h"
37#include "Operations.h"
38#include <math.h>
39#include <wtf/Assertions.h>
40
41namespace JSC {
42
43ASSERT_CLASS_FITS_IN_CELL(JSObject);
44
45void JSObject::markChildren(MarkStack& markStack)
46{
47#ifndef NDEBUG
48 bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
49 markStack.m_isCheckingForDefaultMarkViolation = false;
50#endif
51
52 markChildrenDirect(markStack);
53
54#ifndef NDEBUG
55 markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
56#endif
57}
58
59UString JSObject::className() const
60{
61 const ClassInfo* info = classInfo();
62 if (info)
63 return info->className;
64 return "Object";
65}
66
67bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
68{
69 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
70}
71
72static void throwSetterError(ExecState* exec)
73{
74 throwError(exec, TypeError, "setting a property that has only a getter");
75}
76
77// ECMA 8.6.2.2
78void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
79{
80 ASSERT(value);
81 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
82
83 if (propertyName == exec->propertyNames().underscoreProto) {
84 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
85 if (!value.isObject() && !value.isNull())
86 return;
87
88 JSValue nextPrototypeValue = value;
89 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
90 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
91 if (nextPrototype == this) {
92 throwError(exec, GeneralError, "cyclic __proto__ value");
93 return;
94 }
95 nextPrototypeValue = nextPrototype->prototype();
96 }
97
98 setPrototype(value);
99 return;
100 }
101
102 // Check if there are any setters or getters in the prototype chain
103 JSValue prototype;
104 for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
105 prototype = obj->prototype();
106 if (prototype.isNull()) {
107 putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
108 return;
109 }
110 }
111
112 unsigned attributes;
113 JSCell* specificValue;
114 if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
115 return;
116
117 for (JSObject* obj = this; ; obj = asObject(prototype)) {
118 if (JSValue gs = obj->getDirect(propertyName)) {
119 if (gs.isGetterSetter()) {
120 JSObject* setterFunc = asGetterSetter(gs)->setter();
121 if (!setterFunc) {
122 throwSetterError(exec);
123 return;
124 }
125
126 CallData callData;
127 CallType callType = setterFunc->getCallData(callData);
128 MarkedArgumentBuffer args;
129 args.append(value);
130 call(exec, setterFunc, callType, callData, this, args);
131 return;
132 }
133
134 // If there's an existing property on the object or one of its
135 // prototypes it should be replaced, so break here.
136 break;
137 }
138
139 prototype = obj->prototype();
140 if (prototype.isNull())
141 break;
142 }
143
144 putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
145 return;
146}
147
148void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
149{
150 PutPropertySlot slot;
151 put(exec, Identifier::from(exec, propertyName), value, slot);
152}
153
154void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
155{
156 putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
157}
158
159void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
160{
161 putDirectInternal(exec->globalData(), propertyName, value, attributes);
162}
163
164void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
165{
166 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
167}
168
169bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
170{
171 PropertySlot slot;
172 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
173}
174
175bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
176{
177 PropertySlot slot;
178 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
179}
180
181// ECMA 8.6.2.5
182bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
183{
184 unsigned attributes;
185 JSCell* specificValue;
186 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
187 if ((attributes & DontDelete))
188 return false;
189 removeDirect(propertyName);
190 return true;
191 }
192
193 // Look in the static hashtable of properties
194 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
195 if (entry && entry->attributes() & DontDelete)
196 return false; // this builtin property can't be deleted
197
198 // FIXME: Should the code here actually do some deletion?
199 return true;
200}
201
202bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
203{
204 PropertySlot slot;
205 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
206}
207
208bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
209{
210 return deleteProperty(exec, Identifier::from(exec, propertyName));
211}
212
213static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
214{
215 JSValue function = object->get(exec, propertyName);
216 CallData callData;
217 CallType callType = function.getCallData(callData);
218 if (callType == CallTypeNone)
219 return exec->exception();
220
221 // Prevent "toString" and "valueOf" from observing execution if an exception
222 // is pending.
223 if (exec->hadException())
224 return exec->exception();
225
226 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
227 ASSERT(!result.isGetterSetter());
228 if (exec->hadException())
229 return exec->exception();
230 if (result.isObject())
231 return JSValue();
232 return result;
233}
234
235bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
236{
237 result = defaultValue(exec, PreferNumber);
238 number = result.toNumber(exec);
239 return !result.isString();
240}
241
242// ECMA 8.6.2.6
243JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
244{
245 // Must call toString first for Date objects.
246 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
247 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
248 if (value)
249 return value;
250 value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
251 if (value)
252 return value;
253 } else {
254 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
255 if (value)
256 return value;
257 value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
258 if (value)
259 return value;
260 }
261
262 ASSERT(!exec->hadException());
263
264 return throwError(exec, TypeError, "No default value");
265}
266
267const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
268{
269 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
270 if (const HashTable* propHashTable = info->propHashTable(exec)) {
271 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
272 return entry;
273 }
274 }
275 return 0;
276}
277
278void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
279{
280 JSValue object = getDirect(propertyName);
281 if (object && object.isGetterSetter()) {
282 ASSERT(m_structure->hasGetterSetterProperties());
283 asGetterSetter(object)->setGetter(getterFunction);
284 return;
285 }
286
287 PutPropertySlot slot;
288 GetterSetter* getterSetter = new (exec) GetterSetter(exec);
289 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
290
291 // putDirect will change our Structure if we add a new property. For
292 // getters and setters, though, we also need to change our Structure
293 // if we override an existing non-getter or non-setter.
294 if (slot.type() != PutPropertySlot::NewProperty) {
295 if (!m_structure->isDictionary()) {
296 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
297 setStructure(structure.release());
298 }
299 }
300
301 m_structure->setHasGetterSetterProperties(true);
302 getterSetter->setGetter(getterFunction);
303}
304
305void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
306{
307 JSValue object = getDirect(propertyName);
308 if (object && object.isGetterSetter()) {
309 ASSERT(m_structure->hasGetterSetterProperties());
310 asGetterSetter(object)->setSetter(setterFunction);
311 return;
312 }
313
314 PutPropertySlot slot;
315 GetterSetter* getterSetter = new (exec) GetterSetter(exec);
316 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
317
318 // putDirect will change our Structure if we add a new property. For
319 // getters and setters, though, we also need to change our Structure
320 // if we override an existing non-getter or non-setter.
321 if (slot.type() != PutPropertySlot::NewProperty) {
322 if (!m_structure->isDictionary()) {
323 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
324 setStructure(structure.release());
325 }
326 }
327
328 m_structure->setHasGetterSetterProperties(true);
329 getterSetter->setSetter(setterFunction);
330}
331
332JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
333{
334 JSObject* object = this;
335 while (true) {
336 if (JSValue value = object->getDirect(propertyName)) {
337 if (!value.isGetterSetter())
338 return jsUndefined();
339 JSObject* functionObject = asGetterSetter(value)->getter();
340 if (!functionObject)
341 return jsUndefined();
342 return functionObject;
343 }
344
345 if (!object->prototype() || !object->prototype().isObject())
346 return jsUndefined();
347 object = asObject(object->prototype());
348 }
349}
350
351JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
352{
353 JSObject* object = this;
354 while (true) {
355 if (JSValue value = object->getDirect(propertyName)) {
356 if (!value.isGetterSetter())
357 return jsUndefined();
358 JSObject* functionObject = asGetterSetter(value)->setter();
359 if (!functionObject)
360 return jsUndefined();
361 return functionObject;
362 }
363
364 if (!object->prototype() || !object->prototype().isObject())
365 return jsUndefined();
366 object = asObject(object->prototype());
367 }
368}
369
370bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
371{
372 if (!value.isObject())
373 return false;
374
375 if (!proto.isObject()) {
376 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
377 return false;
378 }
379
380 JSObject* object = asObject(value);
381 while ((object = object->prototype().getObject())) {
382 if (proto == object)
383 return true;
384 }
385 return false;
386}
387
388bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
389{
390 unsigned attributes;
391 if (!getPropertyAttributes(exec, propertyName, attributes))
392 return false;
393 return !(attributes & DontEnum);
394}
395
396bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
397{
398 JSCell* specificValue;
399 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
400 return true;
401
402 // Look in the static hashtable of properties
403 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
404 if (entry) {
405 attributes = entry->attributes();
406 return true;
407 }
408
409 return false;
410}
411
412bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
413{
414 unsigned attributes;
415 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
416 return true;
417
418 // This could be a function within the static table? - should probably
419 // also look in the hash? This currently should not be a problem, since
420 // we've currently always call 'get' first, which should have populated
421 // the normal storage.
422 return false;
423}
424
425void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
426{
427 m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
428}
429
430void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
431{
432 m_structure->getOwnEnumerablePropertyNames(exec, propertyNames, this);
433}
434
435bool JSObject::toBoolean(ExecState*) const
436{
437 return true;
438}
439
440double JSObject::toNumber(ExecState* exec) const
441{
442 JSValue primitive = toPrimitive(exec, PreferNumber);
443 if (exec->hadException()) // should be picked up soon in Nodes.cpp
444 return 0.0;
445 return primitive.toNumber(exec);
446}
447
448UString JSObject::toString(ExecState* exec) const
449{
450 JSValue primitive = toPrimitive(exec, PreferString);
451 if (exec->hadException())
452 return "";
453 return primitive.toString(exec);
454}
455
456JSObject* JSObject::toObject(ExecState*) const
457{
458 return const_cast<JSObject*>(this);
459}
460
461JSObject* JSObject::toThisObject(ExecState*) const
462{
463 return const_cast<JSObject*>(this);
464}
465
466JSObject* JSObject::unwrappedObject()
467{
468 return this;
469}
470
471void JSObject::removeDirect(const Identifier& propertyName)
472{
473 size_t offset;
474 if (m_structure->isUncacheableDictionary()) {
475 offset = m_structure->removePropertyWithoutTransition(propertyName);
476 if (offset != WTF::notFound)
477 putDirectOffset(offset, jsUndefined());
478 return;
479 }
480
481 RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
482 setStructure(structure.release());
483 if (offset != WTF::notFound)
484 putDirectOffset(offset, jsUndefined());
485}
486
487void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
488{
489 putDirectFunction(Identifier(exec, function->name(&exec->globalData())), function, attr);
490}
491
492void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
493{
494 putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
495}
496
497NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
498{
499 if (JSObject* getterFunction = asGetterSetter(*location)->getter())
500 slot.setGetterSlot(getterFunction);
501 else
502 slot.setUndefined();
503}
504
505Structure* JSObject::createInheritorID()
506{
507 m_inheritorID = JSObject::createStructure(this);
508 return m_inheritorID.get();
509}
510
511void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
512{
513 allocatePropertyStorageInline(oldSize, newSize);
514}
515
516JSObject* constructEmptyObject(ExecState* exec)
517{
518 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
519}
520
521bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
522{
523 unsigned attributes = 0;
524 JSCell* cell = 0;
525 size_t offset = m_structure->get(propertyName, attributes, cell);
526 if (offset == WTF::notFound)
527 return false;
528 descriptor.setDescriptor(getDirectOffset(offset), attributes);
529 return true;
530}
531
532bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
533{
534 JSObject* object = this;
535 while (true) {
536 if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
537 return true;
538 JSValue prototype = object->prototype();
539 if (!prototype.isObject())
540 return false;
541 object = asObject(prototype);
542 }
543}
544
545static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue)
546{
547 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
548 target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter));
549 return true;
550 }
551 attributes &= ~ReadOnly;
552 if (descriptor.getter() && descriptor.getter().isObject())
553 target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
554 if (exec->hadException())
555 return false;
556 if (descriptor.setter() && descriptor.setter().isObject())
557 target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
558 return !exec->hadException();
559}
560
561bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
562{
563 // If we have a new property we can just put it on normally
564 PropertyDescriptor current;
565 if (!getOwnPropertyDescriptor(exec, propertyName, current))
566 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined());
567
568 if (descriptor.isEmpty())
569 return true;
570
571 if (current.equalTo(descriptor))
572 return true;
573
574 // Filter out invalid changes
575 if (!current.configurable()) {
576 if (descriptor.configurable()) {
577 if (throwException)
578 throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property.");
579 return false;
580 }
581 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
582 if (throwException)
583 throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property.");
584 return false;
585 }
586 }
587
588 // A generic descriptor is simply changing the attributes of an existing property
589 if (descriptor.isGenericDescriptor()) {
590 if (!current.attributesEqual(descriptor)) {
591 deleteProperty(exec, propertyName);
592 putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
593 }
594 return true;
595 }
596
597 // Changing between a normal property or an accessor property
598 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
599 if (!current.configurable()) {
600 if (throwException)
601 throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property.");
602 return false;
603 }
604 deleteProperty(exec, propertyName);
605 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined());
606 }
607
608 // Changing the value and attributes of an existing property
609 if (descriptor.isDataDescriptor()) {
610 if (!current.configurable()) {
611 if (!current.writable() && descriptor.writable()) {
612 if (throwException)
613 throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property.");
614 return false;
615 }
616 if (!current.writable()) {
617 if (descriptor.value() || !JSValue::strictEqual(current.value(), descriptor.value())) {
618 if (throwException)
619 throwError(exec, TypeError, "Attempting to change value of a readonly property.");
620 return false;
621 }
622 }
623 } else if (current.attributesEqual(descriptor)) {
624 if (!descriptor.value())
625 return true;
626 PutPropertySlot slot;
627 put(exec, propertyName, descriptor.value(), slot);
628 if (exec->hadException())
629 return false;
630 return true;
631 }
632 deleteProperty(exec, propertyName);
633 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
634 }
635
636 // Changing the accessor functions of an existing accessor property
637 ASSERT(descriptor.isAccessorDescriptor());
638 if (!current.configurable()) {
639 if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(current.setter(), descriptor.setter()))) {
640 if (throwException)
641 throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property.");
642 return false;
643 }
644 if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(current.getter(), descriptor.getter()))) {
645 if (throwException)
646 throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property.");
647 return false;
648 }
649 }
650 JSValue accessor = getDirect(propertyName);
651 if (!accessor)
652 return false;
653 GetterSetter* getterSetter = asGetterSetter(accessor);
654 if (current.attributesEqual(descriptor)) {
655 if (descriptor.setter())
656 getterSetter->setSetter(asObject(descriptor.setter()));
657 if (descriptor.getter())
658 getterSetter->setGetter(asObject(descriptor.getter()));
659 return true;
660 }
661 deleteProperty(exec, propertyName);
662 unsigned attrs = current.attributesWithOverride(descriptor);
663 if (descriptor.setter())
664 attrs |= Setter;
665 if (descriptor.getter())
666 attrs |= Getter;
667 putDirect(propertyName, getterSetter, attrs);
668 return true;
669}
670
671} // namespace JSC
Note: See TracBrowser for help on using the repository browser.