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

Last change on this file since 34773 was 34773, checked in by Darin Adler, 17 years ago

2008-06-24 Darin Adler <Darin Adler>

Reviewed by Cameron.

  • kjs/JSObject.cpp: (KJS::JSObject::put): Remove an untested optimization I checked in by accident. The two loops up the prototype chain both need to start from this; instead the second loop was starting where the first loop left off.
  • 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 "date_object.h"
29#include "error_object.h"
30#include "nodes.h"
31#include "operations.h"
32#include "PropertyNameArray.h"
33#include <math.h>
34#include <profiler/Profiler.h>
35#include <wtf/Assertions.h>
36
37#define JAVASCRIPT_MARK_TRACING 0
38
39namespace KJS {
40
41// ------------------------------ JSObject ------------------------------------
42
43void JSObject::mark()
44{
45 JSCell::mark();
46
47#if JAVASCRIPT_MARK_TRACING
48 static int markStackDepth = 0;
49 markStackDepth++;
50 for (int i = 0; i < markStackDepth; i++)
51 putchar('-');
52
53 printf("%s (%p)\n", className().UTF8String().c_str(), this);
54#endif
55
56 JSValue *proto = _proto;
57 if (!proto->marked())
58 proto->mark();
59
60 _prop.mark();
61
62#if JAVASCRIPT_MARK_TRACING
63 markStackDepth--;
64#endif
65}
66
67JSType JSObject::type() const
68{
69 return ObjectType;
70}
71
72UString JSObject::className() const
73{
74 const ClassInfo *ci = classInfo();
75 if ( ci )
76 return ci->className;
77 return "Object";
78}
79
80bool JSObject::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
81{
82 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
83}
84
85static void throwSetterError(ExecState *exec)
86{
87 throwError(exec, TypeError, "setting a property that has only a getter");
88}
89
90// ECMA 8.6.2.2
91void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value)
92{
93 ASSERT(value);
94 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
95
96 if (propertyName == exec->propertyNames().underscoreProto) {
97 JSObject* proto = value->getObject();
98
99 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
100 if (!proto && value != jsNull())
101 return;
102
103 while (proto) {
104 if (proto == this) {
105 throwError(exec, GeneralError, "cyclic __proto__ value");
106 return;
107 }
108 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
109 }
110
111 setPrototype(value);
112 return;
113 }
114
115 // Check if there are any setters or getters in the prototype chain
116 JSValue* prototype;
117 for (JSObject* obj = this; !obj->_prop.hasGetterSetterProperties(); obj = static_cast<JSObject*>(prototype)) {
118 prototype = obj->_proto;
119 if (prototype == jsNull()) {
120 _prop.put(propertyName, value, 0, true);
121 return;
122 }
123 }
124
125 unsigned attributes;
126 if (_prop.get(propertyName, attributes) && attributes & ReadOnly)
127 return;
128
129 for (JSObject* obj = this; ; obj = static_cast<JSObject*>(prototype)) {
130 if (JSValue* gs = obj->_prop.get(propertyName, attributes)) {
131 if (attributes & IsGetterSetter) {
132 JSObject* setterFunc = static_cast<GetterSetter*>(gs)->setter();
133 if (!setterFunc) {
134 throwSetterError(exec);
135 return;
136 }
137
138 CallData callData;
139 CallType callType = setterFunc->getCallData(callData);
140 ArgList args;
141 args.append(value);
142 call(exec, setterFunc, callType, callData, this, args);
143 return;
144 }
145
146 // If there's an existing property on the object or one of its
147 // prototypes it should be replaced, so break here.
148 break;
149 }
150
151 prototype = obj->_proto;
152 if (prototype == jsNull())
153 break;
154 }
155
156 _prop.put(propertyName, value, 0, true);
157}
158
159void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
160{
161 put(exec, Identifier::from(exec, propertyName), value);
162}
163
164void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
165{
166 putDirect(propertyName, value, attributes);
167}
168
169void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
170{
171 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
172}
173
174bool JSObject::hasProperty(ExecState *exec, const Identifier &propertyName) const
175{
176 PropertySlot slot;
177 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
178}
179
180bool JSObject::hasProperty(ExecState *exec, unsigned propertyName) const
181{
182 PropertySlot slot;
183 return const_cast<JSObject *>(this)->getPropertySlot(exec, propertyName, slot);
184}
185
186// ECMA 8.6.2.5
187bool JSObject::deleteProperty(ExecState* exec, const Identifier &propertyName)
188{
189 unsigned attributes;
190 JSValue *v = _prop.get(propertyName, attributes);
191 if (v) {
192 if ((attributes & DontDelete))
193 return false;
194 _prop.remove(propertyName);
195 if (attributes & IsGetterSetter)
196 _prop.setHasGetterSetterProperties(_prop.containsGettersOrSetters());
197 return true;
198 }
199
200 // Look in the static hashtable of properties
201 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
202 if (entry && entry->attributes & DontDelete)
203 return false; // this builtin property can't be deleted
204 // FIXME: Should the code here actually do some deletion?
205 return true;
206}
207
208bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
209{
210 PropertySlot slot;
211 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
212}
213
214bool JSObject::deleteProperty(ExecState *exec, unsigned propertyName)
215{
216 return deleteProperty(exec, Identifier::from(exec, propertyName));
217}
218
219static ALWAYS_INLINE JSValue* callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
220{
221 JSValue* function = object->get(exec, propertyName);
222 CallData callData;
223 CallType callType = function->getCallData(callData);
224 if (callType == CallTypeNone)
225 return 0;
226 JSValue* result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
227 ASSERT(result->type() != GetterSetterType);
228 if (exec->hadException())
229 return exec->exception();
230 if (result->isObject())
231 return 0;
232 return result;
233}
234
235bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
236{
237 result = defaultValue(exec, NumberType);
238 number = result->toNumber(exec);
239 return !result->isString();
240}
241
242// ECMA 8.6.2.6
243JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
244{
245 // We need this check to guard against the case where this object is rhs of
246 // a binary expression where lhs threw an exception in its conversion to
247 // primitive.
248 if (exec->hadException())
249 return exec->exception();
250
251 // Must call toString first for Date objects.
252 if ((hint == StringType) || (hint != NumberType && _proto == exec->lexicalGlobalObject()->datePrototype())) {
253 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
254 return value;
255 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
256 return value;
257 } else {
258 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
259 return value;
260 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
261 return value;
262 }
263
264 ASSERT(!exec->hadException());
265
266 return throwError(exec, TypeError, "No default value");
267}
268
269const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
270{
271 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
272 if (const HashTable* propHashTable = info->propHashTable(exec)) {
273 if (const HashEntry* e = propHashTable->entry(exec, propertyName))
274 return e;
275 }
276 }
277 return 0;
278}
279
280void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunc)
281{
282 JSValue *o = getDirect(propertyName);
283 GetterSetter *gs;
284
285 if (o && o->type() == GetterSetterType) {
286 gs = static_cast<GetterSetter *>(o);
287 } else {
288 gs = new (exec) GetterSetter;
289 putDirect(propertyName, gs, IsGetterSetter);
290 }
291
292 _prop.setHasGetterSetterProperties(true);
293 gs->setGetter(getterFunc);
294}
295
296void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunc)
297{
298 JSValue *o = getDirect(propertyName);
299 GetterSetter *gs;
300
301 if (o && o->type() == GetterSetterType) {
302 gs = static_cast<GetterSetter *>(o);
303 } else {
304 gs = new (exec) GetterSetter;
305 putDirect(propertyName, gs, IsGetterSetter);
306 }
307
308 _prop.setHasGetterSetterProperties(true);
309 gs->setSetter(setterFunc);
310}
311
312JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
313{
314 JSObject* obj = this;
315 while (true) {
316 JSValue* v = obj->getDirect(propertyName);
317 if (v) {
318 if (v->type() != GetterSetterType)
319 return jsUndefined();
320 JSObject* funcObj = static_cast<GetterSetter*>(v)->getter();
321 if (!funcObj)
322 return jsUndefined();
323 return funcObj;
324 }
325
326 if (!obj->prototype() || !obj->prototype()->isObject())
327 return jsUndefined();
328 obj = static_cast<JSObject*>(obj->prototype());
329 }
330}
331
332JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
333{
334 JSObject* obj = this;
335 while (true) {
336 JSValue* v = obj->getDirect(propertyName);
337 if (v) {
338 if (v->type() != GetterSetterType)
339 return jsUndefined();
340 JSObject* funcObj = static_cast<GetterSetter*>(v)->setter();
341 if (!funcObj)
342 return jsUndefined();
343 return funcObj;
344 }
345
346 if (!obj->prototype() || !obj->prototype()->isObject())
347 return jsUndefined();
348 obj = static_cast<JSObject*>(obj->prototype());
349 }
350}
351
352bool JSObject::implementsHasInstance() const
353{
354 return false;
355}
356
357bool JSObject::hasInstance(ExecState* exec, JSValue* value)
358{
359 JSValue* proto = get(exec, exec->propertyNames().prototype);
360 if (!proto->isObject()) {
361 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
362 return false;
363 }
364
365 if (!value->isObject())
366 return false;
367
368 JSObject* o = static_cast<JSObject*>(value);
369 while ((o = o->prototype()->getObject())) {
370 if (o == proto)
371 return true;
372 }
373 return false;
374}
375
376bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
377{
378 unsigned attributes;
379
380 if (!getPropertyAttributes(exec, propertyName, attributes))
381 return false;
382 else
383 return !(attributes & DontEnum);
384}
385
386bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
387{
388 if (_prop.get(propertyName, attributes))
389 return true;
390
391 // Look in the static hashtable of properties
392 const HashEntry* e = findPropertyHashEntry(exec, propertyName);
393 if (e) {
394 attributes = e->attributes;
395 return true;
396 }
397
398 return false;
399}
400
401void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
402{
403 _prop.getEnumerablePropertyNames(propertyNames);
404
405 // Add properties from the static hashtables of properties
406 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
407 const HashTable* table = info->propHashTable(exec);
408 if (!table)
409 continue;
410 table->initializeIfNeeded(exec);
411 ASSERT(table->table);
412 int hashSizeMask = table->hashSizeMask;
413 const HashEntry* e = table->table;
414 for (int i = 0; i <= hashSizeMask; ++i, ++e) {
415 if (e->key && !(e->attributes & DontEnum))
416 propertyNames.add(e->key);
417 }
418 }
419
420 if (_proto->isObject())
421 static_cast<JSObject*>(_proto)->getPropertyNames(exec, propertyNames);
422}
423
424bool JSObject::toBoolean(ExecState*) const
425{
426 return true;
427}
428
429double JSObject::toNumber(ExecState *exec) const
430{
431 JSValue *prim = toPrimitive(exec,NumberType);
432 if (exec->hadException()) // should be picked up soon in nodes.cpp
433 return 0.0;
434 return prim->toNumber(exec);
435}
436
437UString JSObject::toString(ExecState *exec) const
438{
439 JSValue *prim = toPrimitive(exec,StringType);
440 if (exec->hadException()) // should be picked up soon in nodes.cpp
441 return "";
442 return prim->toString(exec);
443}
444
445JSObject *JSObject::toObject(ExecState*) const
446{
447 return const_cast<JSObject*>(this);
448}
449
450JSObject* JSObject::toThisObject(ExecState*) const
451{
452 return const_cast<JSObject*>(this);
453}
454
455JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
456{
457 return 0;
458}
459
460void JSObject::removeDirect(const Identifier &propertyName)
461{
462 _prop.remove(propertyName);
463}
464
465void JSObject::putDirectFunction(InternalFunction* func, int attr)
466{
467 putDirect(func->functionName(), func, attr);
468}
469
470void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
471{
472 if (JSObject* getterFunc = static_cast<GetterSetter*>(*location)->getter())
473 slot.setGetterSlot(getterFunc);
474 else
475 slot.setUndefined();
476}
477
478// ------------------------------ Error ----------------------------------------
479
480JSObject* Error::create(ExecState* exec, ErrorType errtype, const UString& message,
481 int lineno, int sourceId, const UString& sourceURL)
482{
483 JSObject* cons;
484 const char* name;
485 switch (errtype) {
486 case EvalError:
487 cons = exec->lexicalGlobalObject()->evalErrorConstructor();
488 name = "Evaluation error";
489 break;
490 case RangeError:
491 cons = exec->lexicalGlobalObject()->rangeErrorConstructor();
492 name = "Range error";
493 break;
494 case ReferenceError:
495 cons = exec->lexicalGlobalObject()->referenceErrorConstructor();
496 name = "Reference error";
497 break;
498 case SyntaxError:
499 cons = exec->lexicalGlobalObject()->syntaxErrorConstructor();
500 name = "Syntax error";
501 break;
502 case TypeError:
503 cons = exec->lexicalGlobalObject()->typeErrorConstructor();
504 name = "Type error";
505 break;
506 case URIError:
507 cons = exec->lexicalGlobalObject()->URIErrorConstructor();
508 name = "URI error";
509 break;
510 default:
511 cons = exec->lexicalGlobalObject()->errorConstructor();
512 name = "Error";
513 break;
514 }
515
516 ArgList args;
517 if (message.isEmpty())
518 args.append(jsString(exec, name));
519 else
520 args.append(jsString(exec, message));
521 ConstructData constructData;
522 ConstructType constructType = cons->getConstructData(constructData);
523 JSObject* err = construct(exec, cons, constructType, constructData, args);
524
525 if (lineno != -1)
526 err->put(exec, Identifier(exec, "line"), jsNumber(exec, lineno));
527 if (sourceId != -1)
528 err->put(exec, Identifier(exec, "sourceId"), jsNumber(exec, sourceId));
529
530 if(!sourceURL.isNull())
531 err->put(exec, Identifier(exec, "sourceURL"), jsString(exec, sourceURL));
532
533 return err;
534}
535
536JSObject *Error::create(ExecState *exec, ErrorType type, const char *message)
537{
538 return create(exec, type, message, -1, -1, NULL);
539}
540
541JSObject *throwError(ExecState *exec, ErrorType type)
542{
543 JSObject *error = Error::create(exec, type, UString(), -1, -1, NULL);
544 exec->setException(error);
545 return error;
546}
547
548JSObject *throwError(ExecState *exec, ErrorType type, const UString &message)
549{
550 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
551 exec->setException(error);
552 return error;
553}
554
555JSObject *throwError(ExecState *exec, ErrorType type, const char *message)
556{
557 JSObject *error = Error::create(exec, type, message, -1, -1, NULL);
558 exec->setException(error);
559 return error;
560}
561
562JSObject *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString &sourceURL)
563{
564 JSObject *error = Error::create(exec, type, message, line, sourceId, sourceURL);
565 exec->setException(error);
566 return error;
567}
568
569JSObject* constructEmptyObject(ExecState* exec)
570{
571 return new (exec) JSObject(exec->lexicalGlobalObject()->objectPrototype());
572}
573
574} // namespace KJS
Note: See TracBrowser for help on using the repository browser.