]>
Commit | Line | Data |
---|---|---|
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 |
44 | namespace JSC { |
45 | ||
46 | ASSERT_CLASS_FITS_IN_CELL(JSObject); | |
14957cd0 A |
47 | ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); |
48 | ASSERT_CLASS_FITS_IN_CELL(JSFinalObject); | |
49 | ||
6fe7ccc8 A |
50 | ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject); |
51 | ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFinalObject); | |
52 | ||
14957cd0 A |
53 | const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; |
54 | ||
6fe7ccc8 A |
55 | const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) }; |
56 | ||
57 | const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) }; | |
9dae56ea | 58 | |
f9bf01c6 A |
59 | static 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 | 78 | void 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 | 109 | UString 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 | 116 | bool 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 | 123 | void 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 | 188 | void 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 | 195 | void 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 | 202 | bool 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 | 218 | bool 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 | 224 | void 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 | ||
243 | bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const | |
244 | { | |
245 | PropertySlot slot; | |
246 | return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); | |
247 | } | |
248 | ||
249 | bool 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 | 256 | bool 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 | ||
281 | bool 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 | 287 | bool 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 | 293 | static 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 | 315 | bool 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 | 323 | JSValue 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 | ||
347 | const 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 | 358 | bool 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 | ||
376 | bool 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 | 384 | bool 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 | 397 | void 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 | 418 | void 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 | ||
425 | bool JSObject::toBoolean(ExecState*) const | |
426 | { | |
427 | return true; | |
428 | } | |
429 | ||
430 | double 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 | 438 | JSString* 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 | 446 | JSObject* JSObject::toThisObject(JSCell* cell, ExecState*) |
14957cd0 | 447 | { |
6fe7ccc8 | 448 | return jsCast<JSObject*>(cell); |
14957cd0 A |
449 | } |
450 | ||
9dae56ea A |
451 | JSObject* JSObject::unwrappedObject() |
452 | { | |
6fe7ccc8 A |
453 | if (isGlobalThis()) |
454 | return jsCast<JSGlobalThis*>(this)->unwrappedObject(); | |
9dae56ea A |
455 | return this; |
456 | } | |
457 | ||
14957cd0 A |
458 | void 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 | ||
466 | void 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 | ||
474 | void 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. | |
484 | void 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 | ||
513 | void 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 |
531 | NEVER_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 | 542 | Structure* 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 | 555 | PropertyStorage 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 | 586 | bool 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 | ||
597 | bool 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 | 610 | static 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 |
648 | class DefineOwnPropertyScope { |
649 | public: | |
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 | ||
661 | private: | |
662 | JSGlobalData& m_globalData; | |
663 | }; | |
664 | ||
665 | bool 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 |
779 | JSObject* throwTypeError(ExecState* exec, const UString& message) |
780 | { | |
781 | return throwError(exec, createTypeError(exec, message)); | |
782 | } | |
783 | ||
9dae56ea | 784 | } // namespace JSC |