source: webkit/trunk/JavaScriptCore/runtime/JSObject.cpp@ 53170

Last change on this file since 53170 was 53170, checked in by [email protected], 15 years ago

2010-01-12 Kent Hansen <[email protected]>

Reviewed by Geoffrey Garen.

[ES5] Implement Object.getOwnPropertyNames
https://bugs.webkit.org/show_bug.cgi?id=32242

Add an extra argument to getPropertyNames() and getOwnPropertyNames()
(and all reimplementations thereof) that indicates whether non-enumerable
properties should be added.

  • API/JSCallbackObject.h:
  • API/JSCallbackObjectFunctions.h: (JSC::::getOwnPropertyNames):
  • JavaScriptCore.exp:
  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
  • debugger/DebuggerActivation.cpp: (JSC::DebuggerActivation::getOwnPropertyNames):
  • debugger/DebuggerActivation.h:
  • runtime/Arguments.cpp: (JSC::Arguments::getOwnPropertyNames):
  • runtime/Arguments.h:
  • runtime/CommonIdentifiers.h:
  • runtime/JSArray.cpp: (JSC::JSArray::getOwnPropertyNames):
  • runtime/JSArray.h:
  • runtime/JSByteArray.cpp: (JSC::JSByteArray::getOwnPropertyNames):
  • runtime/JSByteArray.h:
  • runtime/JSFunction.cpp: (JSC::JSFunction::getOwnPropertyNames):
  • runtime/JSFunction.h:
  • runtime/JSNotAnObject.cpp: (JSC::JSNotAnObject::getOwnPropertyNames):
  • runtime/JSNotAnObject.h:
  • runtime/JSObject.cpp: (JSC::getClassPropertyNames): (JSC::JSObject::getPropertyNames): (JSC::JSObject::getOwnPropertyNames):
  • runtime/JSObject.h:
  • runtime/JSVariableObject.cpp: (JSC::JSVariableObject::getOwnPropertyNames):
  • runtime/JSVariableObject.h:
  • runtime/ObjectConstructor.cpp: (JSC::ObjectConstructor::ObjectConstructor): (JSC::objectConstructorGetOwnPropertyNames):
  • runtime/RegExpMatchesArray.h: (JSC::RegExpMatchesArray::getOwnPropertyNames):
  • runtime/StringObject.cpp: (JSC::StringObject::getOwnPropertyNames):
  • runtime/StringObject.h:
  • runtime/Structure.cpp: Rename getEnumerablePropertyNames() to getPropertyNames(), which takes an extra argument. (JSC::Structure::getPropertyNames):
  • runtime/Structure.h: (JSC::):

2010-01-12 Kent Hansen <[email protected]>

Reviewed by Geoffrey Garen.

[ES5] Implement Object.getOwnPropertyNames
https://bugs.webkit.org/show_bug.cgi?id=32242

Add new argument to the reimplementation of getOwnPropertyNames().

  • UserObjectImp.cpp: (UserObjectImp::getOwnPropertyNames):
  • UserObjectImp.h:

2010-01-12 Kent Hansen <[email protected]>

Reviewed by Geoffrey Garen.

[ES5] Implement Object.getOwnPropertyNames
https://bugs.webkit.org/show_bug.cgi?id=32242

Add tests for Object.getOwnPropertyNames(o), both standard usage and cross origin.

  • fast/js/Object-getOwnPropertyNames-expected.txt: Added.
  • fast/js/Object-getOwnPropertyNames.html: Added.
  • fast/js/script-tests/Object-getOwnPropertyNames.js: Added.
  • http/tests/security/cross-frame-access-enumeration-expected.txt:
  • http/tests/security/cross-frame-access-enumeration.html:

2010-01-12 Kent Hansen <[email protected]>

Reviewed by Geoffrey Garen.

[ES5] Implement Object.getOwnPropertyNames
https://bugs.webkit.org/show_bug.cgi?id=32242

Add new argument to reimplementations of getPropertyNames()
and getOwnPropertyNames(), and update the JS bindings generator.

