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