]>
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 | ||
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 |
42 | namespace JSC { |
43 | ||
44 | ASSERT_CLASS_FITS_IN_CELL(JSObject); | |
14957cd0 A |
45 | ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); |
46 | ASSERT_CLASS_FITS_IN_CELL(JSFinalObject); | |
47 | ||
48 | const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; | |
49 | ||
50 | const ClassInfo JSObject::s_info = { "Object", 0, 0, 0 }; | |
9dae56ea | 51 | |
f9bf01c6 A |
52 | static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode) |
53 | { | |
54 | // Add properties from the static hashtables of properties | |
55 | for (; classInfo; classInfo = classInfo->parentClass) { | |
56 | const HashTable* table = classInfo->propHashTable(exec); | |
57 | if (!table) | |
58 | continue; | |
59 | table->initializeIfNeeded(exec); | |
60 | ASSERT(table->table); | |
61 | ||
62 | int hashSizeMask = table->compactSize - 1; | |
63 | const HashEntry* entry = table->table; | |
64 | for (int i = 0; i <= hashSizeMask; ++i, ++entry) { | |
65 | if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties))) | |
66 | propertyNames.add(entry->key()); | |
67 | } | |
68 | } | |
69 | } | |
9dae56ea | 70 | |
14957cd0 | 71 | void 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 | ||
86 | UString JSObject::className() const | |
87 | { | |
88 | const ClassInfo* info = classInfo(); | |
14957cd0 A |
89 | ASSERT(info); |
90 | return info->className; | |
9dae56ea A |
91 | } |
92 | ||
93 | bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) | |
94 | { | |
95 | return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); | |
96 | } | |
97 | ||
98 | static void throwSetterError(ExecState* exec) | |
99 | { | |
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 | 104 | void 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 | 169 | void 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 |
175 | void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) |
176 | { | |
177 | putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot); | |
178 | } | |
179 | ||
180 | void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) | |
181 | { | |
182 | putDirectInternal(*globalData, propertyName, value, attributes); | |
183 | } | |
184 | ||
185 | void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes) | |
186 | { | |
187 | putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes); | |
188 | } | |
189 | ||
ba379fdc | 190 | void 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 |
195 | void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) |
196 | { | |
197 | putDirectInternal(exec->globalData(), propertyName, value, attributes); | |
198 | } | |
199 | ||
200 | void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes) | |
9dae56ea A |
201 | { |
202 | putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes); | |
203 | } | |
204 | ||
205 | bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const | |
206 | { | |
207 | PropertySlot slot; | |
208 | return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); | |
209 | } | |
210 | ||
211 | bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const | |
212 | { | |
213 | PropertySlot slot; | |
214 | return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot); | |
215 | } | |
216 | ||
217 | // ECMA 8.6.2.5 | |
218 | bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName) | |
219 | { | |
220 | unsigned attributes; | |
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 | ||
238 | bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const | |
239 | { | |
240 | PropertySlot slot; | |
241 | return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot); | |
242 | } | |
243 | ||
244 | bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName) | |
245 | { | |
246 | return deleteProperty(exec, Identifier::from(exec, propertyName)); | |
247 | } | |
248 | ||
ba379fdc | 249 | static 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 | 271 | bool 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 | 279 | JSValue 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 | ||
303 | const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const | |
304 | { | |
305 | for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { | |
306 | if (const HashTable* propHashTable = info->propHashTable(exec)) { | |
307 | if (const HashEntry* entry = propHashTable->entry(exec, propertyName)) | |
308 | return entry; | |
309 | } | |
310 | } | |
311 | return 0; | |
312 | } | |
313 | ||
f9bf01c6 | 314 | void 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 | 340 | void 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 | 365 | JSValue 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 | 384 | JSValue 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 | 403 | bool 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 | ||
421 | bool 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 | 429 | bool 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 | 442 | void 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 | ||
463 | void 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 | ||
469 | bool JSObject::toBoolean(ExecState*) const | |
470 | { | |
471 | return true; | |
472 | } | |
473 | ||
474 | double 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 | ||
482 | UString 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 | 490 | JSObject* JSObject::toObject(ExecState*, JSGlobalObject*) const |
9dae56ea A |
491 | { |
492 | return const_cast<JSObject*>(this); | |
493 | } | |
494 | ||
495 | JSObject* JSObject::toThisObject(ExecState*) const | |
496 | { | |
497 | return const_cast<JSObject*>(this); | |
498 | } | |
499 | ||
14957cd0 A |
500 | JSValue JSObject::toStrictThisObject(ExecState*) const |
501 | { | |
502 | return const_cast<JSObject*>(this); | |
503 | } | |
504 | ||
9dae56ea A |
505 | JSObject* JSObject::unwrappedObject() |
506 | { | |
507 | return this; | |
508 | } | |
509 | ||
14957cd0 A |
510 | void 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 | ||
518 | void 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 | ||
526 | void JSObject::preventExtensions(JSGlobalData& globalData) | |
527 | { | |
528 | if (isExtensible()) | |
529 | setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get())); | |
530 | } | |
531 | ||
532 | void 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 | ||
550 | void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) | |
551 | { | |
14957cd0 A |
552 | putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); |
553 | } | |
554 | ||
555 | void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr) | |
556 | { | |
557 | putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); | |
9dae56ea A |
558 | } |
559 | ||
560 | void 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 | 565 | void 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 | ||
570 | NEVER_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 | 581 | Structure* 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 | ||
588 | void 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 | 608 | bool 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 | ||
619 | bool 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 | 632 | static 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 | ||
666 | bool 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 |
785 | JSObject* throwTypeError(ExecState* exec, const UString& message) |
786 | { | |
787 | return throwError(exec, createTypeError(exec, message)); | |
788 | } | |
789 | ||
9dae56ea | 790 | } // namespace JSC |