• 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 "JSGlobalObject.h"
31 #include "NativeErrorConstructor.h"
32 #include "ObjectPrototype.h"
33 #include "PropertyNameArray.h"
34 #include "Lookup.h"
35 #include "Nodes.h"
36 #include "Operations.h"
37 #include <math.h>
38 #include <wtf/Assertions.h>
39 
40 #define JSOBJECT_MARK_TRACING 0
41 
42 #if JSOBJECT_MARK_TRACING
43 
44 #define JSOBJECT_MARK_BEGIN() \
45     static int markStackDepth = 0; \
46     for (int i = 0; i < markStackDepth; i++) \
47         putchar('-'); \
48     printf("%s (%p)\n", className().UTF8String().c_str(), this); \
49     markStackDepth++; \
50 
51 #define JSOBJECT_MARK_END() \
52     markStackDepth--;
53 
54 #else // JSOBJECT_MARK_TRACING
55 
56 #define JSOBJECT_MARK_BEGIN()
57 #define JSOBJECT_MARK_END()
58 
59 #endif // JSOBJECT_MARK_TRACING
60 
61 namespace JSC {
62 
63 ASSERT_CLASS_FITS_IN_CELL(JSObject);
64 
markChildren(MarkStack & markStack)65 void JSObject::markChildren(MarkStack& markStack)
66 {
67     JSOBJECT_MARK_BEGIN();
68 
69     JSCell::markChildren(markStack);
70     m_structure->markAggregate(markStack);
71 
72     PropertyStorage storage = propertyStorage();
73     size_t storageSize = m_structure->propertyStorageSize();
74     markStack.appendValues(reinterpret_cast<JSValue*>(storage), storageSize);
75 
76     JSOBJECT_MARK_END();
77 }
78 
className() const79 UString JSObject::className() const
80 {
81     const ClassInfo* info = classInfo();
82     if (info)
83         return info->className;
84     return "Object";
85 }
86 
getOwnPropertySlot(ExecState * exec,unsigned propertyName,PropertySlot & slot)87 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
88 {
89     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
90 }
91 
throwSetterError(ExecState * exec)92 static void throwSetterError(ExecState* exec)
93 {
94     throwError(exec, TypeError, "setting a property that has only a getter");
95 }
96 
97 // ECMA 8.6.2.2
put(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot & slot)98 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
99 {
100     ASSERT(value);
101     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
102 
103     if (propertyName == exec->propertyNames().underscoreProto) {
104         // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
105         if (!value.isObject() && !value.isNull())
106             return;
107 
108         JSValue nextPrototypeValue = value;
109         while (nextPrototypeValue && nextPrototypeValue.isObject()) {
110             JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
111             if (nextPrototype == this) {
112                 throwError(exec, GeneralError, "cyclic __proto__ value");
113                 return;
114             }
115             nextPrototypeValue = nextPrototype->prototype();
116         }
117 
118         setPrototype(value);
119         return;
120     }
121 
122     // Check if there are any setters or getters in the prototype chain
123     JSValue prototype;
124     for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
125         prototype = obj->prototype();
126         if (prototype.isNull()) {
127             putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
128             return;
129         }
130     }
131 
132     unsigned attributes;
133     JSCell* specificValue;
134     if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
135         return;
136 
137     for (JSObject* obj = this; ; obj = asObject(prototype)) {
138         if (JSValue gs = obj->getDirect(propertyName)) {
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);
148                 MarkedArgumentBuffer args;
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 
164     putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
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(ExecState * exec,const Identifier & propertyName,JSValue value,unsigned attributes,bool checkReadOnly,PutPropertySlot & slot)174 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
175 {
176     putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
177 }
178 
putWithAttributes(ExecState * exec,const Identifier & propertyName,JSValue value,unsigned attributes)179 void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
180 {
181     putDirectInternal(exec->globalData(), propertyName, value, attributes);
182 }
183 
putWithAttributes(ExecState * exec,unsigned propertyName,JSValue value,unsigned attributes)184 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
185 {
186     putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
187 }
188 
hasProperty(ExecState * exec,const Identifier & propertyName) const189 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
190 {
191     PropertySlot slot;
192     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
193 }
194 
hasProperty(ExecState * exec,unsigned propertyName) const195 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
196 {
197     PropertySlot slot;
198     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
199 }
200 
201 // ECMA 8.6.2.5
deleteProperty(ExecState * exec,const Identifier & propertyName)202 bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
203 {
204     unsigned attributes;
205     JSCell* specificValue;
206     if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
207         if ((attributes & DontDelete))
208             return false;
209         removeDirect(propertyName);
210         return true;
211     }
212 
213     // Look in the static hashtable of properties
214     const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
215     if (entry && entry->attributes() & DontDelete)
216         return false; // this builtin property can't be deleted
217 
218     // FIXME: Should the code here actually do some deletion?
219     return true;
220 }
221 
hasOwnProperty(ExecState * exec,const Identifier & propertyName) const222 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
223 {
224     PropertySlot slot;
225     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
226 }
227 
deleteProperty(ExecState * exec,unsigned propertyName)228 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
229 {
230     return deleteProperty(exec, Identifier::from(exec, propertyName));
231 }
232 
callDefaultValueFunction(ExecState * exec,const JSObject * object,const Identifier & propertyName)233 static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
234 {
235     JSValue function = object->get(exec, propertyName);
236     CallData callData;
237     CallType callType = function.getCallData(callData);
238     if (callType == CallTypeNone)
239         return exec->exception();
240 
241     // Prevent "toString" and "valueOf" from observing execution if an exception
242     // is pending.
243     if (exec->hadException())
244         return exec->exception();
245 
246     JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
247     ASSERT(!result.isGetterSetter());
248     if (exec->hadException())
249         return exec->exception();
250     if (result.isObject())
251         return JSValue();
252     return result;
253 }
254 
getPrimitiveNumber(ExecState * exec,double & number,JSValue & result)255 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
256 {
257     result = defaultValue(exec, PreferNumber);
258     number = result.toNumber(exec);
259     return !result.isString();
260 }
261 
262 // ECMA 8.6.2.6
defaultValue(ExecState * exec,PreferredPrimitiveType hint) const263 JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
264 {
265     // Must call toString first for Date objects.
266     if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
267         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
268         if (value)
269             return value;
270         value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
271         if (value)
272             return value;
273     } else {
274         JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
275         if (value)
276             return value;
277         value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
278         if (value)
279             return value;
280     }
281 
282     ASSERT(!exec->hadException());
283 
284     return throwError(exec, TypeError, "No default value");
285 }
286 
findPropertyHashEntry(ExecState * exec,const Identifier & propertyName) const287 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
288 {
289     for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
290         if (const HashTable* propHashTable = info->propHashTable(exec)) {
291             if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
292                 return entry;
293         }
294     }
295     return 0;
296 }
297 
defineGetter(ExecState * exec,const Identifier & propertyName,JSObject * getterFunction)298 void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
299 {
300     JSValue object = getDirect(propertyName);
301     if (object && object.isGetterSetter()) {
302         ASSERT(m_structure->hasGetterSetterProperties());
303         asGetterSetter(object)->setGetter(getterFunction);
304         return;
305     }
306 
307     PutPropertySlot slot;
308     GetterSetter* getterSetter = new (exec) GetterSetter(exec);
309     putDirectInternal(exec->globalData(), propertyName, getterSetter, Getter, true, slot);
310 
311     // putDirect will change our Structure if we add a new property. For
312     // getters and setters, though, we also need to change our Structure
313     // if we override an existing non-getter or non-setter.
314     if (slot.type() != PutPropertySlot::NewProperty) {
315         if (!m_structure->isDictionary()) {
316             RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
317             setStructure(structure.release());
318         }
319     }
320 
321     m_structure->setHasGetterSetterProperties(true);
322     getterSetter->setGetter(getterFunction);
323 }
324 
defineSetter(ExecState * exec,const Identifier & propertyName,JSObject * setterFunction)325 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
326 {
327     JSValue object = getDirect(propertyName);
328     if (object && object.isGetterSetter()) {
329         ASSERT(m_structure->hasGetterSetterProperties());
330         asGetterSetter(object)->setSetter(setterFunction);
331         return;
332     }
333 
334     PutPropertySlot slot;
335     GetterSetter* getterSetter = new (exec) GetterSetter(exec);
336     putDirectInternal(exec->globalData(), propertyName, getterSetter, Setter, true, slot);
337 
338     // putDirect will change our Structure if we add a new property. For
339     // getters and setters, though, we also need to change our Structure
340     // if we override an existing non-getter or non-setter.
341     if (slot.type() != PutPropertySlot::NewProperty) {
342         if (!m_structure->isDictionary()) {
343             RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
344             setStructure(structure.release());
345         }
346     }
347 
348     m_structure->setHasGetterSetterProperties(true);
349     getterSetter->setSetter(setterFunction);
350 }
351 
lookupGetter(ExecState *,const Identifier & propertyName)352 JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
353 {
354     JSObject* object = this;
355     while (true) {
356         if (JSValue value = object->getDirect(propertyName)) {
357             if (!value.isGetterSetter())
358                 return jsUndefined();
359             JSObject* functionObject = asGetterSetter(value)->getter();
360             if (!functionObject)
361                 return jsUndefined();
362             return functionObject;
363         }
364 
365         if (!object->prototype() || !object->prototype().isObject())
366             return jsUndefined();
367         object = asObject(object->prototype());
368     }
369 }
370 
lookupSetter(ExecState *,const Identifier & propertyName)371 JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
372 {
373     JSObject* object = this;
374     while (true) {
375         if (JSValue value = object->getDirect(propertyName)) {
376             if (!value.isGetterSetter())
377                 return jsUndefined();
378             JSObject* functionObject = asGetterSetter(value)->setter();
379             if (!functionObject)
380                 return jsUndefined();
381             return functionObject;
382         }
383 
384         if (!object->prototype() || !object->prototype().isObject())
385             return jsUndefined();
386         object = asObject(object->prototype());
387     }
388 }
389 
hasInstance(ExecState * exec,JSValue value,JSValue proto)390 bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
391 {
392     if (!value.isObject())
393         return false;
394 
395     if (!proto.isObject()) {
396         throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
397         return false;
398     }
399 
400     JSObject* object = asObject(value);
401     while ((object = object->prototype().getObject())) {
402         if (proto == object)
403             return true;
404     }
405     return false;
406 }
407 
propertyIsEnumerable(ExecState * exec,const Identifier & propertyName) const408 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
409 {
410     unsigned attributes;
411     if (!getPropertyAttributes(exec, propertyName, attributes))
412         return false;
413     return !(attributes & DontEnum);
414 }
415 
getPropertyAttributes(ExecState * exec,const Identifier & propertyName,unsigned & attributes) const416 bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
417 {
418     JSCell* specificValue;
419     if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
420         return true;
421 
422     // Look in the static hashtable of properties
423     const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
424     if (entry) {
425         attributes = entry->attributes();
426         return true;
427     }
428 
429     return false;
430 }
431 
getPropertySpecificValue(ExecState *,const Identifier & propertyName,JSCell * & specificValue) const432 bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
433 {
434     unsigned attributes;
435     if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
436         return true;
437 
438     // This could be a function within the static table? - should probably
439     // also look in the hash?  This currently should not be a problem, since
440     // we've currently always call 'get' first, which should have populated
441     // the normal storage.
442     return false;
443 }
444 
getPropertyNames(ExecState * exec,PropertyNameArray & propertyNames)445 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
446 {
447     m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
448 }
449 
toBoolean(ExecState *) const450 bool JSObject::toBoolean(ExecState*) const
451 {
452     return true;
453 }
454 
toNumber(ExecState * exec) const455 double JSObject::toNumber(ExecState* exec) const
456 {
457     JSValue primitive = toPrimitive(exec, PreferNumber);
458     if (exec->hadException()) // should be picked up soon in Nodes.cpp
459         return 0.0;
460     return primitive.toNumber(exec);
461 }
462 
toString(ExecState * exec) const463 UString JSObject::toString(ExecState* exec) const
464 {
465     JSValue primitive = toPrimitive(exec, PreferString);
466     if (exec->hadException())
467         return "";
468     return primitive.toString(exec);
469 }
470 
toObject(ExecState *) const471 JSObject* JSObject::toObject(ExecState*) const
472 {
473     return const_cast<JSObject*>(this);
474 }
475 
toThisObject(ExecState *) const476 JSObject* JSObject::toThisObject(ExecState*) const
477 {
478     return const_cast<JSObject*>(this);
479 }
480 
unwrappedObject()481 JSObject* JSObject::unwrappedObject()
482 {
483     return this;
484 }
485 
removeDirect(const Identifier & propertyName)486 void JSObject::removeDirect(const Identifier& propertyName)
487 {
488     size_t offset;
489     if (m_structure->isDictionary()) {
490         offset = m_structure->removePropertyWithoutTransition(propertyName);
491         if (offset != WTF::notFound)
492             putDirectOffset(offset, jsUndefined());
493         return;
494     }
495 
496     RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
497     setStructure(structure.release());
498     if (offset != WTF::notFound)
499         putDirectOffset(offset, jsUndefined());
500 }
501 
putDirectFunction(ExecState * exec,InternalFunction * function,unsigned attr)502 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
503 {
504     putDirectFunction(Identifier(exec, function->name(&exec->globalData())), function, attr);
505 }
506 
putDirectFunctionWithoutTransition(ExecState * exec,InternalFunction * function,unsigned attr)507 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
508 {
509     putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
510 }
511 
fillGetterPropertySlot(PropertySlot & slot,JSValue * location)512 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
513 {
514     if (JSObject* getterFunction = asGetterSetter(*location)->getter())
515         slot.setGetterSlot(getterFunction);
516     else
517         slot.setUndefined();
518 }
519 
createInheritorID()520 Structure* JSObject::createInheritorID()
521 {
522     m_inheritorID = JSObject::createStructure(this);
523     return m_inheritorID.get();
524 }
525 
allocatePropertyStorage(size_t oldSize,size_t newSize)526 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
527 {
528     allocatePropertyStorageInline(oldSize, newSize);
529 }
530 
constructEmptyObject(ExecState * exec)531 JSObject* constructEmptyObject(ExecState* exec)
532 {
533     return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
534 }
535 
536 } // namespace JSC
537