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

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

2008-07-01 Geoffrey Garen <[email protected]>

Reviewed by Oliver Hunt.


Removed and/or reordered exception checks in array-style a[n] access.


SunSpider says 1.4% faster.

  • VM/Machine.cpp: (KJS::Machine::privateExecute): No need to check for exceptions before calling toString, toNumber and/or get. If the call ends up being observable through toString, valueOf, or a getter, we short-circuit it there, instead. In the op_del_by_val case, I removed the incorrect comment without actually removing the code, since I didn't want to tempt the GCC fates!
  • kjs/JSObject.cpp: (KJS::callDefaultValueFunction): Added exception check to prevent toString and valueOf functions from observing execution after an exception has been thrown. This removes some of the burden of exception checking from the machine.

(KJS::JSObject::defaultValue): Removed redundant exception check here.

  • kjs/PropertySlot.cpp: (KJS::PropertySlot::functionGetter): Added exception check to prevent getter functions from observing execution after an exception has been thrown. This removes some of the burden of exception checking from the machine.
  • Property svn:eol-style set to native
File size: 16.5 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * Copyright (C) 1999-2001 Harri Porten ([email protected])
4 * Copyright (C) 2001 Peter Kelly ([email protected])
5 * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
6 * Copyright (C) 2007 Eric Seidel ([email protected])
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "JSObject.h"
27
28#include "DatePrototype.h"
29#include "ErrorConstructor.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 JAVASCRIPT_MARK_TRACING 0
42
43namespace KJS {
44
45// ------------------------------ JSObject ------------------------------------
46
47void JSObject::mark()
48{
49 JSCell::mark();
50
51#if JAVASCRIPT_MARK_TRACING
52 static int markStackDepth = 0;
53 markStackDepth++;
54 for (int i = 0; i < markStackDepth; i++)
55 putchar('-');
56
57 printf("%s (%p)\n", className().UTF8String().c_str(), this);
58#endif
59
60 JSValue *proto = _proto;
61 if (!proto->marked())
62 proto->mark();
63
64 _prop.mark();
65
66#if JAVASCRIPT_MARK_TRACING
67 markStackDepth--;
68#endif
69}
70
71JSType JSObject::type() const
72{
73 return ObjectType;
74}
75
76UString JSObject::className() const
77{
78 const ClassInfo *ci = classInfo();
79 if ( ci )
80 return ci->className;
81 return "Object";
82}
83
84bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
85{
86 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
87}
88
89static void throwSetterError(ExecState *exec)
90{
91 throwError(exec, TypeError, "setting a property that has only a getter");
92}
93
94// ECMA 8.6.2.2
95void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
96{
97 ASSERT(value);
98 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
99
100 if (propertyName == exec->propertyNames().underscoreProto) {
101 JSObject* proto = value->getObject();
102
103 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
104 if (!proto && value != jsNull())
105 return;
106
107 while (proto) {
108 if (proto == this) {
109 throwError(exec, GeneralError, "cyclic __proto__ value");
110 return;
111 }
112 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
113 }
114
115 setPrototype(value);
116 return;
117 }
118
119 // Check if there are any setters or getters in the prototype chain
120 JSValue* prototype;
121 for (JSObject* obj = this; !obj->_prop.hasGetterSetterProperties(); obj = static_cast<JSObject*>(prototype)) {
122 prototype = obj->_proto;
123 if (prototype == jsNull()) {
124 _prop.put(propertyName, value, 0, true);
125 return;
126 }
127 }
128
129 unsigned attributes;
130 if (_prop.get(propertyName, attributes) && attributes & ReadOnly)
131 return;
132
133 for (JSObject* obj = this; ; obj = static_cast<JSObject*>(prototype)) {
134 if (JSValue* gs = obj->_prop.get(propertyName, attributes)) {
135 if (attributes & IsGetterSetter) {
136 JSObject* setterFunc = static_cast<GetterSetter*>(gs)->setter();
137 if (!setterFunc) {
138 throwSetterError(exec);
139 return;
140 }
141
142 CallData callData;
143 CallType callType = setterFunc->getCallData(callData);
144 ArgList args;
145 args.append(value);
146 call(exec, setterFunc, callType, callData, this, args);
147 return;
148 }
149
150 // If there's an existing property on the object or one of its
151 // prototypes it should be replaced, so break here.
152 break;
153 }
154
155 prototype = obj->_proto;
156 if (prototype == jsNull())
157 break;
158 }
159
160 _prop.put(propertyName, value, 0, true);
161}
162
163void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
164{
165 put(exec, Identifier::from(exec, propertyName), value);
166}
167
168void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
169{
170 putDirect(propertyName, value, attributes);
171}
172
173void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
174{
175 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
176}
177
178bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
179{
180 PropertySlot slot;
181 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
182}
183
184bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
185{
186 PropertySlot slot;
187 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
188}
189
190// ECMA 8.6.2.5
191bool JSObject::deleteProperty(ExecState* exec, const Identifier &propertyName)
192{
193 unsigned attributes;
194 JSValue *v = _prop.get(propertyName, attributes);
195 if (v) {
196 if ((attributes & DontDelete))
197 return false;
198 _prop.remove(propertyName);
199 if (attributes & IsGetterSetter)
200 _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
201 return true;
202 }
203
204 // Look in the static hashtable of properties
205 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
206 if (entry && entry->attributes & DontDelete)
207 return false; // this builtin property can't be deleted
208 // FIXME: Should the code here actually do some deletion?
209 return true;
210}
211
212bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
213{
214 PropertySlot slot;
215 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
216}
217
218bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
219{
220 return deleteProperty(exec, Identifier::from(exec, propertyName));
221}
222
223static ALWAYS_INLINE JSValue* callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
224{
225 JSValue* function = object->get(exec, propertyName);
226 CallData callData;
227 CallType callType = function->getCallData(callData);
228 if (callType == CallTypeNone)
229 return exec->exception();
230
231 // Prevent "toString" and "valueOf" from observing execution if an exception
232 // is pending.
233 if (exec->hadException())
234 return exec->exception();
235
236 JSValue* result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
237 ASSERT(result->type() != GetterSetterType);
238 if (exec->hadException())
239 return exec->exception();
240 if (result->isObject())
241 return 0;
242 return result;
243}
244
245bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
246{
247 result = defaultValue(exec, NumberType);
248 number = result->toNumber(exec);
249 return !result->isString();
250}
251
252// ECMA 8.6.2.6
253JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
254{
255 // Must call toString first for Date objects.
256 if ((hint == StringType) || (hint != NumberType && _proto == exec->lexicalGlobalObject()->datePrototype())) {
257 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
258 return value;
259 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
260 return value;
261 } else {
262 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
263 return value;
264 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
265 return value;
266 }
267
268 ASSERT(!exec->hadException());
269
270 return throwError(exec, TypeError, "No default value");
271}
272
273const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
274{
275 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
276 if (const HashTable* propHashTable = info->propHashTable(exec)) {
277 if (const HashEntry* e = propHashTable->entry(exec, propertyName))
278 return e;
279 }
280 }
281 return 0;
282}
283
284void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc)
285{
286 JSValue *o = getDirect(propertyName);
287 GetterSetter *gs;
288
289 if (o && o->type() == GetterSetterType) {
290 gs = static_cast<GetterSetter *>(o);
291 } else {
292 gs = new (exec) GetterSetter;
293 putDirect(propertyName, gs, IsGetterSetter);
294 }
295
296 _prop.setHasGetterSetterProperties(true);
297 gs->setGetter(getterFunc);
298}
299
300void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc)
301{
302 JSValue *o = getDirect(propertyName);
303 GetterSetter *gs;
304
305 if (o && o->type() == GetterSetterType) {
306 gs = static_cast<GetterSetter *>(o);
307 } else {
308 gs = new (exec) GetterSetter;
309 putDirect(propertyName, gs, IsGetterSetter);
310 }
311
312 _prop.setHasGetterSetterProperties(true);
313 gs->setSetter(setterFunc);
314}
315
316JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
317{
318 JSObject* obj = this;
319 while (true) {
320 JSValue* v = obj->getDirect(propertyName);
321 if (v) {
322 if (v->type() != GetterSetterType)
323 return jsUndefined();
324 JSObject* funcObj = static_cast<GetterSetter*>(v)->getter();
325 if (!funcObj)
326 return jsUndefined();
327 return funcObj;
328 }
329
330 if (!obj->prototype() || !obj->prototype()->isObject())
331 return jsUndefined();
332 obj = static_cast<JSObject*>(obj->prototype());
333 }
334}
335
336JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
337{
338 JSObject* obj = this;
339 while (true) {
340 JSValue* v = obj->getDirect(propertyName);
341 if (v) {
342 if (v->type() != GetterSetterType)
343 return jsUndefined();
344 JSObject* funcObj = static_cast<GetterSetter*>(v)->setter();
345 if (!funcObj)
346 return jsUndefined();
347 return funcObj;
348 }
349
350 if (!obj->prototype() || !obj->prototype()->isObject())
351 return jsUndefined();
352 obj = static_cast<JSObject*>(obj->prototype());
353 }
354}
355
356bool JSObject::implementsHasInstance() const
357{
358 return false;
359}
360
361bool JSObject::hasInstance(ExecState* exec, JSValue* value)
362{
363 JSValue* proto = get(exec, exec->propertyNames().prototype);
364 if (!proto->isObject()) {
365 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
366 return false;
367 }
368
369 if (!value->isObject())
370 return false;
371
372 JSObject* o = static_cast<JSObject*>(value);
373 while ((o = o->prototype()->getObject())) {
374 if (o == proto)
375 return true;
376 }
377 return false;
378}
379
380bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
381{
382 unsigned attributes;
383
384 if (!getPropertyAttributes(exec, propertyName, attributes))
385 return false;
386 else
387 return !(attributes & DontEnum);
388}
389
390bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
391{
392 if (_prop.get(propertyName, attributes))
393 return true;
394
395 // Look in the static hashtable of properties
396 const HashEntry* e = findPropertyHashEntry(exec, propertyName);
397 if (e) {
398 attributes = e->attributes;
399 return true;
400 }
401
402 return false;
403}
404
405void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
406{
407 _prop.getEnumerablePropertyNames(propertyNames);
408
409 // Add properties from the static hashtables of properties
410 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
411 const HashTable* table = info->propHashTable(exec);
412 if (!table)
413 continue;
414 table->initializeIfNeeded(exec);
415 ASSERT(table->table);
416 int hashSizeMask = table->hashSizeMask;
417 const HashEntry* e = table->table;
418 for (int i = 0; i <= hashSizeMask; ++i, ++e) {
419 if (e->key && !(e->attributes & DontEnum))
420 propertyNames.add(e->key);
421 }
422 }
423
424 if (_proto->isObject())
425 static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
426}
427
428bool JSObject::toBoolean(ExecState*) const
429{
430 return true;
431}
432
433double JSObject::toNumber(ExecState *exec) const
434{
435 JSValue *prim = toPrimitive(exec,NumberType);
436 if (exec->hadException()) // should be picked up soon in nodes.cpp
437 return 0.0;
438 return prim->toNumber(exec);
439}
440
441UString JSObject::toString(ExecState* exec) const
442{
443 JSValue* primitive = toPrimitive(exec, StringType);
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 _prop.remove(propertyName);
467}
468
469void JSObject::putDirectFunction(InternalFunction* func, int attr)
470{
471 putDirect(func->functionName(), func, attr);
472}
473
474void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
475{
476 if (JSObject* getterFunc = static_cast<GetterSetter*>(*location)->getter())
477 slot.setGetterSlot(getterFunc);
478 else
479 slot.setUndefined();
480}
481
482// ------------------------------ Error ----------------------------------------
483
484JSObject* Error::create(ExecState* exec, ErrorType errtype, const UString& message,
485 int lineno, int sourceId, const UString& sourceURL)
486{
487 JSObject* cons;
488 const char* name;
489 switch (errtype) {
490 case EvalError:
491 cons = exec->lexicalGlobalObject()->evalErrorConstructor();
492 name = "Evaluation error";
493 break;
494 case RangeError:
495 cons = exec->lexicalGlobalObject()->rangeErrorConstructor();
496 name = "Range error";
497 break;
498 case ReferenceError:
499 cons = exec->lexicalGlobalObject()->referenceErrorConstructor();
500 name = "Reference error";
501 break;
502 case SyntaxError:
503 cons = exec->lexicalGlobalObject()->syntaxErrorConstructor();
504 name = "Syntax error";
505 break;
506 case TypeError:
507 cons = exec->lexicalGlobalObject()->typeErrorConstructor();
508 name = "Type error";
509 break;
510 case URIError:
511 cons = exec->lexicalGlobalObject()->URIErrorConstructor();
512 name = "URI error";
513 break;
514 default:
515 cons = exec->lexicalGlobalObject()->errorConstructor();
516 name = "Error";
517 break;
518 }
519
520 ArgList args;
521 if (message.isEmpty())
522 args.append(jsString(exec, name));
523 else
524 args.append(jsString(exec, message));
525 ConstructData constructData;
526 ConstructType constructType = cons->getConstructData(constructData);
527 JSObject* err = construct(exec, cons, constructType, constructData, args);
528
529 if (lineno != -1)
530 err->put(exec, Identifier(exec, "line"), jsNumber(exec, lineno));
531 if (sourceId != -1)
532 err->put(exec, Identifier(exec, "sourceId"), jsNumber(exec, sourceId));
533
534 if(!sourceURL.isNull())
535 err->put(exec, Identifier(exec, "sourceURL"), jsString(exec, sourceURL));
536
537 return err;
538}
539
540JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
541{
542 return create(exec, type, message, -1, -1, NULL);
543}
544
545JSObject *throwError(ExecState *exec, ErrorType type)
546{
547 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
548 exec->setException(error);
549 return error;
550}
551
552JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
553{
554 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
555 exec->setException(error);
556 return error;
557}
558
559JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
560{
561 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
562 exec->setException(error);
563 return error;
564}
565
566JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
567{
568 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
569 exec->setException(error);
570 return error;
571}
572
573JSObject* constructEmptyObject(ExecState* exec)
574{
575 return new (exec) JSObject(exec->lexicalGlobalObject()->objectPrototype());
576}
577
578} // namespace KJS
Note: See TracBrowser for help on using the repository browser.