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

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

JavaScriptCore:

2009-05-11 Gavin Barraclough <[email protected]>

Reviewed by Cameron Zwarich.

Implement JIT generation for instanceof for non-objects (always returns false).
Also fixes the sequencing of the prototype and value isObject checks, to no match the spec.

0.5% progression on v8 tests overall, due to 3.5% on early-boyer.

  • jit/JIT.cpp: (JSC::JIT::privateCompileMainPass): (JSC::JIT::privateCompileSlowCases):
  • runtime/JSObject.cpp: (JSC::JSObject::hasInstance):
  • runtime/TypeInfo.h: (JSC::TypeInfo::TypeInfo):

LayoutTests:

2009-05-11 Gavin Barraclough <[email protected]>

Reviewed by Oliver Hunt.

Add a layout test for instanceof.

  • fast/js/instance-of-immediates-expected.txt: Added.
  • fast/js/instance-of-immediates.html: Added.
  • fast/js/resources/instance-of-immediates.js: Added. (testInstanceOf): (): (testSet):
  • Property svn:eol-style set to native
File size: 16.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 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 "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
61namespace JSC {
62
63ASSERT_CLASS_FITS_IN_CELL(JSObject);
64
65void JSObject::mark()
66{
67 JSOBJECT_MARK_BEGIN();
68
69 JSCell::mark();
70 m_structure->mark();
71
72 PropertyStorage storage = propertyStorage();
73
74 size_t storageSize = m_structure->propertyStorageSize();
75 for (size_t i = 0; i < storageSize; ++i) {
76 JSValue v = JSValue::decode(storage[i]);
77 if (!v.marked())
78 v.mark();
79 }
80
81 JSOBJECT_MARK_END();
82}
83
84UString JSObject::className() const
85{
86 const ClassInfo* info = classInfo();
87 if (info)
88 return info->className;
89 return "Object";
90}
91
92bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
93{
94 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
95}
96
97static void throwSetterError(ExecState* exec)
98{
99 throwError(exec, TypeError, "setting a property that has only a getter");
100}
101
102// ECMA 8.6.2.2
103void 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
113 JSValue nextPrototypeValue = value;
114 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
115 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
116 if (nextPrototype == this) {
117 throwError(exec, GeneralError, "cyclic __proto__ value");
118 return;
119 }
120 nextPrototypeValue = nextPrototype->prototype();
121 }
122
123 setPrototype(value);
124 return;
125 }
126
127 // Check if there are any setters or getters in the prototype chain
128 JSValue prototype;
129 for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
130 prototype = obj->prototype();
131 if (prototype.isNull()) {
132 putDirect(propertyName, value, 0, true, slot);
133 return;
134 }
135 }
136
137 unsigned attributes;
138 if ((m_structure->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
139 return;
140
141 for (JSObject* obj = this; ; obj = asObject(prototype)) {
142 if (JSValue gs = obj->getDirect(propertyName)) {
143 if (gs.isGetterSetter()) {
144 JSObject* setterFunc = asGetterSetter(gs)->setter();
145 if (!setterFunc) {
146 throwSetterError(exec);
147 return;
148 }
149
150 CallData callData;
151 CallType callType = setterFunc->getCallData(callData);
152 MarkedArgumentBuffer args;
153 args.append(value);
154 call(exec, setterFunc, callType, callData, this, args);
155 return;
156 }
157
158 // If there's an existing property on the object or one of its
159 // prototypes it should be replaced, so break here.
160 break;
161 }
162
163 prototype = obj->prototype();
164 if (prototype.isNull())
165 break;
166 }
167
168 putDirect(propertyName, value, 0, true, slot);
169 return;
170}
171
172void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
173{
174 PutPropertySlot slot;
175 put(exec, Identifier::from(exec, propertyName), value, slot);
176}
177
178void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes)
179{
180 putDirect(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 if (m_structure->get(propertyName, attributes) != WTF::notFound) {
205 if ((attributes & DontDelete))
206 return false;
207 removeDirect(propertyName);
208 return true;
209 }
210
211 // Look in the static hashtable of properties
212 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
213 if (entry && entry->attributes() & DontDelete)
214 return false; // this builtin property can't be deleted
215
216 // FIXME: Should the code here actually do some deletion?
217 return true;
218}
219
220bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
221{
222 PropertySlot slot;
223 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
224}
225
226bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
227{
228 return deleteProperty(exec, Identifier::from(exec, propertyName));
229}
230
231static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
232{
233 JSValue function = object->get(exec, propertyName);
234 CallData callData;
235 CallType callType = function.getCallData(callData);
236 if (callType == CallTypeNone)
237 return exec->exception();
238
239 // Prevent "toString" and "valueOf" from observing execution if an exception
240 // is pending.
241 if (exec->hadException())
242 return exec->exception();
243
244 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
245 ASSERT(!result.isGetterSetter());
246 if (exec->hadException())
247 return exec->exception();
248 if (result.isObject())
249 return JSValue();
250 return result;
251}
252
253bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
254{
255 result = defaultValue(exec, PreferNumber);
256 number = result.toNumber(exec);
257 return !result.isString();
258}
259
260// ECMA 8.6.2.6
261JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
262{
263 // Must call toString first for Date objects.
264 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
265 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
266 if (value)
267 return value;
268 value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
269 if (value)
270 return value;
271 } else {
272 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
273 if (value)
274 return value;
275 value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
276 if (value)
277 return value;
278 }
279
280 ASSERT(!exec->hadException());
281
282 return throwError(exec, TypeError, "No default value");
283}
284
285const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
286{
287 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
288 if (const HashTable* propHashTable = info->propHashTable(exec)) {
289 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
290 return entry;
291 }
292 }
293 return 0;
294}
295
296void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
297{
298 JSValue object = getDirect(propertyName);
299 if (object && object.isGetterSetter()) {
300 ASSERT(m_structure->hasGetterSetterProperties());
301 asGetterSetter(object)->setGetter(getterFunction);
302 return;
303 }
304
305 PutPropertySlot slot;
306 GetterSetter* getterSetter = new (exec) GetterSetter;
307 putDirect(propertyName, getterSetter, None, true, slot);
308
309 // putDirect will change our Structure if we add a new property. For
310 // getters and setters, though, we also need to change our Structure
311 // if we override an existing non-getter or non-setter.
312 if (slot.type() != PutPropertySlot::NewProperty) {
313 if (!m_structure->isDictionary()) {
314 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
315 setStructure(structure.release());
316 }
317 }
318
319 m_structure->setHasGetterSetterProperties(true);
320 getterSetter->setGetter(getterFunction);
321}
322
323void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
324{
325 JSValue object = getDirect(propertyName);
326 if (object && object.isGetterSetter()) {
327 ASSERT(m_structure->hasGetterSetterProperties());
328 asGetterSetter(object)->setSetter(setterFunction);
329 return;
330 }
331
332 PutPropertySlot slot;
333 GetterSetter* getterSetter = new (exec) GetterSetter;
334 putDirect(propertyName, getterSetter, None, true, slot);
335
336 // putDirect will change our Structure if we add a new property. For
337 // getters and setters, though, we also need to change our Structure
338 // if we override an existing non-getter or non-setter.
339 if (slot.type() != PutPropertySlot::NewProperty) {
340 if (!m_structure->isDictionary()) {
341 RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
342 setStructure(structure.release());
343 }
344 }
345
346 m_structure->setHasGetterSetterProperties(true);
347 getterSetter->setSetter(setterFunction);
348}
349
350JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
351{
352 JSObject* object = this;
353 while (true) {
354 if (JSValue value = object->getDirect(propertyName)) {
355 if (!value.isGetterSetter())
356 return jsUndefined();
357 JSObject* functionObject = asGetterSetter(value)->getter();
358 if (!functionObject)
359 return jsUndefined();
360 return functionObject;
361 }
362
363 if (!object->prototype() || !object->prototype().isObject())
364 return jsUndefined();
365 object = asObject(object->prototype());
366 }
367}
368
369JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
370{
371 JSObject* object = this;
372 while (true) {
373 if (JSValue value = object->getDirect(propertyName)) {
374 if (!value.isGetterSetter())
375 return jsUndefined();
376 JSObject* functionObject = asGetterSetter(value)->setter();
377 if (!functionObject)
378 return jsUndefined();
379 return functionObject;
380 }
381
382 if (!object->prototype() || !object->prototype().isObject())
383 return jsUndefined();
384 object = asObject(object->prototype());
385 }
386}
387
388bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
389{
390 if (!value.isObject())
391 return false;
392
393 if (!proto.isObject()) {
394 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
395 return false;
396 }
397
398 JSObject* object = asObject(value);
399 while ((object = object->prototype().getObject())) {
400 if (proto == object)
401 return true;
402 }
403 return false;
404}
405
406bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
407{
408 unsigned attributes;
409 if (!getPropertyAttributes(exec, propertyName, attributes))
410 return false;
411 return !(attributes & DontEnum);
412}
413
414bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
415{
416 if (m_structure->get(propertyName, attributes) != WTF::notFound)
417 return true;
418
419 // Look in the static hashtable of properties
420 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
421 if (entry) {
422 attributes = entry->attributes();
423 return true;
424 }
425
426 return false;
427}
428
429void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
430{
431 m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
432}
433
434bool JSObject::toBoolean(ExecState*) const
435{
436 return true;
437}
438
439double JSObject::toNumber(ExecState* exec) const
440{
441 JSValue primitive = toPrimitive(exec, PreferNumber);
442 if (exec->hadException()) // should be picked up soon in Nodes.cpp
443 return 0.0;
444 return primitive.toNumber(exec);
445}
446
447UString JSObject::toString(ExecState* exec) const
448{
449 JSValue primitive = toPrimitive(exec, PreferString);
450 if (exec->hadException())
451 return "";
452 return primitive.toString(exec);
453}
454
455JSObject* JSObject::toObject(ExecState*) const
456{
457 return const_cast<JSObject*>(this);
458}
459
460JSObject* JSObject::toThisObject(ExecState*) const
461{
462 return const_cast<JSObject*>(this);
463}
464
465JSObject* JSObject::unwrappedObject()
466{
467 return this;
468}
469
470void JSObject::removeDirect(const Identifier& propertyName)
471{
472 size_t offset;
473 if (m_structure->isDictionary()) {
474 offset = m_structure->removePropertyWithoutTransition(propertyName);
475 if (offset != WTF::notFound)
476 putDirectOffset(offset, jsUndefined());
477 return;
478 }
479
480 RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
481 setStructure(structure.release());
482 if (offset != WTF::notFound)
483 putDirectOffset(offset, jsUndefined());
484}
485
486void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
487{
488 putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
489}
490
491void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
492{
493 putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
494}
495
496NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
497{
498 if (JSObject* getterFunction = asGetterSetter(*location)->getter())
499 slot.setGetterSlot(getterFunction);
500 else
501 slot.setUndefined();
502}
503
504Structure* JSObject::createInheritorID()
505{
506 m_inheritorID = JSObject::createStructure(this);
507 return m_inheritorID.get();
508}
509
510void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
511{
512 allocatePropertyStorageInline(oldSize, newSize);
513}
514
515JSObject* constructEmptyObject(ExecState* exec)
516{
517 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
518}
519
520} // namespace JSC
Note: See TracBrowser for help on using the repository browser.