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