You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
564 lines
14 KiB
564 lines
14 KiB
// -*- c-basic-offset: 2 -*-
|
|
/*
|
|
* This file is part of the KDE libraries
|
|
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
|
|
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
|
|
* Copyright (C) 2003 Apple Computer, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "value.h"
|
|
#include "object.h"
|
|
#include "types.h"
|
|
#include "interpreter.h"
|
|
#include "lookup.h"
|
|
#include "reference_list.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
#include "internal.h"
|
|
#include "collector.h"
|
|
#include "operations.h"
|
|
#include "error_object.h"
|
|
#include "nodes.h"
|
|
|
|
using namespace KJS;
|
|
|
|
// ------------------------------ Object ---------------------------------------
|
|
|
|
Object Object::dynamicCast(const Value &v)
|
|
{
|
|
if (!v.isValid() || v.type() != ObjectType)
|
|
return Object(0);
|
|
|
|
return Object(static_cast<ObjectImp*>(v.imp()));
|
|
}
|
|
|
|
Value Object::call(ExecState *exec, Object &thisObj, const List &args)
|
|
{
|
|
#if KJS_MAX_STACK > 0
|
|
static int depth = 0; // sum of all concurrent interpreters
|
|
if (++depth > KJS_MAX_STACK) {
|
|
#ifndef NDEBUG
|
|
fprintf(stderr, "Exceeded maximum function call depth\n");
|
|
#endif
|
|
int saveDepth = depth - 1;
|
|
Object err = Error::create(exec, RangeError,
|
|
"Exceeded maximum function call depth.");
|
|
depth = depth - 10; //Give some room for the debugger to operate,
|
|
//so if it tries to examine things we don't get here again
|
|
exec->setException(err);
|
|
depth = saveDepth;
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
Value ret = static_cast<ObjectImp*>(rep)->call(exec,thisObj,args);
|
|
|
|
#if KJS_MAX_STACK > 0
|
|
--depth;
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
// ------------------------------ ObjectImp ------------------------------------
|
|
|
|
ObjectImp::ObjectImp(const Object &proto)
|
|
: _proto(static_cast<ObjectImp*>(proto.imp())), _internalValue(0L)
|
|
{
|
|
//fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
|
|
}
|
|
|
|
ObjectImp::ObjectImp(ObjectImp *proto)
|
|
: _proto(proto), _internalValue(0L)
|
|
{
|
|
}
|
|
|
|
ObjectImp::ObjectImp()
|
|
{
|
|
//fprintf(stderr,"ObjectImp::ObjectImp %p\n",(void*)this);
|
|
_proto = NullImp::staticNull;
|
|
_internalValue = 0L;
|
|
}
|
|
|
|
ObjectImp::~ObjectImp()
|
|
{
|
|
//fprintf(stderr,"ObjectImp::~ObjectImp %p\n",(void*)this);
|
|
}
|
|
|
|
void ObjectImp::mark()
|
|
{
|
|
//fprintf(stderr,"ObjectImp::mark() %p\n",(void*)this);
|
|
ValueImp::mark();
|
|
|
|
if (_proto && !_proto->marked())
|
|
_proto->mark();
|
|
|
|
_prop.mark();
|
|
|
|
if (_internalValue && !_internalValue->marked())
|
|
_internalValue->mark();
|
|
|
|
_scope.mark();
|
|
}
|
|
|
|
const ClassInfo *ObjectImp::classInfo() const
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
bool ObjectImp::inherits(const ClassInfo *info) const
|
|
{
|
|
if (!info)
|
|
return false;
|
|
|
|
const ClassInfo *ci = classInfo();
|
|
if (!ci)
|
|
return false;
|
|
|
|
while (ci && ci != info)
|
|
ci = ci->parentClass;
|
|
|
|
return (ci == info);
|
|
}
|
|
|
|
Type ObjectImp::type() const
|
|
{
|
|
return ObjectType;
|
|
}
|
|
|
|
Value ObjectImp::prototype() const
|
|
{
|
|
return Value(_proto);
|
|
}
|
|
|
|
void ObjectImp::setPrototype(const Value &proto)
|
|
{
|
|
_proto = proto.imp();
|
|
}
|
|
|
|
UString ObjectImp::className() const
|
|
{
|
|
const ClassInfo *ci = classInfo();
|
|
if ( ci )
|
|
return ci->className;
|
|
return "Object";
|
|
}
|
|
|
|
Value ObjectImp::get(ExecState *exec, const Identifier &propertyName) const
|
|
{
|
|
ValueImp *imp = getDirect(propertyName);
|
|
if (imp)
|
|
return Value(imp);
|
|
|
|
Object proto = Object::dynamicCast(prototype());
|
|
|
|
// non-standard netscape extension
|
|
if (propertyName == specialPrototypePropertyName) {
|
|
if (!proto.isValid())
|
|
return Null();
|
|
else
|
|
return Value(proto);
|
|
}
|
|
|
|
if (!proto.isValid())
|
|
return Undefined();
|
|
|
|
return proto.get(exec,propertyName);
|
|
}
|
|
|
|
Value ObjectImp::getPropertyByIndex(ExecState *exec,
|
|
unsigned propertyName) const
|
|
{
|
|
return get(exec, Identifier::from(propertyName));
|
|
}
|
|
|
|
// ECMA 8.6.2.2
|
|
void ObjectImp::put(ExecState *exec, const Identifier &propertyName,
|
|
const Value &value, int attr)
|
|
{
|
|
assert(value.isValid());
|
|
|
|
// non-standard netscape extension
|
|
if (propertyName == specialPrototypePropertyName) {
|
|
setPrototype(value);
|
|
return;
|
|
}
|
|
|
|
/* TODO: check for write permissions directly w/o this call */
|
|
/* Doesn't look very easy with the PropertyMap API - David */
|
|
// putValue() is used for JS assignemnts. It passes no attribute.
|
|
// Assume that a C++ implementation knows what it is doing
|
|
// and let it override the canPut() check.
|
|
if ((attr == None || attr == DontDelete) && !canPut(exec,propertyName)) {
|
|
#ifdef KJS_VERBOSE
|
|
fprintf( stderr, "WARNING: canPut %s said NO\n", propertyName.ascii() );
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
_prop.put(propertyName,value.imp(),attr);
|
|
}
|
|
|
|
// delme
|
|
void ObjectImp::putPropertyByIndex(ExecState *exec, unsigned propertyName,
|
|
const Value &value, int attr)
|
|
{
|
|
put(exec, Identifier::from(propertyName), value, attr);
|
|
}
|
|
|
|
// ECMA 8.6.2.3
|
|
bool ObjectImp::canPut(ExecState *, const Identifier &propertyName) const
|
|
{
|
|
int attributes;
|
|
ValueImp *v = _prop.get(propertyName, attributes);
|
|
if (v)
|
|
return!(attributes & ReadOnly);
|
|
|
|
// Look in the static hashtable of properties
|
|
const HashEntry* e = findPropertyHashEntry(propertyName);
|
|
if (e)
|
|
return !(e->attr & ReadOnly);
|
|
|
|
// Don't look in the prototype here. We can always put an override
|
|
// in the object, even if the prototype has a ReadOnly property.
|
|
return true;
|
|
}
|
|
|
|
// ECMA 8.6.2.4
|
|
bool ObjectImp::hasProperty(ExecState *exec, const Identifier &propertyName) const
|
|
{
|
|
if (_prop.get(propertyName))
|
|
return true;
|
|
|
|
// Look in the static hashtable of properties
|
|
if (findPropertyHashEntry(propertyName))
|
|
return true;
|
|
|
|
// non-standard netscape extension
|
|
if (propertyName == specialPrototypePropertyName)
|
|
return true;
|
|
|
|
// Look in the prototype
|
|
Object proto = Object::dynamicCast(prototype());
|
|
return proto.isValid() && proto.hasProperty(exec,propertyName);
|
|
}
|
|
|
|
bool ObjectImp::hasPropertyByIndex(ExecState *exec, unsigned propertyName) const
|
|
{
|
|
return hasProperty(exec, Identifier::from(propertyName));
|
|
}
|
|
|
|
// ECMA 8.6.2.5
|
|
bool ObjectImp::deleteProperty(ExecState * /*exec*/, const Identifier &propertyName)
|
|
{
|
|
int attributes;
|
|
ValueImp *v = _prop.get(propertyName, attributes);
|
|
if (v) {
|
|
if ((attributes & DontDelete))
|
|
return false;
|
|
_prop.remove(propertyName);
|
|
return true;
|
|
}
|
|
|
|
// Look in the static hashtable of properties
|
|
const HashEntry* entry = findPropertyHashEntry(propertyName);
|
|
if (entry && entry->attr & DontDelete)
|
|
return false; // this builtin property can't be deleted
|
|
return true;
|
|
}
|
|
|
|
bool ObjectImp::deletePropertyByIndex(ExecState *exec, unsigned propertyName)
|
|
{
|
|
return deleteProperty(exec, Identifier::from(propertyName));
|
|
}
|
|
|
|
void ObjectImp::deleteAllProperties( ExecState * )
|
|
{
|
|
_prop.clear();
|
|
}
|
|
|
|
// ECMA 8.6.2.6
|
|
Value ObjectImp::defaultValue(ExecState *exec, Type hint) const
|
|
{
|
|
if (hint != StringType && hint != NumberType) {
|
|
/* Prefer String for Date objects */
|
|
if (_proto == exec->lexicalInterpreter()->builtinDatePrototype().imp())
|
|
hint = StringType;
|
|
else
|
|
hint = NumberType;
|
|
}
|
|
|
|
Value v;
|
|
if (hint == StringType)
|
|
v = get(exec,toStringPropertyName);
|
|
else
|
|
v = get(exec,valueOfPropertyName);
|
|
|
|
if (v.type() == ObjectType) {
|
|
Object o = Object(static_cast<ObjectImp*>(v.imp()));
|
|
if (o.implementsCall()) { // spec says "not primitive type" but ...
|
|
Object thisObj = Object(const_cast<ObjectImp*>(this));
|
|
Value def = o.call(exec,thisObj,List::empty());
|
|
Type defType = def.type();
|
|
if (defType == UnspecifiedType || defType == UndefinedType ||
|
|
defType == NullType || defType == BooleanType ||
|
|
defType == StringType || defType == NumberType) {
|
|
return def;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hint == StringType)
|
|
v = get(exec,valueOfPropertyName);
|
|
else
|
|
v = get(exec,toStringPropertyName);
|
|
|
|
if (v.type() == ObjectType) {
|
|
Object o = Object(static_cast<ObjectImp*>(v.imp()));
|
|
if (o.implementsCall()) { // spec says "not primitive type" but ...
|
|
Object thisObj = Object(const_cast<ObjectImp*>(this));
|
|
Value def = o.call(exec,thisObj,List::empty());
|
|
Type defType = def.type();
|
|
if (defType == UnspecifiedType || defType == UndefinedType ||
|
|
defType == NullType || defType == BooleanType ||
|
|
defType == StringType || defType == NumberType) {
|
|
return def;
|
|
}
|
|
}
|
|
}
|
|
|
|
Object err = Error::create(exec, TypeError, I18N_NOOP("No default value"));
|
|
exec->setException(err);
|
|
return err;
|
|
}
|
|
|
|
const HashEntry* ObjectImp::findPropertyHashEntry( const Identifier& propertyName ) const
|
|
{
|
|
const ClassInfo *info = classInfo();
|
|
while (info) {
|
|
if (info->propHashTable) {
|
|
const HashEntry *e = Lookup::findEntry(info->propHashTable, propertyName);
|
|
if (e)
|
|
return e;
|
|
}
|
|
info = info->parentClass;
|
|
}
|
|
return 0L;
|
|
}
|
|
|
|
bool ObjectImp::implementsConstruct() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Object ObjectImp::construct(ExecState* /*exec*/, const List &/*args*/)
|
|
{
|
|
assert(false);
|
|
return Object(0);
|
|
}
|
|
|
|
bool ObjectImp::implementsCall() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Value ObjectImp::call(ExecState* /*exec*/, Object &/*thisObj*/, const List &/*args*/)
|
|
{
|
|
assert(false);
|
|
return Object(0);
|
|
}
|
|
|
|
bool ObjectImp::implementsHasInstance() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Boolean ObjectImp::hasInstance(ExecState* /*exec*/, const Value &/*value*/)
|
|
{
|
|
assert(false);
|
|
return Boolean(false);
|
|
}
|
|
|
|
ReferenceList ObjectImp::propList(ExecState *exec, bool recursive)
|
|
{
|
|
ReferenceList list;
|
|
if (_proto && _proto->dispatchType() == ObjectType && recursive)
|
|
list = static_cast<ObjectImp*>(_proto)->propList(exec,recursive);
|
|
|
|
_prop.addEnumerablesToReferenceList(list, Object(this));
|
|
|
|
// Add properties from the static hashtable of properties
|
|
const ClassInfo *info = classInfo();
|
|
while (info) {
|
|
if (info->propHashTable) {
|
|
int size = info->propHashTable->size;
|
|
const HashEntry *e = info->propHashTable->entries;
|
|
for (int i = 0; i < size; ++i, ++e) {
|
|
if ( e->soffset && !(e->attr & DontEnum) )
|
|
list.append(Reference(this, &info->propHashTable->sbase[e->soffset])); /// ######### check for duplicates with the propertymap
|
|
}
|
|
}
|
|
info = info->parentClass;
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
Value ObjectImp::internalValue() const
|
|
{
|
|
return Value(_internalValue);
|
|
}
|
|
|
|
void ObjectImp::setInternalValue(const Value &v)
|
|
{
|
|
_internalValue = v.imp();
|
|
}
|
|
|
|
void ObjectImp::setInternalValue(ValueImp *v)
|
|
{
|
|
v->setGcAllowed();
|
|
_internalValue = v;
|
|
}
|
|
|
|
Value ObjectImp::toPrimitive(ExecState *exec, Type preferredType) const
|
|
{
|
|
return defaultValue(exec,preferredType);
|
|
}
|
|
|
|
bool ObjectImp::toBoolean(ExecState* /*exec*/) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
double ObjectImp::toNumber(ExecState *exec) const
|
|
{
|
|
Value prim = toPrimitive(exec,NumberType);
|
|
if (exec->hadException()) // should be picked up soon in nodes.cpp
|
|
return 0.0;
|
|
return prim.toNumber(exec);
|
|
}
|
|
|
|
UString ObjectImp::toString(ExecState *exec) const
|
|
{
|
|
Value prim = toPrimitive(exec,StringType);
|
|
if (exec->hadException()) // should be picked up soon in nodes.cpp
|
|
return "";
|
|
return prim.toString(exec);
|
|
}
|
|
|
|
Object ObjectImp::toObject(ExecState * /*exec*/) const
|
|
{
|
|
return Object(const_cast<ObjectImp*>(this));
|
|
}
|
|
|
|
void ObjectImp::putDirect(const Identifier &propertyName, ValueImp *value, int attr)
|
|
{
|
|
value->setGcAllowed();
|
|
_prop.put(propertyName, value, attr);
|
|
}
|
|
|
|
void ObjectImp::putDirect(const Identifier &propertyName, int value, int attr)
|
|
{
|
|
_prop.put(propertyName, NumberImp::create(value), attr);
|
|
}
|
|
|
|
void ObjectImp::setFunctionName(const Identifier &propertyName)
|
|
{
|
|
if (inherits(&InternalFunctionImp::info))
|
|
static_cast<InternalFunctionImp*>(this)->setName(propertyName);
|
|
}
|
|
|
|
// ------------------------------ Error ----------------------------------------
|
|
|
|
const char * const errorNamesArr[] = {
|
|
I18N_NOOP("Error"), // GeneralError
|
|
I18N_NOOP("Evaluation error"), // EvalError
|
|
I18N_NOOP("Range error"), // RangeError
|
|
I18N_NOOP("Reference error"), // ReferenceError
|
|
I18N_NOOP("Syntax error"), // SyntaxError
|
|
I18N_NOOP("Type error"), // TypeError
|
|
I18N_NOOP("URI error"), // URIError
|
|
};
|
|
|
|
const char * const * const Error::errorNames = errorNamesArr;
|
|
|
|
Object Error::create(ExecState *exec, ErrorType errtype, const char *message,
|
|
int lineno, int sourceId)
|
|
{
|
|
#ifdef KJS_VERBOSE
|
|
// message could be 0L. Don't enable this on Solaris ;)
|
|
fprintf(stderr, "WARNING: KJS %s: %s\n", errorNames[errtype], message);
|
|
#endif
|
|
|
|
Object cons;
|
|
|
|
switch (errtype) {
|
|
case EvalError:
|
|
cons = exec->lexicalInterpreter()->builtinEvalError();
|
|
break;
|
|
case RangeError:
|
|
cons = exec->lexicalInterpreter()->builtinRangeError();
|
|
break;
|
|
case ReferenceError:
|
|
cons = exec->lexicalInterpreter()->builtinReferenceError();
|
|
break;
|
|
case SyntaxError:
|
|
cons = exec->lexicalInterpreter()->builtinSyntaxError();
|
|
break;
|
|
case TypeError:
|
|
cons = exec->lexicalInterpreter()->builtinTypeError();
|
|
break;
|
|
case URIError:
|
|
cons = exec->lexicalInterpreter()->builtinURIError();
|
|
break;
|
|
default:
|
|
cons = exec->lexicalInterpreter()->builtinError();
|
|
break;
|
|
}
|
|
|
|
if (!message)
|
|
message = errorNames[errtype];
|
|
List args;
|
|
args.append(String(message));
|
|
Object err = Object::dynamicCast(cons.construct(exec,args));
|
|
|
|
if (lineno != -1)
|
|
err.put(exec, "line", Number(lineno));
|
|
if (sourceId != -1)
|
|
err.put(exec, "sourceId", Number(sourceId));
|
|
|
|
return err;
|
|
|
|
/*
|
|
#ifndef NDEBUG
|
|
const char *msg = err.get(messagePropertyName).toString().value().ascii();
|
|
if (l >= 0)
|
|
fprintf(stderr, "KJS: %s at line %d. %s\n", estr, l, msg);
|
|
else
|
|
fprintf(stderr, "KJS: %s. %s\n", estr, msg);
|
|
#endif
|
|
|
|
return err;
|
|
*/
|
|
}
|
|
|