initial import
[vuplus_webkit] / 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     if (propertyName == exec->propertyNames().underscoreProto) {
326         // Defining a getter for __proto__ is silently ignored.
327         return;
328     }
329
330     JSValue object = getDirect(exec->globalData(), propertyName);
331     if (object && object.isGetterSetter()) {
332         ASSERT(m_structure->hasGetterSetterProperties());
333         asGetterSetter(object)->setGetter(exec->globalData(), getterFunction);
334         return;
335     }
336
337     JSGlobalData& globalData = exec->globalData();
338     PutPropertySlot slot;
339     GetterSetter* getterSetter = GetterSetter::create(exec);
340     putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot);
341
342     // putDirect will change our Structure if we add a new property. For
343     // getters and setters, though, we also need to change our Structure
344     // if we override an existing non-getter or non-setter.
345     if (slot.type() != PutPropertySlot::NewProperty) {
346         if (!m_structure->isDictionary())
347             setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get()));
348     }
349
350     m_structure->setHasGetterSetterProperties(true);
351     getterSetter->setGetter(globalData, getterFunction);
352 }
353
354 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
355 {
356     if (propertyName == exec->propertyNames().underscoreProto) {
357         // Defining a setter for __proto__ is silently ignored.
358         return;
359     }
360
361     JSValue object = getDirect(exec->globalData(), propertyName);
362     if (object && object.isGetterSetter()) {
363         ASSERT(m_structure->hasGetterSetterProperties());
364         asGetterSetter(object)->setSetter(exec->globalData(), setterFunction);
365         return;
366     }
367
368     PutPropertySlot slot;
369     GetterSetter* getterSetter = GetterSetter::create(exec);
370     putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
371
372     // putDirect will change our Structure if we add a new property. For
373     // getters and setters, though, we also need to change our Structure
374     // if we override an existing non-getter or non-setter.
375     if (slot.type() != PutPropertySlot::NewProperty) {
376         if (!m_structure->isDictionary())
377             setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get()));
378     }
379
380     m_structure->setHasGetterSetterProperties(true);
381     getterSetter->setSetter(exec->globalData(), setterFunction);
382 }
383
384 JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName)
385 {
386     JSObject* object = this;
387     while (true) {
388         if (JSValue value = object->getDirect(exec->globalData(), propertyName)) {
389             if (!value.isGetterSetter())
390                 return jsUndefined();
391             JSObject* functionObject = asGetterSetter(value)->getter();
392             if (!functionObject)
393                 return jsUndefined();
394             return functionObject;
395         }
396
397         if (!object->prototype() || !object->prototype().isObject())
398             return jsUndefined();
399         object = asObject(object->prototype());
400     }
401 }
402
403 JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName)
404 {
405     JSObject* object = this;
406     while (true) {
407         if (JSValue value = object->getDirect(exec->globalData(), propertyName)) {
408             if (!value.isGetterSetter())
409                 return jsUndefined();
410             JSObject* functionObject = asGetterSetter(value)->setter();
411             if (!functionObject)
412                 return jsUndefined();
413             return functionObject;
414         }
415
416         if (!object->prototype() || !object->prototype().isObject())
417             return jsUndefined();
418         object = asObject(object->prototype());
419     }
420 }
421
422 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
423 {
424     if (!value.isObject())
425         return false;
426
427     if (!proto.isObject()) {
428         throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property."));
429         return false;
430     }
431
432     JSObject* object = asObject(value);
433     while ((object = object->prototype().getObject())) {
434         if (proto == object)
435             return true;
436     }
437     return false;
438 }
439
440 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
441 {
442     PropertyDescriptor descriptor;
443     if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
444         return false;
445     return descriptor.enumerable();
446 }
447
448 bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const
449 {
450     unsigned attributes;
451     if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound)
452         return true;
453
454     // This could be a function within the static table? - should probably
455     // also look in the hash?  This currently should not be a problem, since
456     // we've currently always call 'get' first, which should have populated
457     // the normal storage.
458     return false;
459 }
460
461 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
462 {
463     getOwnPropertyNames(exec, propertyNames, mode);
464
465     if (prototype().isNull())
466         return;
467
468     JSObject* prototype = asObject(this->prototype());
469     while(1) {
470         if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
471             prototype->getPropertyNames(exec, propertyNames, mode);
472             break;
473         }
474         prototype->getOwnPropertyNames(exec, propertyNames, mode);
475         JSValue nextProto = prototype->prototype();
476         if (nextProto.isNull())
477             break;
478         prototype = asObject(nextProto);
479     }
480 }
481
482 void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
483 {
484     m_structure->getPropertyNames(exec->globalData(), propertyNames, mode);
485     getClassPropertyNames(exec, classInfo(), propertyNames, mode);
486 }
487
488 bool JSObject::toBoolean(ExecState*) const
489 {
490     return true;
491 }
492
493 double JSObject::toNumber(ExecState* exec) const
494 {
495     JSValue primitive = toPrimitive(exec, PreferNumber);
496     if (exec->hadException()) // should be picked up soon in Nodes.cpp
497         return 0.0;
498     return primitive.toNumber(exec);
499 }
500
501 UString JSObject::toString(ExecState* exec) const
502 {
503     JSValue primitive = toPrimitive(exec, PreferString);
504     if (exec->hadException())
505         return "";
506     return primitive.toString(exec);
507 }
508
509 JSObject* JSObject::toObject(ExecState*, JSGlobalObject*) const
510 {
511     return const_cast<JSObject*>(this);
512 }
513
514 JSObject* JSObject::toThisObject(ExecState*) const
515 {
516     return const_cast<JSObject*>(this);
517 }
518
519 JSValue JSObject::toStrictThisObject(ExecState*) const
520 {
521     return const_cast<JSObject*>(this);
522 }
523
524 JSObject* JSObject::unwrappedObject()
525 {
526     return this;
527 }
528
529 void JSObject::seal(JSGlobalData& globalData)
530 {
531     if (isSealed(globalData))
532         return;
533     preventExtensions(globalData);
534     setStructure(globalData, Structure::sealTransition(globalData, m_structure.get()));
535 }
536
537 void JSObject::freeze(JSGlobalData& globalData)
538 {
539     if (isFrozen(globalData))
540         return;
541     preventExtensions(globalData);
542     setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get()));
543 }
544
545 void JSObject::preventExtensions(JSGlobalData& globalData)
546 {
547     if (isExtensible())
548         setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get()));
549 }
550
551 void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName)
552 {
553     if (m_structure->get(globalData, propertyName) == WTF::notFound)
554         return;
555
556     size_t offset;
557     if (m_structure->isUncacheableDictionary()) {
558         offset = m_structure->removePropertyWithoutTransition(globalData, propertyName);
559         if (offset != WTF::notFound)
560             putUndefinedAtDirectOffset(offset);
561         return;
562     }
563
564     setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset));
565     if (offset != WTF::notFound)
566         putUndefinedAtDirectOffset(offset);
567 }
568
569 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
570 {
571     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
572 }
573
574 void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr)
575 {
576     putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
577 }
578
579 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
580 {
581     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
582 }
583
584 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr)
585 {
586     putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr);
587 }
588
589 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
590 {
591     if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
592         if (!structure()->isDictionary())
593             slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
594         else
595             slot.setGetterSlot(getterFunction);
596     } else
597         slot.setUndefined();
598 }
599
600 Structure* JSObject::createInheritorID(JSGlobalData& globalData)
601 {
602     m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, m_structure->globalObject(), this));
603     ASSERT(m_inheritorID->isEmpty());
604     return m_inheritorID.get();
605 }
606
607 void JSObject::allocatePropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
608 {
609     ASSERT(newSize > oldSize);
610
611     // It's important that this function not rely on m_structure, since
612     // we might be in the middle of a transition.
613     PropertyStorage newPropertyStorage = 0;
614     if (globalData.heap.inPropertyStorageNursery(m_propertyStorage.get())) {
615         newPropertyStorage = static_cast<PropertyStorage>(globalData.heap.allocatePropertyStorage(newSize * sizeof(WriteBarrierBase<Unknown>)));
616         if (!newPropertyStorage || !globalData.heap.inPropertyStorageNursery(m_propertyStorage.get())) {
617             // If allocation failed because it's too big, or it triggered a GC
618             // that promoted us to old space, we need to allocate our property
619             // storage in old space.
620             newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
621         }
622     } else
623         newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
624
625     PropertyStorage oldPropertyStorage = m_propertyStorage.get();
626     ASSERT(newPropertyStorage);
627
628     for (unsigned i = 0; i < oldSize; ++i)
629        newPropertyStorage[i] = oldPropertyStorage[i];
630
631     if (!isUsingInlineStorage() && !globalData.heap.inPropertyStorageNursery(m_propertyStorage.get()))
632         delete [] oldPropertyStorage;
633
634     m_propertyStorage.set(globalData, this, newPropertyStorage);
635 }
636
637 bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
638 {
639     unsigned attributes = 0;
640     JSCell* cell = 0;
641     size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell);
642     if (offset == WTF::notFound)
643         return false;
644     descriptor.setDescriptor(getDirectOffset(offset), attributes);
645     return true;
646 }
647
648 bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
649 {
650     JSObject* object = this;
651     while (true) {
652         if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
653             return true;
654         JSValue prototype = object->prototype();
655         if (!prototype.isObject())
656             return false;
657         object = asObject(prototype);
658     }
659 }
660
661 static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
662 {
663     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
664         if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
665             GetterSetter* accessor = GetterSetter::create(exec);
666             if (oldDescriptor.getter()) {
667                 attributes |= Getter;
668                 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
669             }
670             if (oldDescriptor.setter()) {
671                 attributes |= Setter;
672                 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
673             }
674             target->putWithAttributes(exec, propertyName, accessor, attributes);
675             return true;
676         }
677         JSValue newValue = jsUndefined();
678         if (descriptor.value())
679             newValue = descriptor.value();
680         else if (oldDescriptor.value())
681             newValue = oldDescriptor.value();
682         target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter));
683         return true;
684     }
685     attributes &= ~ReadOnly;
686     if (descriptor.getter() && descriptor.getter().isObject())
687         target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
688     if (exec->hadException())
689         return false;
690     if (descriptor.setter() && descriptor.setter().isObject())
691         target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
692     return !exec->hadException();
693 }
694
695 bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
696 {
697     // If we have a new property we can just put it on normally
698     PropertyDescriptor current;
699     if (!getOwnPropertyDescriptor(exec, propertyName, current)) {
700         // unless extensions are prevented!
701         if (!isExtensible()) {
702             if (throwException)
703                 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
704             return false;
705         }
706         PropertyDescriptor oldDescriptor;
707         oldDescriptor.setValue(jsUndefined());
708         return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
709     }
710
711     if (descriptor.isEmpty())
712         return true;
713
714     if (current.equalTo(exec, descriptor))
715         return true;
716
717     // Filter out invalid changes
718     if (!current.configurable()) {
719         if (descriptor.configurable()) {
720             if (throwException)
721                 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
722             return false;
723         }
724         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
725             if (throwException)
726                 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
727             return false;
728         }
729     }
730
731     // A generic descriptor is simply changing the attributes of an existing property
732     if (descriptor.isGenericDescriptor()) {
733         if (!current.attributesEqual(descriptor)) {
734             deleteProperty(exec, propertyName);
735             putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
736         }
737         return true;
738     }
739
740     // Changing between a normal property or an accessor property
741     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
742         if (!current.configurable()) {
743             if (throwException)
744                 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
745             return false;
746         }
747         deleteProperty(exec, propertyName);
748         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
749     }
750
751     // Changing the value and attributes of an existing property
752     if (descriptor.isDataDescriptor()) {
753         if (!current.configurable()) {
754             if (!current.writable() && descriptor.writable()) {
755                 if (throwException)
756                     throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
757                 return false;
758             }
759             if (!current.writable()) {
760                 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
761                     if (throwException)
762                         throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
763                     return false;
764                 }
765             }
766         } else if (current.attributesEqual(descriptor)) {
767             if (!descriptor.value())
768                 return true;
769             PutPropertySlot slot;
770             put(exec, propertyName, descriptor.value(), slot);
771             if (exec->hadException())
772                 return false;
773             return true;
774         }
775         deleteProperty(exec, propertyName);
776         return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
777     }
778
779     // Changing the accessor functions of an existing accessor property
780     ASSERT(descriptor.isAccessorDescriptor());
781     if (!current.configurable()) {
782         if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
783             if (throwException)
784                 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
785             return false;
786         }
787         if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
788             if (throwException)
789                 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
790             return false;
791         }
792     }
793     JSValue accessor = getDirect(exec->globalData(), propertyName);
794     if (!accessor)
795         return false;
796     GetterSetter* getterSetter = asGetterSetter(accessor);
797     if (current.attributesEqual(descriptor)) {
798         if (descriptor.setter())
799             getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
800         if (descriptor.getter())
801             getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
802         return true;
803     }
804     deleteProperty(exec, propertyName);
805     unsigned attrs = current.attributesWithOverride(descriptor);
806     if (descriptor.setter())
807         attrs |= Setter;
808     if (descriptor.getter())
809         attrs |= Getter;
810     putDirect(exec->globalData(), propertyName, getterSetter, attrs);
811     return true;
812 }
813
814 JSObject* throwTypeError(ExecState* exec, const UString& message)
815 {
816     return throwError(exec, createTypeError(exec, message));
817 }
818
819 } // namespace JSC