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