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

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

2008-06-29 Sam Weinig <[email protected]>

Fix non-AllInOne builds.

  • kjs/JSObject.cpp:
  • kjs/JSValue.cpp:
  • Property svn:eol-style set to native
File size: 16.6 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 0;
230 JSValue* result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
231 ASSERT(result->type() != GetterSetterType);
232 if (exec->hadException())
233 return exec->exception();
234 if (result->isObject())
235 return 0;
236 return result;
237}
238
239bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
240{
241 result = defaultValue(exec, NumberType);
242 number = result->toNumber(exec);
243 return !result->isString();
244}
245
246// ECMA 8.6.2.6
247JSValue* JSObject::defaultValue(ExecState* exec, JSType hint) const
248{
249 // We need this check to guard against the case where this object is rhs of
250 // a binary expression where lhs threw an exception in its conversion to
251 // primitive.
252 if (exec->hadException())
253 return exec->exception();
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.