Test: fast/js/Object-getOwnPropertyNames.html

  • bindings/js/JSDOMWindowCustom.cpp: (WebCore::JSDOMWindow::getPropertyNames): (WebCore::JSDOMWindow::getOwnPropertyNames):
  • bindings/js/JSDOMWindowShell.cpp: (WebCore::JSDOMWindowShell::getPropertyNames): (WebCore::JSDOMWindowShell::getOwnPropertyNames):
  • bindings/js/JSDOMWindowShell.h:
  • bindings/js/JSHistoryCustom.cpp: (WebCore::JSHistory::getOwnPropertyNames):
  • bindings/js/JSLocationCustom.cpp: (WebCore::JSLocation::getOwnPropertyNames):
  • bindings/js/JSQuarantinedObjectWrapper.cpp: (WebCore::JSQuarantinedObjectWrapper::getPropertyNames): (WebCore::JSQuarantinedObjectWrapper::getOwnPropertyNames):
  • bindings/js/JSQuarantinedObjectWrapper.h:
  • bindings/js/JSStorageCustom.cpp: (WebCore::JSStorage::getOwnPropertyNames):
  • bindings/scripts/CodeGeneratorJS.pm:
  • bridge/runtime_array.cpp: (JSC::RuntimeArray::getOwnPropertyNames):
  • bridge/runtime_array.h:
  • bridge/runtime_object.cpp: (JSC::RuntimeObjectImp::getPropertyNames): (JSC::RuntimeObjectImp::getOwnPropertyNames):
  • bridge/runtime_object.h:
  • Property svn:eol-style set to native
