1 // -*- c-basic-offset: 2 -*-
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
5 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
6 * Copyright (C) 2003 Apple Computer, Inc.
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.
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.
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., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
28 #include "interpreter.h"
30 #include "reference_list.h"
37 #include "collector.h"
38 #include "operations.h"
39 #include "error_object.h"
43 #define JAVASCRIPT_CALL_TRACING Yes
46 #ifdef JAVASCRIPT_CALL_TRACING
47 static bool _traceJavaScript = false;
50 void setTraceJavaScript(bool f)
55 static bool traceJavaScript()
57 return _traceJavaScript;
64 // ------------------------------ Object ---------------------------------------
66 Object Object::dynamicCast(const Value &v)
68 if (v.isNull() || v.type() != ObjectType)
71 return Object(static_cast<ObjectImp*>(v.imp()));
75 Value Object::call(ExecState *exec, Object &thisObj, const List &args)
78 static int depth = 0; // sum of all concurrent interpreters
80 #ifdef JAVASCRIPT_CALL_TRACING
81 static bool tracing = false;
82 if (traceJavaScript() && !tracing) {
84 for (int i = 0; i < depth; i++)
86 printf ("*** calling: %s\n", toString(exec).ascii());
87 for (int j = 0; j < args.size(); j++) {
88 for (int i = 0; i < depth; i++)
90 printf ("*** arg[%d] = %s\n", j, args[j].toString(exec).ascii());
96 if (++depth > KJS_MAX_STACK) {
98 Object err = Error::create(exec, RangeError,
99 "Maximum call stack size exceeded.");
100 exec->setException(err);
105 Value ret = imp()->call(exec,thisObj,args);
107 #if KJS_MAX_STACK > 0
111 #ifdef JAVASCRIPT_CALL_TRACING
112 if (traceJavaScript() && !tracing) {
114 for (int i = 0; i < depth; i++)
116 printf ("*** returning: %s\n", ret.toString(exec).ascii());
124 // ------------------------------ ObjectImp ------------------------------------
126 ObjectImp::ObjectImp(const Object &proto)
127 : _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
129 //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
132 ObjectImp::ObjectImp(ObjectImp *proto)
133 : _proto(proto), _internalValue(0L)
135 //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
138 ObjectImp::ObjectImp()
140 //fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
141 _proto = NullImp::staticNull;
145 ObjectImp::~ObjectImp()
147 //fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
150 void ObjectImp::mark()
152 //fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
155 if (_proto && !_proto->marked())
160 if (_internalValue && !_internalValue->marked())
161 _internalValue->mark();
166 const ClassInfo *ObjectImp::classInfo() const
171 bool ObjectImp::inherits(const ClassInfo *info) const
176 const ClassInfo *ci = classInfo();
180 while (ci && ci != info)
181 ci = ci->parentClass;
186 Type ObjectImp::type() const
191 Value ObjectImp::prototype() const
193 return Value(_proto);
196 void ObjectImp::setPrototype(const Value &proto)
198 _proto = proto.imp();
201 UString ObjectImp::className() const
203 const ClassInfo *ci = classInfo();
205 return ci->className;
209 Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
211 ValueImp *imp = getDirect(propertyName);
215 // non-standard netscape extension
216 if (propertyName == specialPrototypePropertyName)
217 return Value(_proto);
219 if (_proto->dispatchType() != ObjectType) {
223 return static_cast<ObjectImp *>(_proto)->get(exec, propertyName);
226 Value ObjectImp::get(ExecState *exec, unsigned propertyName) const
228 return get(exec, Identifier::from(propertyName));
232 void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
233 const Value &value, int attr)
235 assert(!value.isNull());
237 // non-standard netscape extension
238 if (propertyName == specialPrototypePropertyName) {
243 /* TODO: check for write permissions directly w/o this call */
244 /* Doesn't look very easy with the PropertyMap API - David */
245 // putValue() is used for JS assignemnts. It passes no attribute.
246 // Assume that a C++ implementation knows what it is doing
247 // and let it override the canPut() check.
248 if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
250 fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
255 _prop.put(propertyName,value.imp(),attr);
258 void ObjectImp::put(ExecState *exec, unsigned propertyName,
259 const Value &value, int attr)
261 put(exec, Identifier::from(propertyName), value, attr);
265 bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
268 ValueImp *v = _prop.get(propertyName, attributes);
270 return!(attributes & ReadOnly);
272 // Look in the static hashtable of properties
273 const HashEntry* e = findPropertyHashEntry(propertyName);
275 return !(e->attr & ReadOnly);
277 // Don't look in the prototype here. We can always put an override
278 // in the object, even if the prototype has a ReadOnly property.
283 bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
285 if (_prop.get(propertyName))
288 // Look in the static hashtable of properties
289 if (findPropertyHashEntry(propertyName))
292 // non-standard netscape extension
293 if (propertyName == specialPrototypePropertyName)
296 if (_proto->dispatchType() != ObjectType) {
300 // Look in the prototype
301 return static_cast<ObjectImp *>(_proto)->hasProperty(exec, propertyName);
304 bool ObjectImp::hasProperty(ExecState *exec, unsigned propertyName) const
306 return hasProperty(exec, Identifier::from(propertyName));
310 bool ObjectImp::deleteProperty(ExecState */*exec*/, const Identifier &propertyName)
313 ValueImp *v = _prop.get(propertyName, attributes);
315 if ((attributes & DontDelete))
317 _prop.remove(propertyName);
321 // Look in the static hashtable of properties
322 const HashEntry* entry = findPropertyHashEntry(propertyName);
323 if (entry && entry->attr & DontDelete)
324 return false; // this builtin property can't be deleted
328 bool ObjectImp::deleteProperty(ExecState *exec, unsigned propertyName)
330 return deleteProperty(exec, Identifier::from(propertyName));
333 void ObjectImp::deleteAllProperties( ExecState * )
339 Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
341 if (hint != StringType && hint != NumberType) {
342 /* Prefer String for Date objects */
343 if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
350 if (hint == StringType)
351 v = get(exec,toStringPropertyName);
353 v = get(exec,valueOfPropertyName);
355 if (v.type() == ObjectType) {
356 Object o = Object(static_cast<ObjectImp*>(v.imp()));
357 if (o.implementsCall()) { // spec says "not primitive type" but ...
358 Object thisObj = Object(const_cast<ObjectImp*>(this));
359 Value def = o.call(exec,thisObj,List::empty());
360 Type defType = def.type();
361 if (defType == UnspecifiedType || defType == UndefinedType ||
362 defType == NullType || defType == BooleanType ||
363 defType == StringType || defType == NumberType) {
369 if (hint == StringType)
370 v = get(exec,valueOfPropertyName);
372 v = get(exec,toStringPropertyName);
374 if (v.type() == ObjectType) {
375 Object o = Object(static_cast<ObjectImp*>(v.imp()));
376 if (o.implementsCall()) { // spec says "not primitive type" but ...
377 Object thisObj = Object(const_cast<ObjectImp*>(this));
378 Value def = o.call(exec,thisObj,List::empty());
379 Type defType = def.type();
380 if (defType == UnspecifiedType || defType == UndefinedType ||
381 defType == NullType || defType == BooleanType ||
382 defType == StringType || defType == NumberType) {
388 Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
389 exec->setException(err);
393 const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
395 const ClassInfo *info = classInfo();
397 if (info->propHashTable) {
398 const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
402 info = info->parentClass;
407 bool ObjectImp::implementsConstruct() const
412 Object ObjectImp::construct(ExecState */*exec*/, const List &/*args*/)
418 Object ObjectImp::construct(ExecState *exec, const List &args, const UString &/*sourceURL*/, int /*lineNumber*/)
420 return construct(exec, args);
423 bool ObjectImp::implementsCall() const
428 Value ObjectImp::call(ExecState */*exec*/, Object &/*thisObj*/, const List &/*args*/)
434 bool ObjectImp::implementsHasInstance() const
439 Boolean ObjectImp::hasInstance(ExecState */*exec*/, const Value &/*value*/)
442 return Boolean(false);
445 ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
448 if (_proto && _proto->dispatchType() == ObjectType && recursive)
449 list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
451 _prop.addEnumerablesToReferenceList(list, Object(this));
453 // Add properties from the static hashtable of properties
454 const ClassInfo *info = classInfo();
456 if (info->propHashTable) {
457 int size = info->propHashTable->size;
458 const HashEntry *e = info->propHashTable->entries;
459 for (int i = 0; i < size; ++i, ++e) {
460 if ( e->s && !(e->attr & DontEnum) )
461 list.append(Reference(this, e->s)); /// ######### check for duplicates with the propertymap
464 info = info->parentClass;
470 Value ObjectImp::internalValue() const
472 return Value(_internalValue);
475 void ObjectImp::setInternalValue(const Value &v)
477 _internalValue = v.imp();
480 void ObjectImp::setInternalValue(ValueImp *v)
485 Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
487 return defaultValue(exec,preferredType);
490 bool ObjectImp::toBoolean(ExecState */*exec*/) const
495 double ObjectImp::toNumber(ExecState *exec) const
497 Value prim = toPrimitive(exec,NumberType);
498 if (exec->hadException()) // should be picked up soon in nodes.cpp
500 return prim.toNumber(exec);
503 UString ObjectImp::toString(ExecState *exec) const
505 Value prim = toPrimitive(exec,StringType);
506 if (exec->hadException()) // should be picked up soon in nodes.cpp
508 return prim.toString(exec);
511 Object ObjectImp::toObject(ExecState */*exec*/) const
513 return Object(const_cast<ObjectImp*>(this));
516 void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
518 _prop.put(propertyName, value, attr);
521 void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
523 _prop.put(propertyName, NumberImp::create(value), attr);
526 // ------------------------------ Error ----------------------------------------
528 const char * const errorNamesArr[] = {
529 I18N_NOOP("Error"), // GeneralError
530 I18N_NOOP("Evaluation error"), // EvalError
531 I18N_NOOP("Range error"), // RangeError
532 I18N_NOOP("Reference error"), // ReferenceError
533 I18N_NOOP("Syntax error"), // SyntaxError
534 I18N_NOOP("Type error"), // TypeError
535 I18N_NOOP("URI error"), // URIError
538 const char * const * const Error::errorNames = errorNamesArr;
540 Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
541 int lineno, int sourceId, const UString *sourceURL)
546 cons = exec->lexicalInterpreter()->builtinEvalError();
549 cons = exec->lexicalInterpreter()->builtinRangeError();
552 cons = exec->lexicalInterpreter()->builtinReferenceError();
555 cons = exec->lexicalInterpreter()->builtinSyntaxError();
558 cons = exec->lexicalInterpreter()->builtinTypeError();
561 cons = exec->lexicalInterpreter()->builtinURIError();
564 cons = exec->lexicalInterpreter()->builtinError();
569 message = errorNames[errtype];
571 args.append(String(message));
572 Object err = Object::dynamicCast(cons.construct(exec,args));
575 err.put(exec, "line", Number(lineno));
577 err.put(exec, "sourceId", Number(sourceId));
580 err.put(exec,"sourceURL", String(*sourceURL));
586 const char *msg = err.get("message").toString().value().ascii();
588 fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
590 fprintf(stderr, "KJS: %s. %s\n", estr, msg);
597 ObjectImp *error(ExecState *exec, ErrorType type, const char *message, int line, int sourceId, const UString *sourceURL)
599 return Error::create(exec, type, message, line, sourceId, sourceURL).imp();