Unreviewed, rolling out r94445 and r94448.
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.cpp
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
5  *  Copyright (C) 2007 Eric Seidel (eric@webkit.org)
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 "JSFunction.h"
31 #include "JSGlobalObject.h"
32 #include "NativeErrorConstructor.h"
33 #include "ObjectPrototype.h"
34 #include "PropertyDescriptor.h"
35 #include "PropertyNameArray.h"
36 #include "Lookup.h"
37 #include "Nodes.h"
38 #include "Operations.h"
39 #include <math.h>
40 #include <wtf/Assertions.h>
41
42 namespace JSC {
43
44 ASSERT_CLASS_FITS_IN_CELL(JSObject);
45 ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject);
46 ASSERT_CLASS_FITS_IN_CELL(JSFinalObject);
47
48 const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
49
50 const ClassInfo JSObject::s_info = { "Object", 0, 0, 0 };
51
52 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
53 {
54     // Add properties from the static hashtables of properties
55     for (; classInfo; classInfo = classInfo->parentClass) {
56         const HashTable* table = classInfo->propHashTable(exec);
57         if (!table)
58             continue;
59         table->initializeIfNeeded(exec);
60         ASSERT(table->table);
61
62         int hashSizeMask = table->compactSize - 1;
63         const HashEntry* entry = table->table;
64         for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
65             if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
66                 propertyNames.add(entry->key());
67         }
68     }
69 }
70
71 void JSObject::visitChildren(SlotVisitor& visitor)
72 {
73     ASSERT_GC_OBJECT_INHERITS(this, &s_info);
74 #ifndef NDEBUG
75     bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
76     visitor.m_isCheckingForDefaultMarkViolation = false;
77 #endif
78
79     visitChildrenDirect(visitor);
80
81 #ifndef NDEBUG
82     visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
83 #endif
84 }
85
86 UString JSObject::className() const
87 {
88     const ClassInfo* info = classInfo();
89     ASSERT(info);
90     return info->className;
91 }
92
93 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
94 {
95     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
96 }
97
98 static void throwSetterError(ExecState* exec)
99 {
100     throwError(exec, createTypeError(exec, "setting a property that has only a getter"));
101 }
102
103 // ECMA 8.6.2.2
104 void 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         // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
111         if (!value.isObject() && !value.isNull())
112             return;
113
114         if (!isExtensible()) {
115             if (slot.isStrictMode())
116                 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
117             return;
118         }
119             
120         if (!setPrototypeWithCycleCheck(exec->globalData(), value))
121             throwError(exec, createError(exec, "cyclic __proto__ 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->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
128         prototype = obj->prototype();
129         if (prototype.isNull()) {
130             if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode())
131                 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
132             return;
133         }
134     }
135     
136     unsigned attributes;
137     JSCell* specificValue;
138     if ((m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) {
139         if (slot.isStrictMode())
140             throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
141         return;
142     }
143
144     for (JSObject* obj = this; ; obj = asObject(prototype)) {
145         if (JSValue gs = obj->getDirect(exec->globalData(), propertyName)) {
146             if (gs.isGetterSetter()) {
147                 JSObject* setterFunc = asGetterSetter(gs)->setter();        
148                 if (!setterFunc) {
149                     throwSetterError(exec);
150                     return;
151                 }
152                 
153                 CallData callData;
154                 CallType callType = setterFunc->getCallData(callData);
155                 MarkedArgumentBuffer args;
156                 args.append(value);
157
158                 // If this is WebCore's global object then we need to substitute the shell.
159                 call(exec, setterFunc, callType, callData, this->toThisObject(exec), args);
160                 return;
161             }
162
163             // If there's an existing property on the object or one of its 
164             // prototypes it should be replaced, so break here.
165             break;
166         }
167
168         prototype = obj->prototype();
169         if (prototype.isNull())
170             break;
171     }
172
173     if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode())
174         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
175     return;
176 }
177
178 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
179 {
180     PutPropertySlot slot;
181     put(exec, Identifier::from(exec, propertyName), value, slot);
182 }
183
184 void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
185 {
186     putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot);
187 }
188
189 void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
190 {
191     putDirectInternal(*globalData, propertyName, value, attributes);
192 }
193
194 void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes)
195 {
196     putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes);
197 }
198
199 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
200 {
201     putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
202 }
203
204 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
205 {
206     putDirectInternal(exec->globalData(), propertyName, value, attributes);
207 }
208
209 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
210 {
211     putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
212 }
213
214 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
215 {
216     PropertySlot slot;
217     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
218 }
219
220 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
221 {
222     PropertySlot slot;
223     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
224 }
225
226 // ECMA 8.6.2.5
227 bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
228 {
229     unsigned attributes;
230     JSCell* specificValue;
231     if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) {
232         if ((attributes & DontDelete))
233             return false;
234         removeDirect(exec->globalData(), propertyName);
235         return true;
236     }
237
238     // Look in the static hashtable of properties
239     const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
240     if (entry && entry->attributes() & DontDelete)
241         return false; // this builtin property can't be deleted
242
243     // FIXME: Should the code here actually do some deletion?
244     return true;
245 }
246
247 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
248 {
249     PropertySlot slot;
250     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
251 }
252
253 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
254 {
255     return deleteProperty(exec, Identifier::from(exec, propertyName));
256 }
257
258 static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
259 {
260     JSValue function = object->get(exec, propertyName);
261     CallData callData;
262     CallType callType = getCallData(function, callData);
263     if (callType == CallTypeNone)
264         return exec->exception();
265
266     // Prevent "toString" and "valueOf" from observing execution if an exception
267     // is pending.
268     if (exec->hadException())
269         return exec->exception();
270
271     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
272     ASSERT(!result.isGetterSetter());
273     if (exec->hadException())
274         return exec->exception();
275     if (result.isObject())
276         return JSValue();
277     return result;
278 }
279
280 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
281 {
282     result = defaultValue(exec, PreferNumber);
283     number = result.toNumber(exec);
284     return !result.isString();
285 }
286
287 // ECMA 8.6.2.6
288 JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
289 {
290     // Must call toString first for Date objects.
291     if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
292         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
293         if (value)
294             return value;
295         value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
296         if (value)
297             return value;
298     } else {
299         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
300         if (value)
301             return value;
302         value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
303         if (value)
304             return value;
305     }
306
307     ASSERT(!exec->hadException());
308
309     return throwError(exec, createTypeError(exec, "No default value"));
310 }
311
312 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
313 {
314     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
315         if (const HashTable* propHashTable = info->propHashTable(exec)) {
316             if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
317                 return entry;
318         }
319     }
320     return 0;
321 }
322
323 void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
324 {
325     JSValue object = getDirect(exec->globalData(), propertyName);
326     if (object && object.isGetterSetter()) {
327         ASSERT(m_structure->hasGetterSetterProperties());
328         asGetterSetter(object)->setGetter(exec->globalData(), getterFunction);
329         return;
330     }
331
332     JSGlobalData& globalData = exec->globalData();
333     PutPropertySlot slot;
334     GetterSetter* getterSetter = GetterSetter::create(exec);
335     putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot);
336
337     // putDirect will change our Structure if we add a new property. For
338     // getters and setters, though, we also need to change our Structure
339     // if we override an existing non-getter or non-setter.
340     if (slot.type() != PutPropertySlot::NewProperty) {
341         if (!m_structure->isDictionary())
342             setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get()));
343     }
344
345     m_structure->setHasGetterSetterProperties(true);
346     getterSetter->setGetter(globalData, getterFunction);
347 }
348
349 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
350 {
351     JSValue object = getDirect(exec->globalData(), propertyName);
352     if (object && object.isGetterSetter()) {
353         ASSERT(m_structure->hasGetterSetterProperties());
354         asGetterSetter(object)->setSetter(exec->globalData(), setterFunction);
355         return;
356     }
357
358     PutPropertySlot slot;
359     GetterSetter* getterSetter = GetterSetter::create(exec);
360     putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
361
362     // putDirect will change our Structure if we add a new property. For
363     // getters and setters, though, we also need to change our Structure
364     // if we override an existing non-getter or non-setter.
365     if (slot.type() != PutPropertySlot::NewProperty) {
366         if (!m_structure->isDictionary())
367             setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get()));
368     }
369
370     m_structure->setHasGetterSetterProperties(true);
371     getterSetter->setSetter(exec->globalData(), setterFunction);
372 }
373
374 JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName)
375 {
376     JSObject* object = this;
377     while (true) {
378         if (JSValue value = object->getDirect(exec->globalData(), propertyName)) {
379             if (!value.isGetterSetter())
380                 return jsUndefined();
381             JSObject* functionObject = asGetterSetter(value)->getter();
382             if (!functionObject)
383                 return jsUndefined();
384             return functionObject;
385         }
386
387         if (!object->prototype() || !object->prototype().isObject())
388             return jsUndefined();
389         object = asObject(object->prototype());
390     }
391 }
392
393 JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName)
394 {
395     JSObject* object = this;
396     while (true) {
397         if (JSValue value = object->getDirect(exec->globalData(), propertyName)) {
398             if (!value.isGetterSetter())
399                 return jsUndefined();
400             JSObject* functionObject = asGetterSetter(value)->setter();
401             if (!functionObject)
402                 return jsUndefined();
403             return functionObject;
404         }
405
406         if (!object->prototype() || !object->prototype().isObject())
407             return jsUndefined();
408         object = asObject(object->prototype());
409     }
410 }
411
412 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
413 {
414     if (!value.isObject())
415         return false;
416
417     if (!proto.isObject()) {
418         throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property."));
419         return false;
420     }
421
422     JSObject* object = asObject(value);
423     while ((object = object->prototype().getObject())) {
424         if (proto == object)
425             return true;
426     }
427     return false;
428 }
429
430 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
431 {
432     PropertyDescriptor descriptor;
433     if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
434         return false;
435     return descriptor.enumerable();
436 }
437
438 bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const
439 {
440     unsigned attributes;
441     if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound)
442         return true;
443
444     // This could be a function within the static table? - should probably
445     // also look in the hash?  This currently should not be a problem, since
446     // we've currently always call 'get' first, which should have populated
447     // the normal storage.
448     return false;
449 }
450
451 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
452 {
453     getOwnPropertyNames(exec, propertyNames, mode);
454
455     if (prototype().isNull())
456         return;
457
458     JSObject* prototype = asObject(this->prototype());
459     while(1) {
460         if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
461             prototype->getPropertyNames(exec, propertyNames, mode);
462             break;
463         }
464         prototype->getOwnPropertyNames(exec, propertyNames, mode);
465         JSValue nextProto = prototype->prototype();
466         if (nextProto.isNull())
467             break;
468         prototype = asObject(nextProto);
469     }
470 }
471
472 void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
473 {
474     m_structure->getPropertyNames(exec->globalData(), propertyNames, mode);
475     getClassPropertyNames(exec, classInfo(), propertyNames, mode);
476 }
477
478 bool JSObject::toBoolean(ExecState*) const
479 {
480     return true;
481 }
482
483 double JSObject::toNumber(ExecState* exec) const
484 {
485     JSValue primitive = toPrimitive(exec, PreferNumber);
486     if (exec->hadException()) // should be picked up soon in Nodes.cpp
487         return 0.0;
488     return primitive.toNumber(exec);
489 }
490
491 UString JSObject::toString(ExecState* exec) const
492 {
493     JSValue primitive = toPrimitive(exec, PreferString);
494     if (exec->hadException())
495         return "";
496     return primitive.toString(exec);
497 }
498
499 JSObject* JSObject::toObject(ExecState*, JSGlobalObject*) const
500 {
501     return const_cast<JSObject*>(this);
502 }
503
504 JSObject* JSObject::toThisObject(ExecState*) const
505 {
506     return const_cast<JSObject*>(this);
507 }
508
509 JSValue JSObject::toStrictThisObject(ExecState*) const
510 {
511     return const_cast<JSObject*>(this);
512 }
513
514 JSObject* JSObject::unwrappedObject()
515 {
516     return this;
517 }
518
519 void JSObject::seal(JSGlobalData& globalData)
520 {
521     if (isSealed(globalData))
522         return;
523     preventExtensions(globalData);
524     setStructure(globalData, Structure::sealTransition(globalData, m_structure.get()));
525 }
526
527 void JSObject::freeze(JSGlobalData& globalData)
528 {
529     if (isFrozen(globalData))
530         return;
531     preventExtensions(globalData);
532     setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get()));
533 }
534
535 void JSObject::preventExtensions(JSGlobalData& globalData)
536 {
537     if (isExtensible())
538         setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get()));
539 }
540
541 void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName)
542 {
543     if (m_structure->get(globalData, propertyName) == WTF::notFound)
544         return;
545
546     size_t offset;
547     if (m_structure->isUncacheableDictionary()) {
548         offset = m_structure->removePropertyWithoutTransition(globalData, propertyName);
549         if (offset != WTF::notFound)
550             putUndefinedAtDirectOffset(offset);
551         return;
552     }
553
554     setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset));
555     if (offset != WTF::notFound)
556         putUndefinedAtDirectOffset(offset);
557 }
558
559 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
560 {
561     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
562 }
563
564 void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr)
565 {
566     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
567 }
568
569 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
570 {
571     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
572 }
573
574 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr)
575 {
576     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
577 }
578
579 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
580 {
581     if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
582         if (!structure()->isDictionary())
583             slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
584         else
585             slot.setGetterSlot(getterFunction);
586     } else
587         slot.setUndefined();
588 }
589
590 Structure* JSObject::createInheritorID(JSGlobalData& globalData)
591 {
592     m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, this));
593     ASSERT(m_inheritorID->isEmpty());
594     return m_inheritorID.get();
595 }
596
597 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
598 {
599     ASSERT(newSize > oldSize);
600
601     // It's important that this function not rely on m_structure, since
602     // we might be in the middle of a transition.
603     bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity);
604
605     PropertyStorage oldPropertyStorage = m_propertyStorage;
606     PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
607
608     for (unsigned i = 0; i < oldSize; ++i)
609        newPropertyStorage[i] = oldPropertyStorage[i];
610
611     if (!wasInline)
612         delete [] oldPropertyStorage;
613
614     m_propertyStorage = newPropertyStorage;
615 }
616
617 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
618 {
619     unsigned attributes = 0;
620     JSCell* cell = 0;
621     size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell);
622     if (offset == WTF::notFound)
623         return false;
624     descriptor.setDescriptor(getDirectOffset(offset), attributes);
625     return true;
626 }
627
628 bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
629 {
630     JSObject* object = this;
631     while (true) {
632         if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
633             return true;
634         JSValue prototype = object->prototype();
635         if (!prototype.isObject())
636             return false;
637         object = asObject(prototype);
638     }
639 }
640
641 static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
642 {
643     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
644         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
645             GetterSetter* accessor = GetterSetter::create(exec);
646             if (oldDescriptor.getter()) {
647                 attributes |= Getter;
648                 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
649             }
650             if (oldDescriptor.setter()) {
651                 attributes |= Setter;
652                 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
653             }
654             target->putWithAttributes(exec, propertyName, accessor, attributes);
655             return true;
656         }
657         JSValue newValue = jsUndefined();
658         if (descriptor.value())
659             newValue = descriptor.value();
660         else if (oldDescriptor.value())
661             newValue = oldDescriptor.value();
662         target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter));
663         return true;
664     }
665     attributes &= ~ReadOnly;
666     if (descriptor.getter() && descriptor.getter().isObject())
667         target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
668     if (exec->hadException())
669         return false;
670     if (descriptor.setter() && descriptor.setter().isObject())
671         target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
672     return !exec->hadException();
673 }
674
675 bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
676 {
677     // If we have a new property we can just put it on normally
678     PropertyDescriptor current;
679     if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
680         // unless extensions are prevented!
681         if (!isExtensible()) {
682             if (throwException)
683                 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
684             return false;
685         }
686         PropertyDescriptor oldDescriptor;
687         oldDescriptor.setValue(jsUndefined());
688         return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
689     }
690
691     if (descriptor.isEmpty())
692         return true;
693
694     if (current.equalTo(exec, descriptor))
695         return true;
696
697     // Filter out invalid changes
698     if (!current.configurable()) {
699         if (descriptor.configurable()) {
700             if (throwException)
701                 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
702             return false;
703         }
704         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
705             if (throwException)
706                 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
707             return false;
708         }
709     }
710
711     // A generic descriptor is simply changing the attributes of an existing property
712     if (descriptor.isGenericDescriptor()) {
713         if (!current.attributesEqual(descriptor)) {
714             deleteProperty(exec, propertyName);
715             putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
716         }
717         return true;
718     }
719
720     // Changing between a normal property or an accessor property
721     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
722         if (!current.configurable()) {
723             if (throwException)
724                 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
725             return false;
726         }
727         deleteProperty(exec, propertyName);
728         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
729     }
730
731     // Changing the value and attributes of an existing property
732     if (descriptor.isDataDescriptor()) {
733         if (!current.configurable()) {
734             if (!current.writable() && descriptor.writable()) {
735                 if (throwException)
736                     throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
737                 return false;
738             }
739             if (!current.writable()) {
740                 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
741                     if (throwException)
742                         throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
743                     return false;
744                 }
745             }
746         } else if (current.attributesEqual(descriptor)) {
747             if (!descriptor.value())
748                 return true;
749             PutPropertySlot slot;
750             put(exec, propertyName, descriptor.value(), slot);
751             if (exec->hadException())
752                 return false;
753             return true;
754         }
755         deleteProperty(exec, propertyName);
756         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
757     }
758
759     // Changing the accessor functions of an existing accessor property
760     ASSERT(descriptor.isAccessorDescriptor());
761     if (!current.configurable()) {
762         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
763             if (throwException)
764                 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
765             return false;
766         }
767         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
768             if (throwException)
769                 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
770             return false;
771         }
772     }
773     JSValue accessor = getDirect(exec->globalData(), propertyName);
774     if (!accessor)
775         return false;
776     GetterSetter* getterSetter = asGetterSetter(accessor);
777     if (current.attributesEqual(descriptor)) {
778         if (descriptor.setter())
779             getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
780         if (descriptor.getter())
781             getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
782         return true;
783     }
784     deleteProperty(exec, propertyName);
785     unsigned attrs = current.attributesWithOverride(descriptor);
786     if (descriptor.setter())
787         attrs |= Setter;
788     if (descriptor.getter())
789         attrs |= Getter;
790     putDirect(exec->globalData(), propertyName, getterSetter, attrs);
791     return true;
792 }
793
794 JSObject* throwTypeError(ExecState* exec, const UString& message)
795 {
796     return throwError(exec, createTypeError(exec, message));
797 }
798
799 } // namespace JSC