source: webkit/trunk/JavaScriptCore/kjs/JSObject.cpp@ 37333

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

2008-10-06 Maciej Stachowiak <[email protected]>

Reviewed by Sam Weinig.



1) Make JSValue::toBoolean nonvirtual and completely inline by
making use of the StructureID type field.


2) Make JSValue::toBoolean not take an ExecState; doesn't need it.


3) Make op_not, op_loop_if_true and op_jtrue not read the
ExecState (toBoolean doesn't need it any more) and not check
exceptions (toBoolean can't throw).

  • API/JSValueRef.cpp: (JSValueToBoolean):
  • JavaScriptCore.exp:
  • VM/CodeBlock.cpp: (JSC::CodeBlock::dump):
  • VM/Machine.cpp: (JSC::Machine::privateExecute): (JSC::Machine::cti_op_loop_if_true): (JSC::Machine::cti_op_not): (JSC::Machine::cti_op_jtrue):
  • kjs/ArrayPrototype.cpp: (JSC::arrayProtoFuncFilter): (JSC::arrayProtoFuncEvery): (JSC::arrayProtoFuncSome):
  • kjs/BooleanConstructor.cpp: (JSC::constructBoolean): (JSC::callBooleanConstructor):
  • kjs/GetterSetter.h:
  • kjs/JSCell.h: (JSC::JSValue::toBoolean):
  • kjs/JSNumberCell.cpp:
  • kjs/JSNumberCell.h: (JSC::JSNumberCell::toBoolean):
  • kjs/JSObject.cpp:
  • kjs/JSObject.h: (JSC::JSObject::toBoolean): (JSC::JSCell::toBoolean):
  • kjs/JSString.cpp:
  • kjs/JSString.h: (JSC::JSString::toBoolean):
  • kjs/JSValue.h:
  • kjs/RegExpConstructor.cpp: (JSC::setRegExpConstructorMultiline):
  • kjs/RegExpObject.cpp: (JSC::RegExpObject::match):
  • kjs/RegExpPrototype.cpp: (JSC::regExpProtoFuncToString):
  • Property svn:eol-style set to native
File size: 16.3 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 <profiler/Profiler.h>
39#include <wtf/Assertions.h>
40
41#define JSOBJECT_MARK_TRACING 0
42
43#if JSOBJECT_MARK_TRACING
44
45#define JSOBJECT_MARK_BEGIN() \
46 static int markStackDepth = 0; \
47 for (int i = 0; i < markStackDepth; i++) \
48 putchar('-'); \
49 printf("%s (%p)\n", className().UTF8String().c_str(), this); \
50 markStackDepth++; \
51
52#define JSOBJECT_MARK_END() \
53 markStackDepth--;
54
55#else // JSOBJECT_MARK_TRACING
56
57#define JSOBJECT_MARK_BEGIN()
58#define JSOBJECT_MARK_END()
59
60#endif // JSOBJECT_MARK_TRACING
61
62namespace JSC {
63
64ASSERT_CLASS_FITS_IN_CELL(JSObject);
65
66void JSObject::mark()
67{
68 JSOBJECT_MARK_BEGIN();
69
70 JSCell::mark();
71 m_structureID->mark();
72
73 unsigned storageSize = m_structureID->propertyMap().storageSize();
74 for (unsigned i = 0; i < storageSize; ++i) {
75 JSValue* v = m_propertyStorage[i];
76 if (!v->marked())
77 v->mark();
78 }
79
80 JSOBJECT_MARK_END();
81}
82
83UString JSObject::className() const
84{
85 const ClassInfo* info = classInfo();
86 if (info)
87 return info->className;
88 return "Object";
89}
90
91bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
92{
93 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
94}
95
96static void throwSetterError(ExecState* exec)
97{
98 throwError(exec, TypeError, "setting a property that has only a getter");
99}
100
101// ECMA 8.6.2.2
102void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
103{
104 ASSERT(value);
105 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
106
107 if (propertyName == exec->propertyNames().underscoreProto) {
108 JSObject* proto = value->getObject();
109
110 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
111 if (!proto && !value->isNull())
112 return;
113
114 while (proto) {
115 if (proto == this) {
116 throwError(exec, GeneralError, "cyclic __proto__ value");
117 return;
118 }
119 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
120 }
121
122 setPrototype(value);
123 return;
124 }
125
126 // Check if there are any setters or getters in the prototype chain
127 JSValue* prototype;
128 for (JSObject* obj = this; !obj->structureID()->propertyMap().hasGetterSetterProperties(); obj = static_cast<JSObject*>(prototype)) {
129 prototype = obj->prototype();
130 if (prototype->isNull()) {
131 putDirect(propertyName, value, 0, true, slot);
132 return;
133 }
134 }
135
136 unsigned attributes;
137 if ((m_structureID->propertyMap().getOffset(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
138 return;
139
140 for (JSObject* obj = this; ; obj = static_cast<JSObject*>(prototype)) {
141 if (JSValue* gs = obj->getDirect(propertyName)) {
142 if (gs->isGetterSetter()) {
143 JSObject* setterFunc = static_cast<GetterSetter*>(gs)->setter();
144 if (!setterFunc) {
145 throwSetterError(exec);
146 return;
147 }
148
149 CallData callData;
150 CallType callType = setterFunc->getCallData(callData);
151 ArgList args;
152 args.append(value);
153 call(exec, setterFunc, callType, callData, this, args);
154 return;
155 }
156
157 // If there's an existing property on the object or one of its
158 // prototypes it should be replaced, so break here.
159 break;
160 }
161
162 prototype = obj->prototype();
163 if (prototype->isNull())
164 break;
165 }
166
167 putDirect(propertyName, value, 0, true, slot);
168 return;
169}
170
171void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
172{
173 PutPropertySlot slot;
174 put(exec, Identifier::from(exec, propertyName), value, slot);
175}
176
177void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
178{
179 putDirect(propertyName, value, attributes);
180}
181
182void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
183{
184 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
185}
186
187bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
188{
189 PropertySlot slot;
190 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
191}
192
193bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
194{
195 PropertySlot slot;
196 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
197}
198
199// ECMA 8.6.2.5
200bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
201{
202 unsigned attributes;
203 if (m_structureID->propertyMap().getOffset(propertyName, attributes) != WTF::notFound) {
204 if ((attributes & DontDelete))
205 return false;
206 removeDirect(propertyName);
207 return true;
208 }
209
210 // Look in the static hashtable of properties
211 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
212 if (entry && entry->attributes() & DontDelete)
213 return false; // this builtin property can't be deleted
214
215 // FIXME: Should the code here actually do some deletion?
216 return true;
217}
218
219bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
220{
221 PropertySlot slot;
222 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
223}
224
225bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
226{
227 return deleteProperty(exec, Identifier::from(exec, propertyName));
228}
229
230static ALWAYS_INLINE JSValue* callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
231{
232 JSValue* function = object->get(exec, propertyName);
233 CallData callData;
234 CallType callType = function->getCallData(callData);
235 if (callType == CallTypeNone)
236 return exec->exception();
237
238 // Prevent "toString" and "valueOf" from observing execution if an exception
239 // is pending.
240 if (exec->hadException())
241 return exec->exception();
242
243 JSValue* result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
244 ASSERT(!result->isGetterSetter());
245 if (exec->hadException())
246 return exec->exception();
247 if (result->isObject())
248 return 0;
249 return result;
250}
251
252bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
253{
254 result = defaultValue(exec, PreferNumber);
255 number = result->toNumber(exec);
256 return !result->isString();
257}
258
259// ECMA 8.6.2.6
260JSValue* JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
261{
262 // Must call toString first for Date objects.
263 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
264 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
265 return value;
266 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
267 return value;
268 } else {
269 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
270 return value;
271 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
272 return value;
273 }
274
275 ASSERT(!exec->hadException());
276
277 return throwError(exec, TypeError, "No default value");
278}
279
280const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
281{
282 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
283 if (const HashTable* propHashTable = info->propHashTable(exec)) {
284 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
285 return entry;
286 }
287 }
288 return 0;
289}
290
291void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
292{
293 JSValue* object = getDirect(propertyName);
294 if (object && object->isGetterSetter()) {
295 ASSERT(m_structureID->propertyMap().hasGetterSetterProperties());
296 GetterSetter* getterSetter = static_cast<GetterSetter*>(object);
297 getterSetter->setGetter(getterFunction);
298 return;
299 }
300
301 PutPropertySlot slot;
302 GetterSetter* getterSetter = new (exec) GetterSetter;
303 putDirect(propertyName, getterSetter, None, true, slot);
304
305 // putDirect will change our StructureID if we add a new property. For
306 // getters and setters, though, we also need to change our StructureID
307 // if we override an existing non-getter or non-setter.
308 if (slot.type() != PutPropertySlot::NewProperty) {
309 if (!m_structureID->isDictionary()) {
310 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
311 setStructureID(structureID.release());
312 }
313 }
314
315 m_structureID->propertyMap().setHasGetterSetterProperties(true);
316 getterSetter->setGetter(getterFunction);
317}
318
319void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
320{
321 JSValue* object = getDirect(propertyName);
322 if (object && object->isGetterSetter()) {
323 ASSERT(m_structureID->propertyMap().hasGetterSetterProperties());
324 GetterSetter* getterSetter = static_cast<GetterSetter*>(object);
325 getterSetter->setSetter(setterFunction);
326 return;
327 }
328
329 PutPropertySlot slot;
330 GetterSetter* getterSetter = new (exec) GetterSetter;
331 putDirect(propertyName, getterSetter, None, true, slot);
332
333 // putDirect will change our StructureID if we add a new property. For
334 // getters and setters, though, we also need to change our StructureID
335 // if we override an existing non-getter or non-setter.
336 if (slot.type() != PutPropertySlot::NewProperty) {
337 if (!m_structureID->isDictionary()) {
338 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
339 setStructureID(structureID.release());
340 }
341 }
342
343 m_structureID->propertyMap().setHasGetterSetterProperties(true);
344 getterSetter->setSetter(setterFunction);
345}
346
347JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
348{
349 JSObject* object = this;
350 while (true) {
351 JSValue* value = object->getDirect(propertyName);
352 if (value) {
353 if (!value->isGetterSetter())
354 return jsUndefined();
355 JSObject* functionObject = static_cast<GetterSetter*>(value)->getter();
356 if (!functionObject)
357 return jsUndefined();
358 return functionObject;
359 }
360
361 if (!object->prototype() || !object->prototype()->isObject())
362 return jsUndefined();
363 object = static_cast<JSObject*>(object->prototype());
364 }
365}
366
367JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
368{
369 JSObject* object = this;
370 while (true) {
371 JSValue* value = object->getDirect(propertyName);
372 if (value) {
373 if (!value->isGetterSetter())
374 return jsUndefined();
375 JSObject* functionObject = static_cast<GetterSetter*>(value)->setter();
376 if (!functionObject)
377 return jsUndefined();
378 return functionObject;
379 }
380
381 if (!object->prototype() || !object->prototype()->isObject())
382 return jsUndefined();
383 object = static_cast<JSObject*>(object->prototype());
384 }
385}
386
387bool JSObject::hasInstance(ExecState* exec, JSValue* value, JSValue* proto)
388{
389 if (!proto->isObject()) {
390 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
391 return false;
392 }
393
394 if (!value->isObject())
395 return false;
396
397 JSObject* object = static_cast<JSObject*>(value);
398 while ((object = object->prototype()->getObject())) {
399 if (object == proto)
400 return true;
401 }
402 return false;
403}
404
405bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
406{
407 unsigned attributes;
408 if (!getPropertyAttributes(exec, propertyName, attributes))
409 return false;
410 return !(attributes & DontEnum);
411}
412
413bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
414{
415 if (m_structureID->propertyMap().getOffset(propertyName, attributes) != WTF::notFound)
416 return true;
417
418 // Look in the static hashtable of properties
419 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
420 if (entry) {
421 attributes = entry->attributes();
422 return true;
423 }
424
425 return false;
426}
427
428void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
429{
430 m_structureID->getEnumerablePropertyNames(exec, propertyNames, this);
431}
432
433double JSObject::toNumber(ExecState* exec) const
434{
435 JSValue* primitive = toPrimitive(exec, PreferNumber);
436 if (exec->hadException()) // should be picked up soon in nodes.cpp
437 return 0.0;
438 return primitive->toNumber(exec);
439}
440
441UString JSObject::toString(ExecState* exec) const
442{
443 JSValue* primitive = toPrimitive(exec, PreferString);
444 if (exec->hadException())
445 return "";
446 return primitive->toString(exec);
447}
448
449JSObject* JSObject::toObject(ExecState*) const
450{
451 return const_cast<JSObject*>(this);
452}
453
454JSObject* JSObject::toThisObject(ExecState*) const
455{
456 return const_cast<JSObject*>(this);
457}
458
459JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
460{
461 return 0;
462}
463
464void JSObject::removeDirect(const Identifier& propertyName)
465{
466 if (m_structureID->isDictionary()) {
467 m_structureID->propertyMap().remove(propertyName, m_propertyStorage);
468 m_structureID->clearEnumerationCache();
469 return;
470 }
471
472 RefPtr<StructureID> structureID = StructureID::toDictionaryTransition(m_structureID);
473 structureID->propertyMap().remove(propertyName, m_propertyStorage);
474 setStructureID(structureID.release());
475}
476
477void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
478{
479 putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
480}
481
482NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
483{
484 if (JSObject* getterFunction = static_cast<GetterSetter*>(*location)->getter())
485 slot.setGetterSlot(getterFunction);
486 else
487 slot.setUndefined();
488}
489
490StructureID* JSObject::createInheritorID()
491{
492 m_inheritorID = JSObject::createStructureID(this);
493 return m_inheritorID.get();
494}
495
496void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
497{
498 JSValue** oldPropertStorage = m_propertyStorage;
499 m_propertyStorage = new JSValue*[newSize];
500
501 for (unsigned i = 0; i < oldSize; ++i)
502 m_propertyStorage[i] = oldPropertStorage[i];
503
504 if (oldPropertStorage != m_inlineStorage)
505 delete [] oldPropertStorage;
506}
507
508JSObject* constructEmptyObject(ExecState* exec)
509{
510 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
511}
512
513} // namespace JSC
Note: See TracBrowser for help on using the repository browser.