source: webkit/trunk/JavaScriptCore/kjs/object.cpp@ 10857

Last change on this file since 10857 was 10857, checked in by mjs, 20 years ago

Reverted fix for this bug, because it was part of a time range that caused a performance
regression:

<rdar://problem/4260481> Remove Reference type from JavaScriptCore

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1// -*- c-basic-offset: 2 -*-
2/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten ([email protected])
5 * Copyright (C) 2001 Peter Kelly ([email protected])
6 * Copyright (C) 2003 Apple Computer, Inc.
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 Steet, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "value.h"
27#include "object.h"
28#include "types.h"
29#include "interpreter.h"
30#include "lookup.h"
31#include "reference_list.h"
32
33#include <assert.h>
34#include <math.h>
35#include <stdio.h>
36
37#include "internal.h"
38#include "collector.h"
39#include "operations.h"
40#include "error_object.h"
41#include "nodes.h"
42
43#ifndef NDEBUG
44//#define JAVASCRIPT_CALL_TRACING 1
45#endif
46
47#if JAVASCRIPT_CALL_TRACING
48static bool _traceJavaScript = false;
49
50extern "C" {
51 void setTraceJavaScript(bool f)
52 {
53 _traceJavaScript = f;
54 }
55
56 static bool traceJavaScript()
57 {
58 return _traceJavaScript;
59 }
60}
61#endif
62
63namespace KJS {
64
65// ------------------------------ Object ---------------------------------------
66
67ValueImp *ObjectImp::call(ExecState *exec, ObjectImp *thisObj, const List &args)
68{
69 assert(implementsCall());
70
71#if KJS_MAX_STACK > 0
72 static int depth = 0; // sum of all concurrent interpreters
73
74#if JAVASCRIPT_CALL_TRACING
75 static bool tracing = false;
76 if (traceJavaScript() && !tracing) {
77 tracing = true;
78 for (int i = 0; i < depth; i++)
79 putchar (' ');
80 printf ("*** calling: %s\n", toString(exec).ascii());
81 for (int j = 0; j < args.size(); j++) {
82 for (int i = 0; i < depth; i++)
83 putchar (' ');
84 printf ("*** arg[%d] = %s\n", j, args[j]->toString(exec).ascii());
85 }
86 tracing = false;
87 }
88#endif
89
90 if (++depth > KJS_MAX_STACK) {
91 --depth;
92 return throwError(exec, RangeError, "Maximum call stack size exceeded.");
93 }
94#endif
95
96 ValueImp *ret = callAsFunction(exec,thisObj,args);
97
98#if KJS_MAX_STACK > 0
99 --depth;
100#endif
101
102#if JAVASCRIPT_CALL_TRACING
103 if (traceJavaScript() && !tracing) {
104 tracing = true;
105 for (int i = 0; i < depth; i++)
106 putchar (' ');
107 printf ("*** returning: %s\n", ret->toString(exec).ascii());
108 tracing = false;
109 }
110#endif
111
112 return ret;
113}
114
115// ------------------------------ ObjectImp ------------------------------------
116
117void ObjectImp::mark()
118{
119 AllocatedValueImp::mark();
120
121 ValueImp *proto = _proto;
122 if (!proto->marked())
123 proto->mark();
124
125 _prop.mark();
126
127 if (_internalValue && !_internalValue->marked())
128 _internalValue->mark();
129
130 _scope.mark();
131}
132
133Type ObjectImp::type() const
134{
135 return ObjectType;
136}
137
138const ClassInfo *ObjectImp::classInfo() const
139{
140 return 0;
141}
142
143UString ObjectImp::className() const
144{
145 const ClassInfo *ci = classInfo();
146 if ( ci )
147 return ci->className;
148 return "Object";
149}
150
151ValueImp *ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
152{
153 PropertySlot slot;
154
155 if (const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot))
156 return slot.getValue(exec, propertyName);
157
158 return Undefined();
159}
160
161ValueImp *ObjectImp::get(ExecState *exec, unsigned propertyName) const
162{
163 PropertySlot slot;
164 if (const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot))
165 return slot.getValue(exec, propertyName);
166
167 return Undefined();
168}
169
170bool ObjectImp::getPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
171{
172 ObjectImp *imp = this;
173
174 while (true) {
175 if (imp->getOwnPropertySlot(exec, propertyName, slot))
176 return true;
177
178 ValueImp *proto = imp->_proto;
179 if (!proto->isObject())
180 break;
181
182 imp = static_cast<ObjectImp *>(proto);
183 }
184
185 return false;
186}
187
188bool ObjectImp::getOwnPropertySlot(ExecState *exec, unsigned propertyName, PropertySlot& slot)
189{
190 return getOwnPropertySlot(exec, Identifier::from(propertyName), slot);
191}
192
193// ECMA 8.6.2.2
194void ObjectImp::put(ExecState *exec, const Identifier &propertyName, ValueImp *value, int attr)
195{
196 assert(value);
197
198 // non-standard netscape extension
199 if (propertyName == exec->dynamicInterpreter()->specialPrototypeIdentifier()) {
200 setPrototype(value);
201 return;
202 }
203
204 /* TODO: check for write permissions directly w/o this call */
205 /* Doesn't look very easy with the PropertyMap API - David */
206 // putValue() is used for JS assignemnts. It passes no attribute.
207 // Assume that a C++ implementation knows what it is doing
208 // and let it override the canPut() check.
209 if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
210#ifdef KJS_VERBOSE
211 fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
212#endif
213 return;
214 }
215
216 _prop.put(propertyName,value,attr);
217}
218
219void ObjectImp::put(ExecState *exec, unsigned propertyName,
220 ValueImp *value, int attr)
221{
222 put(exec, Identifier::from(propertyName), value, attr);
223}
224
225// ECMA 8.6.2.3
226bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
227{
228 int attributes;
229 ValueImp *v = _prop.get(propertyName, attributes);
230 if (v)
231 return!(attributes & ReadOnly);
232
233 // Look in the static hashtable of properties
234 const HashEntry* e = findPropertyHashEntry(propertyName);
235 if (e)
236 return !(e->attr & ReadOnly);
237
238 // Don't look in the prototype here. We can always put an override
239 // in the object, even if the prototype has a ReadOnly property.
240 return true;
241}
242
243// ECMA 8.6.2.4
244bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
245{
246 PropertySlot slot;
247 return const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot);
248}
249
250bool ObjectImp::hasProperty(ExecState *exec, unsigned propertyName) const
251{
252 PropertySlot slot;
253 return const_cast<ObjectImp *>(this)->getPropertySlot(exec, propertyName, slot);
254}
255
256// ECMA 8.6.2.5
257bool ObjectImp::deleteProperty(ExecState */*exec*/, const Identifier &propertyName)
258{
259 int attributes;
260 ValueImp *v = _prop.get(propertyName, attributes);
261 if (v) {
262 if ((attributes & DontDelete))
263 return false;
264 _prop.remove(propertyName);
265 return true;
266 }
267
268 // Look in the static hashtable of properties
269 const HashEntry* entry = findPropertyHashEntry(propertyName);
270 if (entry && entry->attr & DontDelete)
271 return false; // this builtin property can't be deleted
272 return true;
273}
274
275bool ObjectImp::deleteProperty(ExecState *exec, unsigned propertyName)
276{
277 return deleteProperty(exec, Identifier::from(propertyName));
278}
279
280static inline
281#ifdef __GNUC__
282__attribute__((always_inline))
283#endif
284ValueImp *tryGetAndCallProperty(ExecState *exec, const ObjectImp *object, const Identifier &propertyName) {
285 ValueImp *v = object->get(exec, propertyName);
286 if (v->isObject()) {
287 ObjectImp *o = static_cast<ObjectImp*>(v);
288 if (o->implementsCall()) { // spec says "not primitive type" but ...
289 ObjectImp *thisObj = const_cast<ObjectImp*>(object);
290 ValueImp *def = o->call(exec, thisObj, List::empty());
291 Type defType = def->type();
292 if (defType == UnspecifiedType || defType == UndefinedType ||
293 defType == NullType || defType == BooleanType ||
294 defType == StringType || defType == NumberType) {
295 return def;
296 }
297 }
298 }
299 return NULL;
300}
301
302// ECMA 8.6.2.6
303ValueImp *ObjectImp::defaultValue(ExecState *exec, Type hint) const
304{
305 Identifier firstPropertyName;
306 Identifier secondPropertyName;
307 /* Prefer String for Date objects */
308 if ((hint == StringType) || (hint != StringType) && (hint != NumberType) && (_proto == exec->lexicalInterpreter()->builtinDatePrototype())) {
309 firstPropertyName = toStringPropertyName;
310 secondPropertyName = valueOfPropertyName;
311 } else {
312 firstPropertyName = valueOfPropertyName;
313 secondPropertyName = toStringPropertyName;
314 }
315
316 ValueImp *v;
317 if ((v = tryGetAndCallProperty(exec, this, firstPropertyName)))
318 return v;
319 if ((v = tryGetAndCallProperty(exec, this, secondPropertyName)))
320 return v;
321
322 if (exec->hadException())
323 return exec->exception();
324
325 return throwError(exec, TypeError, "No default value");
326}
327
328const HashEntry* ObjectImp::findPropertyHashEntry(const Identifier& propertyName) const
329{
330 for (const ClassInfo *info = classInfo(); info; info = info->parentClass) {
331 if (const HashTable *propHashTable = info->propHashTable) {
332 if (const HashEntry *e = Lookup::findEntry(propHashTable, propertyName))
333 return e;
334 }
335 }
336 return 0;
337}
338
339bool ObjectImp::implementsConstruct() const
340{
341 return false;
342}
343
344ObjectImp *ObjectImp::construct(ExecState */*exec*/, const List &/*args*/)
345{
346 assert(false);
347 return NULL;
348}
349
350ObjectImp *ObjectImp::construct(ExecState *exec, const List &args, const UString &/*sourceURL*/, int /*lineNumber*/)
351{
352 return construct(exec, args);
353}
354
355bool ObjectImp::implementsCall() const
356{
357 return false;
358}
359
360ValueImp *ObjectImp::callAsFunction(ExecState */*exec*/, ObjectImp */*thisObj*/, const List &/*args*/)
361{
362 assert(false);
363 return NULL;
364}
365
366bool ObjectImp::implementsHasInstance() const
367{
368 return false;
369}
370
371bool ObjectImp::hasInstance(ExecState */*exec*/, ValueImp */*value*/)
372{
373 assert(false);
374 return false;
375}
376
377ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
378{
379 ReferenceList list;
380 if (_proto->isObject() && recursive)
381 list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
382
383 _prop.addEnumerablesToReferenceList(list, this);
384
385 // Add properties from the static hashtable of properties
386 const ClassInfo *info = classInfo();
387 while (info) {
388 if (info->propHashTable) {
389 int size = info->propHashTable->size;
390 const HashEntry *e = info->propHashTable->entries;
391 for (int i = 0; i < size; ++i, ++e) {
392 if ( e->s && !(e->attr & DontEnum) )
393 list.append(Reference(this, e->s)); /// ######### check for duplicates with the propertymap
394 }
395 }
396 info = info->parentClass;
397 }
398
399 return list;
400}
401
402ValueImp *ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
403{
404 return defaultValue(exec,preferredType);
405}
406
407bool ObjectImp::toBoolean(ExecState */*exec*/) const
408{
409 return true;
410}
411
412double ObjectImp::toNumber(ExecState *exec) const
413{
414 ValueImp *prim = toPrimitive(exec,NumberType);
415 if (exec->hadException()) // should be picked up soon in nodes.cpp
416 return 0.0;
417 return prim->toNumber(exec);
418}
419
420UString ObjectImp::toString(ExecState *exec) const
421{
422 ValueImp *prim = toPrimitive(exec,StringType);
423 if (exec->hadException()) // should be picked up soon in nodes.cpp
424 return "";
425 return prim->toString(exec);
426}
427
428ObjectImp *ObjectImp::toObject(ExecState */*exec*/) const
429{
430 return const_cast<ObjectImp*>(this);
431}
432
433void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
434{
435 _prop.put(propertyName, value, attr);
436}
437
438void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
439{
440 _prop.put(propertyName, jsNumber(value), attr);
441}
442
443// ------------------------------ Error ----------------------------------------
444
445const char * const errorNamesArr[] = {
446 I18N_NOOP("Error"), // GeneralError
447 I18N_NOOP("Evaluation error"), // EvalError
448 I18N_NOOP("Range error"), // RangeError
449 I18N_NOOP("Reference error"), // ReferenceError
450 I18N_NOOP("Syntax error"), // SyntaxError
451 I18N_NOOP("Type error"), // TypeError
452 I18N_NOOP("URI error"), // URIError
453};
454
455const char * const * const Error::errorNames = errorNamesArr;
456
457ObjectImp *Error::create(ExecState *exec, ErrorType errtype, const UString &message,
458 int lineno, int sourceId, const UString *sourceURL)
459{
460 ObjectImp *cons;
461 switch (errtype) {
462 case EvalError:
463 cons = exec->lexicalInterpreter()->builtinEvalError();
464 break;
465 case RangeError:
466 cons = exec->lexicalInterpreter()->builtinRangeError();
467 break;
468 case ReferenceError:
469 cons = exec->lexicalInterpreter()->builtinReferenceError();
470 break;
471 case SyntaxError:
472 cons = exec->lexicalInterpreter()->builtinSyntaxError();
473 break;
474 case TypeError:
475 cons = exec->lexicalInterpreter()->builtinTypeError();
476 break;
477 case URIError:
478 cons = exec->lexicalInterpreter()->builtinURIError();
479 break;
480 default:
481 cons = exec->lexicalInterpreter()->builtinError();
482 break;
483 }
484
485 List args;
486 if (message.isEmpty())
487 args.append(jsString(errorNames[errtype]));
488 else
489 args.append(jsString(message));
490 ObjectImp *err = static_cast<ObjectImp *>(cons->construct(exec,args));
491
492 if (lineno != -1)
493 err->put(exec, "line", Number(lineno));
494 if (sourceId != -1)
495 err->put(exec, "sourceId", Number(sourceId));
496
497 if(sourceURL)
498 err->put(exec,"sourceURL", String(*sourceURL));
499
500 return err;
501
502/*
503#ifndef NDEBUG
504 const char *msg = err->get("message")->toString().value().ascii();
505 if (l >= 0)
506 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
507 else
508 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
509#endif
510
511 return err;
512*/
513}
514
515ObjectImp *Error::create(ExecState *exec, ErrorType type, const char *message)
516{
517 return create(exec, type, message, -1, -1, NULL);
518}
519
520ObjectImp *throwError(ExecState *exec, ErrorType type)
521{
522 ObjectImp *error = Error::create(exec, type, UString(), -1, -1, NULL);
523 exec->setException(error);
524 return error;
525}
526
527ObjectImp *throwError(ExecState *exec, ErrorType type, const UString &message)
528{
529 ObjectImp *error = Error::create(exec, type, message, -1, -1, NULL);
530 exec->setException(error);
531 return error;
532}
533
534ObjectImp *throwError(ExecState *exec, ErrorType type, const char *message)
535{
536 ObjectImp *error = Error::create(exec, type, message, -1, -1, NULL);
537 exec->setException(error);
538 return error;
539}
540
541ObjectImp *throwError(ExecState *exec, ErrorType type, const UString &message, int line, int sourceId, const UString *sourceURL)
542{
543 ObjectImp *error = Error::create(exec, type, message, line, sourceId, sourceURL);
544 exec->setException(error);
545 return error;
546}
547
548} // namespace KJS
Note: See TracBrowser for help on using the repository browser.