aaaacae8c2ff4e4bccd1f8e05ba72c969e0dbd71
[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(JSGlobalData& globalData, 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     PropertyStorage oldPropertyStorage = m_propertyStorage;
604     PropertyStorage newPropertyStorage = static_cast<PropertyStorage>(globalData.heap.allocatePropertyStorage(newSize * sizeof(WriteBarrierBase<Unknown>)));
605     ASSERT(newPropertyStorage);
606
607     for (unsigned i = 0; i < oldSize; ++i)
608        newPropertyStorage[i] = oldPropertyStorage[i];
609
610     if (!isUsingInlineStorage() && !globalData.heap.inPropertyStorageNursery(oldPropertyStorage))
611         delete [] oldPropertyStorage;
612
613     m_propertyStorage = newPropertyStorage;
614 }
615
616 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
617 {
618     unsigned attributes = 0;
619     JSCell* cell = 0;
620     size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell);
621     if (offset == WTF::notFound)
622         return false;
623     descriptor.setDescriptor(getDirectOffset(offset), attributes);
624     return true;
625 }
626
627 bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
628 {
629     JSObject* object = this;
630     while (true) {
631         if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
632             return true;
633         JSValue prototype = object->prototype();
634         if (!prototype.isObject())
635             return false;
636         object = asObject(prototype);
637     }
638 }
639
640 static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
641 {
642     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
643         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
644             GetterSetter* accessor = GetterSetter::create(exec);
645             if (oldDescriptor.getter()) {
646                 attributes |= Getter;
647                 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
648             }
649             if (oldDescriptor.setter()) {
650                 attributes |= Setter;
651                 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
652             }
653             target->putWithAttributes(exec, propertyName, accessor, attributes);
654             return true;
655         }
656         JSValue newValue = jsUndefined();
657         if (descriptor.value())
658             newValue = descriptor.value();
659         else if (oldDescriptor.value())
660             newValue = oldDescriptor.value();
661         target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter));
662         return true;
663     }
664     attributes &= ~ReadOnly;
665     if (descriptor.getter() && descriptor.getter().isObject())
666         target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
667     if (exec->hadException())
668         return false;
669     if (descriptor.setter() && descriptor.setter().isObject())
670         target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
671     return !exec->hadException();
672 }
673
674 bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
675 {
676     // If we have a new property we can just put it on normally
677     PropertyDescriptor current;
678     if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
679         // unless extensions are prevented!
680         if (!isExtensible()) {
681             if (throwException)
682                 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
683             return false;
684         }
685         PropertyDescriptor oldDescriptor;
686         oldDescriptor.setValue(jsUndefined());
687         return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
688     }
689
690     if (descriptor.isEmpty())
691         return true;
692
693     if (current.equalTo(exec, descriptor))
694         return true;
695
696     // Filter out invalid changes
697     if (!current.configurable()) {
698         if (descriptor.configurable()) {
699             if (throwException)
700                 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
701             return false;
702         }
703         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
704             if (throwException)
705                 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
706             return false;
707         }
708     }
709
710     // A generic descriptor is simply changing the attributes of an existing property
711     if (descriptor.isGenericDescriptor()) {
712         if (!current.attributesEqual(descriptor)) {
713             deleteProperty(exec, propertyName);
714             putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
715         }
716         return true;
717     }
718
719     // Changing between a normal property or an accessor property
720     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
721         if (!current.configurable()) {
722             if (throwException)
723                 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
724             return false;
725         }
726         deleteProperty(exec, propertyName);
727         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
728     }
729
730     // Changing the value and attributes of an existing property
731     if (descriptor.isDataDescriptor()) {
732         if (!current.configurable()) {
733             if (!current.writable() && descriptor.writable()) {
734                 if (throwException)
735                     throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
736                 return false;
737             }
738             if (!current.writable()) {
739                 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
740                     if (throwException)
741                         throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
742                     return false;
743                 }
744             }
745         } else if (current.attributesEqual(descriptor)) {
746             if (!descriptor.value())
747                 return true;
748             PutPropertySlot slot;
749             put(exec, propertyName, descriptor.value(), slot);
750             if (exec->hadException())
751                 return false;
752             return true;
753         }
754         deleteProperty(exec, propertyName);
755         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
756     }
757
758     // Changing the accessor functions of an existing accessor property
759     ASSERT(descriptor.isAccessorDescriptor());
760     if (!current.configurable()) {
761         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
762             if (throwException)
763                 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
764             return false;
765         }
766         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
767             if (throwException)
768                 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
769             return false;
770         }
771     }
772     JSValue accessor = getDirect(exec->globalData(), propertyName);
773     if (!accessor)
774         return false;
775     GetterSetter* getterSetter = asGetterSetter(accessor);
776     if (current.attributesEqual(descriptor)) {
777         if (descriptor.setter())
778             getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
779         if (descriptor.getter())
780             getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
781         return true;
782     }
783     deleteProperty(exec, propertyName);
784     unsigned attrs = current.attributesWithOverride(descriptor);
785     if (descriptor.setter())
786         attrs |= Setter;
787     if (descriptor.getter())
788         attrs |= Getter;
789     putDirect(exec->globalData(), propertyName, getterSetter, attrs);
790     return true;
791 }
792
793 JSObject* throwTypeError(ExecState* exec, const UString& message)
794 {
795     return throwError(exec, createTypeError(exec, message));
796 }
797
798 } // namespace JSC