File size: 24.0 KB
Line 
1/*
2 * Copyright (C) 1999-2001 Harri Porten ([email protected])
3 * Copyright (C) 2001 Peter Kelly ([email protected])
4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel ([email protected])
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 *
22 */
23
24#include "config.h"
25#include "JSObject.h"
26
27#include "DatePrototype.h"
28#include "ErrorConstructor.h"
29#include "GetterSetter.h"
30#include "JSGlobalObject.h"
31#include "NativeErrorConstructor.h"
32#include "ObjectPrototype.h"
33#include "PropertyDescriptor.h"
34#include "PropertyNameArray.h"
35#include "Lookup.h"
36#include "Nodes.h"
37#include "Operations.h"
38#include <math.h>
39#include <wtf/Assertions.h>
40
41namespace JSC {
42
43ASSERT_CLASS_FITS_IN_CELL(JSObject);
44
45static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
46{
47 // Add properties from the static hashtables of properties
48 for (; classInfo; classInfo = classInfo->parentClass) {
49 const HashTable* table = classInfo->propHashTable(exec);
50 if (!table)
51 continue;
52 table->initializeIfNeeded(exec);
53 ASSERT(table->table);
54
55 int hashSizeMask = table->compactSize - 1;
56 const HashEntry* entry = table->table;
57 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
58 if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
59 propertyNames.add(entry->key());
60 }
61 }
62}
63
64void JSObject::markChildren(MarkStack& markStack)
65{
66#ifndef NDEBUG
67 bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
68 markStack.m_isCheckingForDefaultMarkViolation = false;
69#endif
70
71 markChildrenDirect(markStack);
72
73#ifndef NDEBUG
74 markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
75#endif
76}
77
78UString JSObject::className() const
79{
80 const ClassInfo* info = classInfo();
81 if (info)
82 return info->className;
83 return "Object";
84}
85
86bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
87{
88 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
89}
90
91static void throwSetterError(ExecState* exec)
92{
93 throwError(exec, TypeError, "setting a property that has only a getter");
94}
95
96// ECMA 8.6.2.2
97void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
98{
99 ASSERT(value);
100 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
101
102 if (propertyName == exec->propertyNames().underscoreProto) {
103 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
104 if (!value.isObject() && !value.isNull())
105 return;
106
107 JSValue nextPrototypeValue = value;
108 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
109 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
110 if (nextPrototype == this) {
111 throwError(exec, GeneralError, "cyclic __proto__ value");
112 return;
113 }
114 nextPrototypeValue = nextPrototype->prototype();
115 }
116
117 setPrototype(value);
118 return;
119 }
120
121 // Check if there are any setters or getters in the prototype chain
122 JSValue prototype;
123 for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
124 prototype = obj->prototype();
125 if (prototype.isNull()) {
126 putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
127 return;
128 }
129 }
130
131 unsigned attributes;
132 JSCell* specificValue;
133 if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
134 return;
135
136 for (JSObject* obj = this; ; obj = asObject(prototype)) {
137 if (JSValue gs = obj->getDirect(propertyName)) {
138 if (gs.isGetterSetter()) {
139 JSObject* setterFunc = asGetterSetter(gs)->setter();
140 if (!setterFunc) {
141 throwSetterError(exec);
142 return;
143 }
144
145 CallData callData;
146 CallType callType = setterFunc->getCallData(callData);
147 MarkedArgumentBuffer args;
148 args.append(value);
149 call(exec, setterFunc, callType, callData, this, args);
150 return;
151 }
152
153 // If there's an existing property on the object or one of its
154 // prototypes it should be replaced, so break here.
155 break;
156 }
157
158 prototype = obj->prototype();
159 if (prototype.isNull())
160 break;
161 }
162
163 putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
164 return;
165}
166
167void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
168{
169 PutPropertySlot slot;
170 put(exec, Identifier::from(exec, propertyName), value, slot);
171}
172
173void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
174{
175 putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
176}
177
178void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
179{
180 putDirectInternal(exec->globalData(), propertyName, value, attributes);
181}
182
183void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
184{
185 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
186}
187
188bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
189{
190 PropertySlot slot;
191 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
192}
193
194bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
195{
196 PropertySlot slot;
197 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
198}
199
200// ECMA 8.6.2.5
201bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
202{
203 unsigned attributes;
204 JSCell* specificValue;
205 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
206 if ((attributes & DontDelete))
207 return false;
208 removeDirect(propertyName);
209 return true;
210 }
211
212 // Look in the static hashtable of properties
213 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
214 if (entry && entry->attributes() & DontDelete)
215 return false; // this builtin property can't be deleted
216
217 // FIXME: Should the code here actually do some deletion?
218 return true;
219}
220
221bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
222{
223 PropertySlot slot;
224 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
225}
226
227bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
228{
229 return deleteProperty(exec, Identifier::from(exec, propertyName));
230}
231
232static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
233{
234 JSValue function = object->get(exec, propertyName);
235 CallData callData;
236 CallType callType = function.getCallData(callData);
237 if (callType == CallTypeNone)
238 return exec->exception();
239
240 // Prevent "toString" and "valueOf" from observing execution if an exception
241 // is pending.
242 if (exec->hadException())
243 return exec->exception();
244
245 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
246 ASSERT(!result.isGetterSetter());
247 if (exec->hadException())
248 return exec->exception();
249 if (result.isObject())
250 return JSValue();
251 return result;
252}
253
254bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
255{
256 result = defaultValue(exec, PreferNumber);
257 number = result.toNumber(exec);
258 return !result.isString();
259}
260
261// ECMA 8.6.2.6
262JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
263{
264 // Must call toString first for Date objects.
265 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
266 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
267 if (value)
268 return value;
269 value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
270 if (value)
271 return value;
272 } else {
273 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
274 if (value)
275 return value;
276 value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
277 if (value)
278 return value;
279 }
280
281 ASSERT(!exec->hadException());
282
283 return throwError(exec, TypeError, "No default value");
284}
285
286const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
287{
288 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
289 if (const HashTable* propHashTable = info->propHashTable(exec)) {
290 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
291 return entry;
292 }
293 }
294 return 0;
295}
296
297void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
298{
299 JSValue object = getDirect(propertyName);
300 if (object && object.isGetterSetter()) {
301 ASSERT(m_structure->hasGetterSetterProperties());
302 asGetterSetter(object)->setGetter(getterFunction);
303 return;
304 }
305
306 PutPropertySlot slot;
307 GetterSetter* getterSetter = new (exec) GetterSetter(exec);
308 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
309
310 // putDirect will change our Structure if we add a new property. For
311 // getters and setters, though, we also need to change our Structure
312 // if we override an existing non-getter or non-setter.
313 if (slot.type() != PutPropertySlot::NewProperty) {
314 if (!m_structure->isDictionary()) {
315 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
316 setStructure(structure.release());
317 }
318 }
319
320 m_structure->setHasGetterSetterProperties(true);
321 getterSetter->setGetter(getterFunction);
322}
323
324void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
325{
326 JSValue object = getDirect(propertyName);
327 if (object && object.isGetterSetter()) {
328 ASSERT(m_structure->hasGetterSetterProperties());
329 asGetterSetter(object)->setSetter(setterFunction);
330 return;
331 }
332
333 PutPropertySlot slot;
334 GetterSetter* getterSetter = new (exec) GetterSetter(exec);
335 putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
336
337 // putDirect will change our Structure if we add a new property. For
338 // getters and setters, though, we also need to change our Structure
339 // if we override an existing non-getter or non-setter.
340 if (slot.type() != PutPropertySlot::NewProperty) {
341 if (!m_structure->isDictionary()) {
342 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
343 setStructure(structure.release());
344 }
345 }
346
347 m_structure->setHasGetterSetterProperties(true);
348 getterSetter->setSetter(setterFunction);
349}
350
351JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
352{
353 JSObject* object = this;
354 while (true) {
355 if (JSValue value = object->getDirect(propertyName)) {
356 if (!value.isGetterSetter())
357 return jsUndefined();
358 JSObject* functionObject = asGetterSetter(value)->getter();
359 if (!functionObject)
360 return jsUndefined();
361 return functionObject;
362 }
363
364 if (!object->prototype() || !object->prototype().isObject())
365 return jsUndefined();
366 object = asObject(object->prototype());
367 }
368}
369
370JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
371{
372 JSObject* object = this;
373 while (true) {
374 if (JSValue value = object->getDirect(propertyName)) {
375 if (!value.isGetterSetter())
376 return jsUndefined();
377 JSObject* functionObject = asGetterSetter(value)->setter();
378 if (!functionObject)
379 return jsUndefined();
380 return functionObject;
381 }
382
383 if (!object->prototype() || !object->prototype().isObject())
384 return jsUndefined();
385 object = asObject(object->prototype());
386 }
387}
388
389bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
390{
391 if (!value.isObject())
392 return false;
393
394 if (!proto.isObject()) {
395 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
396 return false;
397 }
398
399 JSObject* object = asObject(value);
400 while ((object = object->prototype().getObject())) {
401 if (proto == object)
402 return true;
403 }
404 return false;
405}
406
407bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
408{
409 PropertyDescriptor descriptor;
410 if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
411 return false;
412 return descriptor.enumerable();
413}
414
415bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
416{
417 unsigned attributes;
418 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
419 return true;
420
421 // This could be a function within the static table? - should probably
422 // also look in the hash? This currently should not be a problem, since
423 // we've currently always call 'get' first, which should have populated
424 // the normal storage.
425 return false;
426}
427
428void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
429{
430 getOwnPropertyNames(exec, propertyNames, mode);
431
432 if (prototype().isNull())
433 return;
434
435 JSObject* prototype = asObject(this->prototype());
436 while(1) {
437 if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
438 prototype->getPropertyNames(exec, propertyNames, mode);
439 break;
440 }
441 prototype->getOwnPropertyNames(exec, propertyNames, mode);
442 JSValue nextProto = prototype->prototype();
443 if (nextProto.isNull())
444 break;
445 prototype = asObject(nextProto);
446 }
447}
448
449void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
450{
451 m_structure->getPropertyNames(propertyNames, mode);
452 getClassPropertyNames(exec, classInfo(), propertyNames, mode);
453}
454
455bool JSObject::toBoolean(ExecState*) const
456{
457 return true;
458}
459
460double JSObject::toNumber(ExecState* exec) const
461{
462 JSValue primitive = toPrimitive(exec, PreferNumber);
463 if (exec->hadException()) // should be picked up soon in Nodes.cpp
464 return 0.0;
465 return primitive.toNumber(exec);
466}
467
468UString JSObject::toString(ExecState* exec) const
469{
470 JSValue primitive = toPrimitive(exec, PreferString);
471 if (exec->hadException())
472 return "";
473 return primitive.toString(exec);
474}
475
476JSObject* JSObject::toObject(ExecState*) const
477{
478 return const_cast<JSObject*>(this);
479}
480
481JSObject* JSObject::toThisObject(ExecState*) const
482{
483 return const_cast<JSObject*>(this);
484}
485
486JSObject* JSObject::unwrappedObject()
487{
488 return this;
489}
490
491void JSObject::removeDirect(const Identifier& propertyName)
492{
493 size_t offset;
494 if (m_structure->isUncacheableDictionary()) {
495 offset = m_structure->removePropertyWithoutTransition(propertyName);
496 if (offset != WTF::notFound)
497 putDirectOffset(offset, jsUndefined());
498 return;
499 }
500
501 RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
502 setStructure(structure.release());
503 if (offset != WTF::notFound)
504 putDirectOffset(offset, jsUndefined());
505}
506
507void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
508{
509 putDirectFunction(Identifier(exec, function->name(exec)), function, attr);
510}
511
512void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
513{
514 putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr);
515}
516
517NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
518{
519 if (JSObject* getterFunction = asGetterSetter(*location)->getter())
520 slot.setGetterSlot(getterFunction);
521 else
522 slot.setUndefined();
523}
524
525Structure* JSObject::createInheritorID()
526{
527 m_inheritorID = JSObject::createStructure(this);
528 return m_inheritorID.get();
529}
530
531void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
532{
533 allocatePropertyStorageInline(oldSize, newSize);
534}
535
536bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
537{
538 unsigned attributes = 0;
539 JSCell* cell = 0;
540 size_t offset = m_structure->get(propertyName, attributes, cell);
541 if (offset == WTF::notFound)
542 return false;
543 descriptor.setDescriptor(getDirectOffset(offset), attributes);
544 return true;
545}
546
547bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
548{
549 JSObject* object = this;
550 while (true) {
551 if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
552 return true;
553 JSValue prototype = object->prototype();
554 if (!prototype.isObject())
555 return false;
556 object = asObject(prototype);
557 }
558}
559
560static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue)
561{
562 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
563 target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter));
564 return true;
565 }
566 attributes &= ~ReadOnly;
567 if (descriptor.getter() && descriptor.getter().isObject())
568 target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
569 if (exec->hadException())
570 return false;
571 if (descriptor.setter() && descriptor.setter().isObject())
572 target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
573 return !exec->hadException();
574}
575
576bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
577{
578 // If we have a new property we can just put it on normally
579 PropertyDescriptor current;
580 if (!getOwnPropertyDescriptor(exec, propertyName, current))
581 return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined());
582
583 if (descriptor.isEmpty())
584 return true;
585
586 if (current.equalTo(exec, descriptor))
587 return true;
588
589 // Filter out invalid changes
590 if (!current.configurable()) {
591 if (descriptor.configurable()) {
592 if (throwException)
593 throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property.");
594 return false;
595 }
596 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
597 if (throwException)
598 throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property.");
599 return false;
600 }
601 }
602
603 // A generic descriptor is simply changing the attributes of an existing property
604 if (descriptor.isGenericDescriptor()) {
605 if (!current.attributesEqual(descriptor)) {
606 deleteProperty(exec, propertyName);
607 putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
608 }
609 return true;
610 }
611
612 // Changing between a normal property or an accessor property
613 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
614 if (!current.configurable()) {
615 if (throwException)
616 throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property.");
617 return false;
618 }
619 deleteProperty(exec, propertyName);
620 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined());
621 }
622
623 // Changing the value and attributes of an existing property
624 if (descriptor.isDataDescriptor()) {
625 if (!current.configurable()) {
626 if (!current.writable() && descriptor.writable()) {
627 if (throwException)
628 throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property.");
629 return false;
630 }
631 if (!current.writable()) {
632 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
633 if (throwException)
634 throwError(exec, TypeError, "Attempting to change value of a readonly property.");
635 return false;
636 }
637 }
638 } else if (current.attributesEqual(descriptor)) {
639 if (!descriptor.value())
640 return true;
641 PutPropertySlot slot;
642 put(exec, propertyName, descriptor.value(), slot);
643 if (exec->hadException())
644 return false;
645 return true;
646 }
647 deleteProperty(exec, propertyName);
648 return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
649 }
650
651 // Changing the accessor functions of an existing accessor property
652 ASSERT(descriptor.isAccessorDescriptor());
653 if (!current.configurable()) {
654 if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
655 if (throwException)
656 throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property.");
657 return false;
658 }
659 if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
660 if (throwException)
661 throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property.");
662 return false;
663 }
664 }
665 JSValue accessor = getDirect(propertyName);
666 if (!accessor)
667 return false;
668 GetterSetter* getterSetter = asGetterSetter(accessor);
669 if (current.attributesEqual(descriptor)) {
670 if (descriptor.setter())
671 getterSetter->setSetter(asObject(descriptor.setter()));
672 if (descriptor.getter())
673 getterSetter->setGetter(asObject(descriptor.getter()));
674 return true;
675 }
676 deleteProperty(exec, propertyName);
677 unsigned attrs = current.attributesWithOverride(descriptor);
678 if (descriptor.setter())
679 attrs |= Setter;
680 if (descriptor.getter())
681 attrs |= Getter;
682 putDirect(propertyName, getterSetter, attrs);
683 return true;
684}
685
686} // namespace JSC
Note: See TracBrowser for help on using the repository browser.