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