blob: 5065caf6271229fda27008c7a2599b9a5bb2c4ba [file] [log] [blame]
// -*- c-basic-offset: 2 -*-
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
#include "nodes.h"
//#include <iostream>
#include <math.h>
#include <assert.h>
#ifdef KJS_DEBUG_MEM
#include <stdio.h>
#include <typeinfo>
#endif
#include "collector.h"
#include "debugger.h"
#include "function_object.h"
#include "internal.h"
#include "value.h"
#include "object.h"
#include "types.h"
#include "interpreter.h"
#include "lexer.h"
#include "operations.h"
#include "ustring.h"
using namespace KJS;
#define KJS_BREAKPOINT \
if (!hitStatement(exec)) \
return Completion(Normal);
#define KJS_ABORTPOINT \
if (exec->interpreter()->imp()->debugger() && \
exec->interpreter()->imp()->debugger()->imp()->aborted()) \
return Completion(Normal);
#define KJS_CHECKEXCEPTION \
if (exec->hadException()) \
return Completion(Throw, exec->exception()); \
if (Collector::outOfMemory()) \
return Completion(Throw, Error::create(exec,GeneralError,"Out of memory"));
#define KJS_CHECKEXCEPTIONVALUE \
if (exec->hadException()) \
return exec->exception(); \
if (Collector::outOfMemory()) \
return Undefined(); // will be picked up by KJS_CHECKEXCEPTION
#define KJS_CHECKEXCEPTIONREFERENCE \
if (exec->hadException()) \
return Reference::makeValueReference(Undefined());; \
if (Collector::outOfMemory()) \
return Reference::makeValueReference(Undefined()); // will be picked up by KJS_CHECKEXCEPTION
#define KJS_CHECKEXCEPTIONLIST \
if (exec->hadException()) \
return List(); \
if (Collector::outOfMemory()) \
return List(); // will be picked up by KJS_CHECKEXCEPTION
#ifdef KJS_DEBUG_MEM
std::list<Node *> * Node::s_nodes = 0L;
#endif
// ------------------------------ Node -----------------------------------------
Node::Node()
{
line = Lexer::curr()->lineNo();
refcount = 0;
#ifdef KJS_DEBUG_MEM
if (!s_nodes)
s_nodes = new std::list<Node *>;
s_nodes->push_back(this);
#endif
}
Node::~Node()
{
#ifdef KJS_DEBUG_MEM
s_nodes->remove( this );
#endif
}
Reference Node::evaluateReference(ExecState *exec)
{
Value v = evaluate(exec);
KJS_CHECKEXCEPTIONREFERENCE
return Reference::makeValueReference(v);
}
#ifdef KJS_DEBUG_MEM
void Node::finalCheck()
{
fprintf( stderr, "Node::finalCheck(): list count : %d\n", (int)s_nodes.size() );
fprintf( stderr, "[%d] Still having node %p (%s) (refcount %d)\n", i, (void*)*it, typeid( **it ).name(), (*it)->refcount );
delete s_nodes;
s_nodes = 0L;
}
#endif
Value Node::throwError(ExecState *exec, ErrorType e, const char *msg)
{
Object err = Error::create(exec, e, msg, lineNo(), sourceId());
exec->setException(err);
return err;
}
// ------------------------------ StatementNode --------------------------------
StatementNode::StatementNode() : l0(-1), l1(-1), sid(-1), breakPoint(false)
{
}
void StatementNode::setLoc(int line0, int line1, int sourceId)
{
l0 = line0;
l1 = line1;
sid = sourceId;
}
// return true if the debugger wants us to stop at this point
bool StatementNode::hitStatement(ExecState *exec)
{
Debugger *dbg = exec->interpreter()->imp()->debugger();
if (dbg)
return dbg->atStatement(exec,sid,l0,l1);
else
return true; // continue
}
// return true if the debugger wants us to stop at this point
bool StatementNode::abortStatement(ExecState *exec)
{
Debugger *dbg = exec->interpreter()->imp()->debugger();
if (dbg)
return dbg->imp()->aborted();
else
return false;
}
// ------------------------------ NullNode -------------------------------------
Value NullNode::evaluate(ExecState */*exec*/)
{
return Null();
}
// ------------------------------ BooleanNode ----------------------------------
Value BooleanNode::evaluate(ExecState */*exec*/)
{
return Boolean(value);
}
// ------------------------------ NumberNode -----------------------------------
Value NumberNode::evaluate(ExecState */*exec*/)
{
return Number(value);
}
// ------------------------------ StringNode -----------------------------------
Value StringNode::evaluate(ExecState */*exec*/)
{
return String(value);
}
// ------------------------------ RegExpNode -----------------------------------
Value RegExpNode::evaluate(ExecState *exec)
{
List list;
String p(pattern);
String f(flags);
list.append(p);
list.append(f);
Object reg = exec->interpreter()->imp()->builtinRegExp();
return reg.construct(exec,list);
}
// ------------------------------ ThisNode -------------------------------------
// ECMA 11.1.1
Value ThisNode::evaluate(ExecState *exec)
{
return exec->context().thisValue();
}
// ------------------------------ ResolveNode ----------------------------------
// ECMA 11.1.2 & 10.1.4
Value ResolveNode::evaluate(ExecState *exec)
{
return evaluateReference(exec).getValue(exec);
}
Reference ResolveNode::evaluateReference(ExecState *exec)
{
const List chain = exec->context().scopeChain();
ListIterator scope = chain.begin();
while (scope != chain.end()) {
ObjectImp *o = static_cast<ObjectImp*>((*scope).imp());
//cout << "Resolve: looking at '" << ident.ascii() << "'"
// << " in " << (void*)o << " " << o->classInfo()->className << endl;
if (o->hasProperty(exec,ident)) {
//cout << "Resolve: FOUND '" << ident.ascii() << "'"
// << " in " << (void*)o << " " << o->classInfo()->className << endl;
return Reference(Object(o), ident);
}
scope++;
}
// identifier not found
//cout << "Resolve: didn't find '" << ident.ascii() << "'" << endl;
return Reference(Null(), ident);
}
// ------------------------------ GroupNode ------------------------------------
void GroupNode::ref()
{
Node::ref();
if ( group )
group->ref();
}
bool GroupNode::deref()
{
if ( group && group->deref() )
delete group;
return Node::deref();
}
// ECMA 11.1.6
Value GroupNode::evaluate(ExecState *exec)
{
return group->evaluate(exec);
}
// ------------------------------ ElisionNode ----------------------------------
void ElisionNode::ref()
{
Node::ref();
if ( elision )
elision->ref();
}
bool ElisionNode::deref()
{
if ( elision && elision->deref() )
delete elision;
return Node::deref();
}
// ECMA 11.1.4
Value ElisionNode::evaluate(ExecState *exec)
{
if (elision)
return Number(elision->evaluate(exec).toNumber(exec) + 1);
else
return Number(1);
}
// ------------------------------ ElementNode ----------------------------------
void ElementNode::ref()
{
Node::ref();
if ( list )
list->ref();
if ( elision )
elision->ref();
if ( node )
node->ref();
}
bool ElementNode::deref()
{
if ( list && list->deref() )
delete list;
if ( elision && elision->deref() )
delete elision;
if ( node && node->deref() )
delete node;
return Node::deref();
}
// ECMA 11.1.4
Value ElementNode::evaluate(ExecState *exec)
{
Object array;
Value val;
int length = 0;
int elisionLen = elision ? elision->evaluate(exec).toInt32(exec) : 0;
KJS_CHECKEXCEPTIONVALUE
if (list) {
array = Object(static_cast<ObjectImp*>(list->evaluate(exec).imp()));
KJS_CHECKEXCEPTIONVALUE
val = node->evaluate(exec);
length = array.get(exec,lengthPropertyName).toInt32(exec);
} else {
Value newArr = exec->interpreter()->builtinArray().construct(exec,List::empty());
array = Object(static_cast<ObjectImp*>(newArr.imp()));
val = node->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
}
array.put(exec, elisionLen + length, val);
return array;
}
// ------------------------------ ArrayNode ------------------------------------
void ArrayNode::ref()
{
Node::ref();
if ( element )
element->ref();
if ( elision )
elision->ref();
}
bool ArrayNode::deref()
{
if ( element && element->deref() )
delete element;
if ( elision && elision->deref() )
delete elision;
return Node::deref();
}
// ECMA 11.1.4
Value ArrayNode::evaluate(ExecState *exec)
{
Object array;
int length;
int elisionLen = elision ? elision->evaluate(exec).toInt32(exec) : 0;
KJS_CHECKEXCEPTIONVALUE
if (element) {
array = Object(static_cast<ObjectImp*>(element->evaluate(exec).imp()));
KJS_CHECKEXCEPTIONVALUE
length = opt ? array.get(exec,lengthPropertyName).toInt32(exec) : 0;
} else {
Value newArr = exec->interpreter()->builtinArray().construct(exec,List::empty());
array = Object(static_cast<ObjectImp*>(newArr.imp()));
length = 0;
}
if (opt)
array.put(exec,lengthPropertyName, Number(elisionLen + length), DontEnum | DontDelete);
return array;
}
// ------------------------------ ObjectLiteralNode ----------------------------
void ObjectLiteralNode::ref()
{
Node::ref();
if ( list )
list->ref();
}
bool ObjectLiteralNode::deref()
{
if ( list && list->deref() )
delete list;
return Node::deref();
}
// ECMA 11.1.5
Value ObjectLiteralNode::evaluate(ExecState *exec)
{
if (list)
return list->evaluate(exec);
return exec->interpreter()->builtinObject().construct(exec,List::empty());
}
// ------------------------------ PropertyValueNode ----------------------------
void PropertyValueNode::ref()
{
Node::ref();
if ( name )
name->ref();
if ( assign )
assign->ref();
if ( list )
list->ref();
}
bool PropertyValueNode::deref()
{
if ( name && name->deref() )
delete name;
if ( assign && assign->deref() )
delete assign;
if ( list && list->deref() )
delete list;
return Node::deref();
}
// ECMA 11.1.5
Value PropertyValueNode::evaluate(ExecState *exec)
{
Object obj;
if (list) {
obj = Object(static_cast<ObjectImp*>(list->evaluate(exec).imp()));
KJS_CHECKEXCEPTIONVALUE
}
else {
Value newObj = exec->interpreter()->builtinObject().construct(exec,List::empty());
obj = Object(static_cast<ObjectImp*>(newObj.imp()));
}
Value n = name->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v = assign->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
obj.put(exec,n.toString(exec), v);
return obj;
}
// ------------------------------ PropertyNode ---------------------------------
// ECMA 11.1.5
Value PropertyNode::evaluate(ExecState */*exec*/)
{
Value s;
if (str.isNull()) {
s = String(UString::from(numeric));
} else {
s = String(str.ustring());
}
return s;
}
// ------------------------------ AccessorNode1 --------------------------------
void AccessorNode1::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
}
bool AccessorNode1::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
return Node::deref();
}
// ECMA 11.2.1a
Value AccessorNode1::evaluate(ExecState *exec)
{
return evaluateReference(exec).getValue(exec);
}
Reference AccessorNode1::evaluateReference(ExecState *exec)
{
Value v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONREFERENCE
Value v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONREFERENCE
Object o = v1.toObject(exec);
unsigned i;
if (v2.toUInt32(i))
return Reference(o, i);
String s = v2.toString(exec);
return Reference(o, s.value());
}
// ------------------------------ AccessorNode2 --------------------------------
void AccessorNode2::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool AccessorNode2::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.2.1b
Value AccessorNode2::evaluate(ExecState *exec)
{
return evaluateReference(exec).getValue(exec);
}
Reference AccessorNode2::evaluateReference(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONREFERENCE
Object o = v.toObject(exec);
return Reference(o, ident);
}
// ------------------------------ ArgumentListNode -----------------------------
ArgumentListNode::ArgumentListNode(Node *e) : list(0L), expr(e)
{
}
ArgumentListNode::ArgumentListNode(ArgumentListNode *l, Node *e)
: list(l), expr(e)
{
}
void ArgumentListNode::ref()
{
ArgumentListNode *l = this;
while (l != NULL) {
l->Node::ref();
if ( l->expr )
l->expr->ref();
l = l->list;
}
}
bool ArgumentListNode::deref()
{
if ( expr && expr->deref() )
delete expr;
ArgumentListNode *l = this->list;
while (l != NULL) {
if ( l->expr && l->expr->deref() )
delete l->expr;
ArgumentListNode *next = l->list;
if (l->Node::deref()) {
l->list = NULL;
delete l;
}
l = next;
}
return Node::deref();
}
Value ArgumentListNode::evaluate(ExecState */*exec*/)
{
assert(0);
return Value(); // dummy, see evaluateList()
}
// ECMA 11.2.4
List ArgumentListNode::evaluateList(ExecState *exec)
{
List l;
ArgumentListNode *n = this;
while (n != NULL) {
Value v = n->expr->evaluate(exec);
KJS_CHECKEXCEPTIONLIST
l.append(v);
n = n->list;
}
return l;
}
// ------------------------------ ArgumentsNode --------------------------------
ArgumentsNode::ArgumentsNode(ArgumentListNode *l) : list(l)
{
}
void ArgumentsNode::ref()
{
Node::ref();
if ( list )
list->ref();
}
bool ArgumentsNode::deref()
{
if ( list && list->deref() )
delete list;
return Node::deref();
}
Value ArgumentsNode::evaluate(ExecState */*exec*/)
{
assert(0);
return Value(); // dummy, see evaluateList()
}
// ECMA 11.2.4
List ArgumentsNode::evaluateList(ExecState *exec)
{
if (!list)
return List();
return list->evaluateList(exec);
}
// ------------------------------ NewExprNode ----------------------------------
// ECMA 11.2.2
void NewExprNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
if ( args )
args->ref();
}
bool NewExprNode::deref()
{
if ( expr && expr->deref() )
delete expr;
if ( args && args->deref() )
delete args;
return Node::deref();
}
Value NewExprNode::evaluate(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
List argList;
if (args) {
argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
}
if (v.type() != ObjectType) {
return throwError(exec, TypeError, "Expression is no object. Cannot be new'ed");
}
Object constr = Object(static_cast<ObjectImp*>(v.imp()));
if (!constr.implementsConstruct()) {
return throwError(exec, TypeError, "Expression is no constructor.");
}
Value res = constr.construct(exec,argList);
return res;
}
// ------------------------------ FunctionCallNode -----------------------------
void FunctionCallNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
if ( args )
args->ref();
}
bool FunctionCallNode::deref()
{
if ( expr && expr->deref() )
delete expr;
if ( args && args->deref() )
delete args;
return Node::deref();
}
// ECMA 11.2.3
Value FunctionCallNode::evaluate(ExecState *exec)
{
Reference ref = expr->evaluateReference(exec);
KJS_CHECKEXCEPTIONVALUE
List argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
Value v = ref.getValue(exec);
if (v.type() != ObjectType) {
#ifndef NDEBUG
printInfo(exec, "WARNING: Failed function call attempt on", v, line);
#endif
return throwError(exec, TypeError, "Expression is no object. Cannot be called.");
}
Object func = Object(static_cast<ObjectImp*>(v.imp()));
if (!func.implementsCall()) {
#ifndef NDEBUG
printInfo(exec, "Failed function call attempt on", v, line);
#endif
return throwError(exec, TypeError, "Expression does not allow calls.");
}
#if KJS_MAX_STACK > 0
static int depth = 0; // sum of all concurrent interpreters
if (++depth > KJS_MAX_STACK) {
#ifndef NDEBUG
printInfo(exec, "Exceeded maximum function call depth", v, line);
#endif
return throwError(exec, RangeError, "Exceeded maximum call stack size.");
}
#endif
Value thisVal;
// XXX - should check for constant reference
if (ref.isMutable())
thisVal = ref.getBase(exec);
else
thisVal = Null();
if (thisVal.type() == ObjectType &&
Object::dynamicCast(thisVal).inherits(&ActivationImp::info))
thisVal = Null();
if (thisVal.type() != ObjectType) {
// ECMA 11.2.3 says that in this situation the this value should be null.
// However, section 10.2.3 says that in the case where the value provided
// by the caller is null, the global object should be used. It also says
// that the section does not apply to interal functions, but for simplicity
// of implementation we use the global object anyway here. This guarantees
// that in host objects you always get a valid object for this.
// thisVal = Null();
thisVal = exec->interpreter()->globalObject();
}
Object thisObj = Object::dynamicCast(thisVal);
Value result = func.call(exec,thisObj, argList);
#if KJS_MAX_STACK > 0
--depth;
#endif
return result;
}
// ------------------------------ PostfixNode ----------------------------------
void PostfixNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool PostfixNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.3
Value PostfixNode::evaluate(ExecState *exec)
{
Reference ref = expr->evaluateReference(exec);
KJS_CHECKEXCEPTIONVALUE
Value v = ref.getValue(exec);
Number n = v.toNumber(exec);
double newValue = (oper == OpPlusPlus) ? n.value() + 1 : n.value() - 1;
Value n2 = Number(newValue);
ref.putValue(exec,n2);
return n;
}
// ------------------------------ DeleteNode -----------------------------------
void DeleteNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool DeleteNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.1
Value DeleteNode::evaluate(ExecState *exec)
{
Reference ref = expr->evaluateReference(exec);
KJS_CHECKEXCEPTIONVALUE
return Boolean(ref.deleteValue(exec));
}
// ------------------------------ VoidNode -------------------------------------
void VoidNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool VoidNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.2
Value VoidNode::evaluate(ExecState *exec)
{
Value dummy1 = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return Undefined();
}
// ------------------------------ TypeOfNode -----------------------------------
void TypeOfNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool TypeOfNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.3
Value TypeOfNode::evaluate(ExecState *exec)
{
const char *s = 0L;
Reference ref = expr->evaluateReference(exec);
KJS_CHECKEXCEPTIONVALUE
// XXX - Really should check if this is a constant reference
if (ref.isMutable()) {
Value b = ref.getBase(exec);
if (b.type() == NullType)
return String("undefined");
}
Value v = ref.getValue(exec);
switch (v.type())
{
case UndefinedType:
s = "undefined";
break;
case NullType:
s = "object";
break;
case BooleanType:
s = "boolean";
break;
case NumberType:
s = "number";
break;
case StringType:
s = "string";
break;
default:
if (v.type() == ObjectType && static_cast<ObjectImp*>(v.imp())->implementsCall())
s = "function";
else
s = "object";
break;
}
return String(s);
}
// ------------------------------ PrefixNode -----------------------------------
void PrefixNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool PrefixNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.4 and 11.4.5
Value PrefixNode::evaluate(ExecState *exec)
{
Reference ref = expr->evaluateReference(exec);
KJS_CHECKEXCEPTIONVALUE
Value v = ref.getValue(exec);
Number n = v.toNumber(exec);
double newValue = (oper == OpPlusPlus) ? n.value() + 1 : n.value() - 1;
Value n2 = Number(newValue);
ref.putValue(exec,n2);
return n2;
}
// ------------------------------ UnaryPlusNode --------------------------------
void UnaryPlusNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool UnaryPlusNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.6
Value UnaryPlusNode::evaluate(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return Number(v.toNumber(exec)); /* TODO: optimize */
}
// ------------------------------ NegateNode -----------------------------------
void NegateNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool NegateNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.7
Value NegateNode::evaluate(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Number n = v.toNumber(exec);
double d = -n.value();
return Number(d);
}
// ------------------------------ BitwiseNotNode -------------------------------
void BitwiseNotNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool BitwiseNotNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.8
Value BitwiseNotNode::evaluate(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
int i32 = v.toInt32(exec);
return Number(~i32);
}
// ------------------------------ LogicalNotNode -------------------------------
void LogicalNotNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool LogicalNotNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.4.9
Value LogicalNotNode::evaluate(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool b = v.toBoolean(exec);
return Boolean(!b);
}
// ------------------------------ MultNode -------------------------------------
void MultNode::ref()
{
Node::ref();
if ( term1 )
term1->ref();
if ( term2 )
term2->ref();
}
bool MultNode::deref()
{
if ( term1 && term1->deref() )
delete term1;
if ( term2 && term2->deref() )
delete term2;
return Node::deref();
}
// ECMA 11.5
Value MultNode::evaluate(ExecState *exec)
{
Value v1 = term1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v2 = term2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return mult(exec,v1, v2, oper);
}
// ------------------------------ AddNode --------------------------------------
void AddNode::ref()
{
Node::ref();
if ( term1 )
term1->ref();
if ( term2 )
term2->ref();
}
bool AddNode::deref()
{
if ( term1 && term1->deref() )
delete term1;
if ( term2 && term2->deref() )
delete term2;
return Node::deref();
}
// ECMA 11.6
Value AddNode::evaluate(ExecState *exec)
{
Value v1 = term1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v2 = term2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return add(exec,v1, v2, oper);
}
// ------------------------------ ShiftNode ------------------------------------
void ShiftNode::ref()
{
Node::ref();
if ( term1 )
term1->ref();
if ( term2 )
term2->ref();
}
bool ShiftNode::deref()
{
if ( term1 && term1->deref() )
delete term1;
if ( term2 && term2->deref() )
delete term2;
return Node::deref();
}
// ECMA 11.7
Value ShiftNode::evaluate(ExecState *exec)
{
Value v1 = term1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v2 = term2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
unsigned int i2 = v2.toUInt32(exec);
i2 &= 0x1f;
long result;
switch (oper) {
case OpLShift:
result = v1.toInt32(exec) << i2;
break;
case OpRShift:
result = v1.toInt32(exec) >> i2;
break;
case OpURShift:
result = v1.toUInt32(exec) >> i2;
break;
default:
assert(!"ShiftNode: unhandled switch case");
result = 0L;
}
return Number(static_cast<double>(result));
}
// ------------------------------ RelationalNode -------------------------------
void RelationalNode::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
}
bool RelationalNode::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
return Node::deref();
}
// ECMA 11.8
Value RelationalNode::evaluate(ExecState *exec)
{
Value v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool b;
if (oper == OpLess || oper == OpGreaterEq) {
int r = relation(exec, v1, v2);
if (r < 0)
b = false;
else
b = (oper == OpLess) ? (r == 1) : (r == 0);
} else if (oper == OpGreater || oper == OpLessEq) {
int r = relation(exec, v2, v1);
if (r < 0)
b = false;
else
b = (oper == OpGreater) ? (r == 1) : (r == 0);
} else if (oper == OpIn) {
// Is all of this OK for host objects?
if (v2.type() != ObjectType)
return throwError(exec, TypeError,
"Shift expression not an object into IN expression." );
Object o2(static_cast<ObjectImp*>(v2.imp()));
b = o2.hasProperty(exec,v1.toString(exec));
} else {
if (v2.type() != ObjectType)
return throwError(exec, TypeError,
"Called instanceof operator on non-object." );
Object o2(static_cast<ObjectImp*>(v2.imp()));
if (!o2.implementsHasInstance()) {
// According to the spec, only some types of objects "imlement" the [[HasInstance]] property.
// But we are supposed to throw an exception where the object does not "have" the [[HasInstance]]
// property. It seems that all object have the property, but not all implement it, so in this
// case we return false (consistent with mozilla)
return Boolean(false);
// return throwError(exec, TypeError,
// "Object does not implement the [[HasInstance]] method." );
}
return o2.hasInstance(exec, v1);
}
return Boolean(b);
}
// ------------------------------ EqualNode ------------------------------------
void EqualNode::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
}
bool EqualNode::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
return Node::deref();
}
// ECMA 11.9
Value EqualNode::evaluate(ExecState *exec)
{
Value v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool result;
if (oper == OpEqEq || oper == OpNotEq) {
// == and !=
bool eq = equal(exec,v1, v2);
result = oper == OpEqEq ? eq : !eq;
} else {
// === and !==
bool eq = strictEqual(exec,v1, v2);
result = oper == OpStrEq ? eq : !eq;
}
return Boolean(result);
}
// ------------------------------ BitOperNode ----------------------------------
void BitOperNode::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
}
bool BitOperNode::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
return Node::deref();
}
// ECMA 11.10
Value BitOperNode::evaluate(ExecState *exec)
{
Value v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
int i1 = v1.toInt32(exec);
int i2 = v2.toInt32(exec);
int result;
if (oper == OpBitAnd)
result = i1 & i2;
else if (oper == OpBitXOr)
result = i1 ^ i2;
else
result = i1 | i2;
return Number(result);
}
// ------------------------------ BinaryLogicalNode ----------------------------
void BinaryLogicalNode::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
}
bool BinaryLogicalNode::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
return Node::deref();
}
// ECMA 11.11
Value BinaryLogicalNode::evaluate(ExecState *exec)
{
Value v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool b1 = v1.toBoolean(exec);
if ((!b1 && oper == OpAnd) || (b1 && oper == OpOr))
return v1;
Value v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v2;
}
// ------------------------------ ConditionalNode ------------------------------
void ConditionalNode::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
if ( logical )
logical->ref();
}
bool ConditionalNode::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
if ( logical && logical->deref() )
delete logical;
return Node::deref();
}
// ECMA 11.12
Value ConditionalNode::evaluate(ExecState *exec)
{
Value v = logical->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool b = v.toBoolean(exec);
if (b)
v = expr1->evaluate(exec);
else
v = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ------------------------------ AssignNode -----------------------------------
void AssignNode::ref()
{
Node::ref();
if ( left )
left->ref();
if ( expr )
expr->ref();
}
bool AssignNode::deref()
{
if ( left && left->deref() )
delete left;
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 11.13
Value AssignNode::evaluate(ExecState *exec)
{
Reference l = left->evaluateReference(exec);
KJS_CHECKEXCEPTIONVALUE
Value e, v;
if (oper == OpEqual) {
v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
} else {
Value v1 = l.getValue(exec);
Value v2 = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
int i1 = v1.toInt32(exec);
int i2 = v2.toInt32(exec);
unsigned int ui;
switch (oper) {
case OpMultEq:
v = mult(exec, v1, v2, '*');
break;
case OpDivEq:
v = mult(exec, v1, v2, '/');
break;
case OpPlusEq:
v = add(exec, v1, v2, '+');
break;
case OpMinusEq:
v = add(exec, v1, v2, '-');
break;
case OpLShift:
v = Number(i1 <<= i2);
break;
case OpRShift:
v = Number(i1 >>= i2);
break;
case OpURShift:
ui = v1.toUInt32(exec);
v = Number(ui >>= i2);
break;
case OpAndEq:
v = Number(i1 &= i2);
break;
case OpXOrEq:
v = Number(i1 ^= i2);
break;
case OpOrEq:
v = Number(i1 |= i2);
break;
case OpModEq: {
double d1 = v1.toNumber(exec);
double d2 = v2.toNumber(exec);
v = Number(fmod(d1,d2));
}
break;
default:
v = Undefined();
}
};
l.putValue(exec,v);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ------------------------------ CommaNode ------------------------------------
void CommaNode::ref()
{
Node::ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
}
bool CommaNode::deref()
{
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
return Node::deref();
}
// ECMA 11.14
Value CommaNode::evaluate(ExecState *exec)
{
Value dummy = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Value v = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ------------------------------ StatListNode ---------------------------------
void StatListNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( list )
list->ref();
}
bool StatListNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( list && list->deref() )
delete list;
return Node::deref();
}
// ECMA 12.1
Completion StatListNode::execute(ExecState *exec)
{
if (!list) {
Completion c = statement->execute(exec);
KJS_ABORTPOINT
if (exec->hadException()) {
Value ex = exec->exception();
exec->clearException();
return Completion(Throw, ex);
}
else
return c;
}
Completion l = list->execute(exec);
KJS_ABORTPOINT
if (l.complType() != Normal)
return l;
Completion e = statement->execute(exec);
KJS_ABORTPOINT;
if (exec->hadException()) {
Value ex = exec->exception();
exec->clearException();
return Completion(Throw, ex);
}
Value v = e.isValueCompletion() ? e.value() : l.value();
return Completion(e.complType(), v, e.target() );
}
void StatListNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
if (list)
list->processVarDecls(exec);
}
// ------------------------------ AssignExprNode -------------------------------
void AssignExprNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool AssignExprNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.2
Value AssignExprNode::evaluate(ExecState *exec)
{
return expr->evaluate(exec);
}
// ------------------------------ VarDeclNode ----------------------------------
VarDeclNode::VarDeclNode(const Identifier &id, AssignExprNode *in)
: ident(id), init(in)
{
}
void VarDeclNode::ref()
{
Node::ref();
if ( init )
init->ref();
}
bool VarDeclNode::deref()
{
if ( init && init->deref() )
delete init;
return Node::deref();
}
// ECMA 12.2
Value VarDeclNode::evaluate(ExecState *exec)
{
Object variable = Object::dynamicCast(exec->context().variableObject());
Value val;
if (init) {
val = init->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
} else {
if ( variable.hasProperty(exec, ident ) ) // already declared ?
return Value();
val = Undefined();
}
#ifdef KJS_VERBOSE
printInfo(exec,(UString("new variable ")+ident).cstring().c_str(),val);
#endif
// We use Internal to bypass all checks in derived objects, e.g. so that
// "var location" creates a dynamic property instead of activating window.location.
variable.put(exec, ident, val, DontDelete | Internal);
return String(ident.ustring());
}
void VarDeclNode::processVarDecls(ExecState *exec)
{
Object variable = exec->context().variableObject();
variable.put(exec,ident, Undefined(), DontDelete);
}
// ------------------------------ VarDeclListNode ------------------------------
void VarDeclListNode::ref()
{
Node::ref();
if ( list )
list->ref();
if ( var )
var->ref();
}
bool VarDeclListNode::deref()
{
if ( list && list->deref() )
delete list;
if ( var && var->deref() )
delete var;
return Node::deref();
}
// ECMA 12.2
Value VarDeclListNode::evaluate(ExecState *exec)
{
if (list)
(void) list->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
(void) var->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return Undefined();
}
void VarDeclListNode::processVarDecls(ExecState *exec)
{
if (list)
list->processVarDecls(exec);
var->processVarDecls(exec);
}
// ------------------------------ VarStatementNode -----------------------------
void VarStatementNode::ref()
{
Node::ref();
if ( list )
list->ref();
}
bool VarStatementNode::deref()
{
if ( list && list->deref() )
delete list;
return Node::deref();
}
// ECMA 12.2
Completion VarStatementNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
(void) list->evaluate(exec); // returns 0L
KJS_CHECKEXCEPTION
return Completion(Normal);
}
void VarStatementNode::processVarDecls(ExecState *exec)
{
list->processVarDecls(exec);
}
// ------------------------------ BlockNode ------------------------------------
void BlockNode::ref()
{
Node::ref();
if ( source )
source->ref();
}
bool BlockNode::deref()
{
if ( source && source->deref() )
delete source;
return Node::deref();
}
// ECMA 12.1
Completion BlockNode::execute(ExecState *exec)
{
if (!source)
return Completion(Normal);
source->processFuncDecl(exec);
return source->execute(exec);
}
void BlockNode::processVarDecls(ExecState *exec)
{
if (source)
source->processVarDecls(exec);
}
// ------------------------------ EmptyStatementNode ---------------------------
// ECMA 12.3
Completion EmptyStatementNode::execute(ExecState */*exec*/)
{
return Completion(Normal);
}
// ------------------------------ ExprStatementNode ----------------------------
void ExprStatementNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool ExprStatementNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.4
Completion ExprStatementNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
return Completion(Normal, v);
}
// ------------------------------ IfNode ---------------------------------------
void IfNode::ref()
{
Node::ref();
if ( statement1 )
statement1->ref();
if ( statement2 )
statement2->ref();
if ( expr )
expr->ref();
}
bool IfNode::deref()
{
if ( statement1 && statement1->deref() )
delete statement1;
if ( statement2 && statement2->deref() )
delete statement2;
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.5
Completion IfNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
bool b = v.toBoolean(exec);
// if ... then
if (b)
return statement1->execute(exec);
// no else
if (!statement2)
return Completion(Normal);
// else
return statement2->execute(exec);
}
void IfNode::processVarDecls(ExecState *exec)
{
statement1->processVarDecls(exec);
if (statement2)
statement2->processVarDecls(exec);
}
// ------------------------------ DoWhileNode ----------------------------------
void DoWhileNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( expr )
expr->ref();
}
bool DoWhileNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.6.1
Completion DoWhileNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value be, bv;
Completion c;
Value value;
do {
// bail out on error
KJS_CHECKEXCEPTION
c = statement->execute(exec);
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
if ((c.complType() == Break) && ls.contains(c.target()))
return Completion(Normal, value);
if (c.complType() != Normal)
return c;
}
bv = expr->evaluate(exec);
KJS_CHECKEXCEPTION
} while (bv.toBoolean(exec));
return Completion(Normal, value);
}
void DoWhileNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ WhileNode ------------------------------------
void WhileNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( expr )
expr->ref();
}
bool WhileNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.6.2
Completion WhileNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value be, bv;
Completion c;
bool b(false);
Value value;
while (1) {
bv = expr->evaluate(exec);
KJS_CHECKEXCEPTION
b = bv.toBoolean(exec);
// bail out on error
KJS_CHECKEXCEPTION
if (!b)
return Completion(Normal, value);
c = statement->execute(exec);
if (c.isValueCompletion())
value = c.value();
if ((c.complType() == Continue) && ls.contains(c.target()))
continue;
if ((c.complType() == Break) && ls.contains(c.target()))
return Completion(Normal, value);
if (c.complType() != Normal)
return c;
}
}
void WhileNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ ForNode --------------------------------------
void ForNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( expr1 )
expr1->ref();
if ( expr2 )
expr2->ref();
if ( expr3 )
expr3->ref();
}
bool ForNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( expr1 && expr1->deref() )
delete expr1;
if ( expr2 && expr2->deref() )
delete expr2;
if ( expr3 && expr3->deref() )
delete expr3;
return Node::deref();
}
// ECMA 12.6.3
Completion ForNode::execute(ExecState *exec)
{
Value e, v, cval;
bool b;
if (expr1) {
v = expr1->evaluate(exec);
KJS_CHECKEXCEPTION
}
while (1) {
if (expr2) {
v = expr2->evaluate(exec);
KJS_CHECKEXCEPTION
b = v.toBoolean(exec);
if (b == false)
return Completion(Normal, cval);
}
// bail out on error
KJS_CHECKEXCEPTION
Completion c = statement->execute(exec);
if (c.isValueCompletion())
cval = c.value();
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
if ((c.complType() == Break) && ls.contains(c.target()))
return Completion(Normal, cval);
if (c.complType() != Normal)
return c;
}
if (expr3) {
v = expr3->evaluate(exec);
KJS_CHECKEXCEPTION
}
}
}
void ForNode::processVarDecls(ExecState *exec)
{
if (expr1)
expr1->processVarDecls(exec);
statement->processVarDecls(exec);
}
// ------------------------------ ForInNode ------------------------------------
ForInNode::ForInNode(Node *l, Node *e, StatementNode *s)
: init(0L), lexpr(l), expr(e), varDecl(0L), statement(s)
{
}
ForInNode::ForInNode(const Identifier &i, AssignExprNode *in, Node *e, StatementNode *s)
: ident(i), init(in), expr(e), statement(s)
{
// for( var foo = bar in baz )
varDecl = new VarDeclNode(ident, init);
lexpr = new ResolveNode(ident);
}
void ForInNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( expr )
expr->ref();
if ( lexpr )
lexpr->ref();
if ( init )
init->ref();
if ( varDecl )
varDecl->ref();
}
bool ForInNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( expr && expr->deref() )
delete expr;
if ( lexpr && lexpr->deref() )
delete lexpr;
if ( init && init->deref() )
delete init;
if ( varDecl && varDecl->deref() )
delete varDecl;
return Node::deref();
}
// ECMA 12.6.4
Completion ForInNode::execute(ExecState *exec)
{
Value e, retval;
Object v;
Completion c;
ReferenceList propList;
if ( varDecl ) {
varDecl->evaluate(exec);
KJS_CHECKEXCEPTION
}
e = expr->evaluate(exec);
KJS_CHECKEXCEPTION
v = e.toObject(exec);
propList = v.propList(exec);
ReferenceListIterator propIt = propList.begin();
while (propIt != propList.end()) {
Identifier name = propIt->getPropertyName(exec);
if (!v.hasProperty(exec,name)) {
propIt++;
continue;
}
Reference ref = lexpr->evaluateReference(exec);
KJS_CHECKEXCEPTION
ref.putValue(exec, String(name.ustring()));
c = statement->execute(exec);
if (c.isValueCompletion())
retval = c.value();
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
if ((c.complType() == Break) && ls.contains(c.target()))
break;
if (c.complType() != Normal) {
return c;
}
}
propIt++;
}
// bail out on error
KJS_CHECKEXCEPTION
return Completion(Normal, retval);
}
void ForInNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ ContinueNode ---------------------------------
// ECMA 12.7
Completion ContinueNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value dummy;
return exec->context().imp()->seenLabels()->contains(ident) ?
Completion(Continue, dummy, ident) :
Completion(Throw,
throwError(exec, SyntaxError, "Label not found in containing block"));
}
// ------------------------------ BreakNode ------------------------------------
// ECMA 12.8
Completion BreakNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value dummy;
return exec->context().imp()->seenLabels()->contains(ident) ?
Completion(Break, dummy, ident) :
Completion(Throw,
throwError(exec, SyntaxError, "Label not found in containing block"));
}
// ------------------------------ ReturnNode -----------------------------------
void ReturnNode::ref()
{
Node::ref();
if ( value )
value->ref();
}
bool ReturnNode::deref()
{
if ( value && value->deref() )
delete value;
return Node::deref();
}
// ECMA 12.9
Completion ReturnNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
if (!value)
return Completion(ReturnValue, Undefined());
Value v = value->evaluate(exec);
KJS_CHECKEXCEPTION
return Completion(ReturnValue, v);
}
// ------------------------------ WithNode -------------------------------------
void WithNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( expr )
expr->ref();
}
bool WithNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.10
Completion WithNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
Object o = v.toObject(exec);
KJS_CHECKEXCEPTION
exec->context().imp()->pushScope(o);
Completion res = statement->execute(exec);
exec->context().imp()->popScope();
return res;
}
void WithNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ CaseClauseNode -------------------------------
void CaseClauseNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
if ( list )
list->ref();
}
bool CaseClauseNode::deref()
{
if ( expr && expr->deref() )
delete expr;
if ( list && list->deref() )
delete list;
return Node::deref();
}
// ECMA 12.11
Value CaseClauseNode::evaluate(ExecState *exec)
{
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ECMA 12.11
Completion CaseClauseNode::evalStatements(ExecState *exec)
{
if (list)
return list->execute(exec);
else
return Completion(Normal, Undefined());
}
void CaseClauseNode::processVarDecls(ExecState *exec)
{
if (list)
list->processVarDecls(exec);
}
// ------------------------------ ClauseListNode -------------------------------
void ClauseListNode::ref()
{
Node::ref();
if ( cl )
cl->ref();
if ( nx )
nx->ref();
}
bool ClauseListNode::deref()
{
if ( cl && cl->deref() )
delete cl;
if ( nx && nx->deref() )
delete nx;
return Node::deref();
}
Value ClauseListNode::evaluate(ExecState */*exec*/)
{
/* should never be called */
assert(false);
return Value();
}
// ECMA 12.11
ClauseListNode* ClauseListNode::append(CaseClauseNode *c)
{
ClauseListNode *l = this;
while (l->nx)
l = l->nx;
l->nx = new ClauseListNode(c);
return this;
}
void ClauseListNode::processVarDecls(ExecState *exec)
{
if (cl)
cl->processVarDecls(exec);
if (nx)
nx->processVarDecls(exec);
}
// ------------------------------ CaseBlockNode --------------------------------
void CaseBlockNode::ref()
{
Node::ref();
if ( def )
def->ref();
if ( list1 )
list1->ref();
if ( list2 )
list2->ref();
}
bool CaseBlockNode::deref()
{
if ( def && def->deref() )
delete def;
if ( list1 && list1->deref() )
delete list1;
if ( list2 && list2->deref() )
delete list2;
return Node::deref();
}
Value CaseBlockNode::evaluate(ExecState */*exec*/)
{
/* should never be called */
assert(false);
return Value();
}
// ECMA 12.11
Completion CaseBlockNode::evalBlock(ExecState *exec, const Value& input)
{
Value v;
Completion res;
ClauseListNode *a = list1, *b = list2;
CaseClauseNode *clause;
while (a) {
clause = a->clause();
a = a->next();
v = clause->evaluate(exec);
KJS_CHECKEXCEPTION
if (strictEqual(exec, input, v)) {
res = clause->evalStatements(exec);
if (res.complType() != Normal)
return res;
while (a) {
res = a->clause()->evalStatements(exec);
if (res.complType() != Normal)
return res;
a = a->next();
}
break;
}
}
while (b) {
clause = b->clause();
b = b->next();
v = clause->evaluate(exec);
KJS_CHECKEXCEPTION
if (strictEqual(exec, input, v)) {
res = clause->evalStatements(exec);
if (res.complType() != Normal)
return res;
goto step18;
}
}
// default clause
if (def) {
res = def->evalStatements(exec);
if (res.complType() != Normal)
return res;
}
b = list2;
step18:
while (b) {
clause = b->clause();
res = clause->evalStatements(exec);
if (res.complType() != Normal)
return res;
b = b->next();
}
// bail out on error
KJS_CHECKEXCEPTION
return Completion(Normal);
}
void CaseBlockNode::processVarDecls(ExecState *exec)
{
if (list1)
list1->processVarDecls(exec);
if (def)
def->processVarDecls(exec);
if (list2)
list2->processVarDecls(exec);
}
// ------------------------------ SwitchNode -----------------------------------
void SwitchNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
if ( block )
block->ref();
}
bool SwitchNode::deref()
{
if ( expr && expr->deref() )
delete expr;
if ( block && block->deref() )
delete block;
return Node::deref();
}
// ECMA 12.11
Completion SwitchNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
Completion res = block->evalBlock(exec,v);
if ((res.complType() == Break) && ls.contains(res.target()))
return Completion(Normal, res.value());
else
return res;
}
void SwitchNode::processVarDecls(ExecState *exec)
{
block->processVarDecls(exec);
}
// ------------------------------ LabelNode ------------------------------------
void LabelNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
}
bool LabelNode::deref()
{
if ( statement && statement->deref() )
delete statement;
return Node::deref();
}
// ECMA 12.12
Completion LabelNode::execute(ExecState *exec)
{
Completion e;
if (!exec->context().imp()->seenLabels()->push(label)) {
return Completion( Throw,
throwError(exec, SyntaxError, "Duplicated label found" ));
};
e = statement->execute(exec);
exec->context().imp()->seenLabels()->pop();
if ((e.complType() == Break) && (e.target() == label))
return Completion(Normal, e.value());
else
return e;
}
void LabelNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ ThrowNode ------------------------------------
void ThrowNode::ref()
{
Node::ref();
if ( expr )
expr->ref();
}
bool ThrowNode::deref()
{
if ( expr && expr->deref() )
delete expr;
return Node::deref();
}
// ECMA 12.13
Completion ThrowNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Value v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
// bail out on error
KJS_CHECKEXCEPTION
return Completion(Throw, v);
}
// ------------------------------ CatchNode ------------------------------------
void CatchNode::ref()
{
Node::ref();
if ( block )
block->ref();
}
bool CatchNode::deref()
{
if ( block && block->deref() )
delete block;
return Node::deref();
}
Completion CatchNode::execute(ExecState */*exec*/)
{
// should never be reached. execute(exec, arg) is used instead
assert(0L);
return Completion();
}
// ECMA 12.14
Completion CatchNode::execute(ExecState *exec, const Value &arg)
{
/* TODO: correct ? Not part of the spec */
exec->clearException();
Object obj(new ObjectImp());
obj.put(exec, ident, arg, DontDelete);
exec->context().imp()->pushScope(obj);
Completion c = block->execute(exec);
exec->context().imp()->popScope();
return c;
}
void CatchNode::processVarDecls(ExecState *exec)
{
block->processVarDecls(exec);
}
// ------------------------------ FinallyNode ----------------------------------
void FinallyNode::ref()
{
Node::ref();
if ( block )
block->ref();
}
bool FinallyNode::deref()
{
if ( block && block->deref() )
delete block;
return Node::deref();
}
// ECMA 12.14
Completion FinallyNode::execute(ExecState *exec)
{
return block->execute(exec);
}
void FinallyNode::processVarDecls(ExecState *exec)
{
block->processVarDecls(exec);
}
// ------------------------------ TryNode --------------------------------------
void TryNode::ref()
{
Node::ref();
if ( block )
block->ref();
if ( _final )
_final->ref();
if ( _catch )
_catch->ref();
}
bool TryNode::deref()
{
if ( block && block->deref() )
delete block;
if ( _final && _final->deref() )
delete _final;
if ( _catch && _catch->deref() )
delete _catch;
return Node::deref();
}
// ECMA 12.14
Completion TryNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Completion c, c2;
c = block->execute(exec);
if (!_final) {
if (c.complType() != Throw)
return c;
return _catch->execute(exec,c.value());
}
if (!_catch) {
c2 = _final->execute(exec);
return (c2.complType() == Normal) ? c : c2;
}
if (c.complType() == Throw)
c = _catch->execute(exec,c.value());
c2 = _final->execute(exec);
return (c2.complType() == Normal) ? c : c2;
}
void TryNode::processVarDecls(ExecState *exec)
{
block->processVarDecls(exec);
if (_final)
_final->processVarDecls(exec);
if (_catch)
_catch->processVarDecls(exec);
}
// ------------------------------ ParameterNode --------------------------------
void ParameterNode::ref()
{
Node::ref();
if ( next )
next->ref();
}
bool ParameterNode::deref()
{
if ( next && next->deref() )
delete next;
return Node::deref();
}
ParameterNode* ParameterNode::append(const Identifier &i)
{
ParameterNode *p = this;
while (p->next)
p = p->next;
p->next = new ParameterNode(i);
return this;
}
// ECMA 13
Value ParameterNode::evaluate(ExecState */*exec*/)
{
return Undefined();
}
// ------------------------------ FunctionBodyNode -----------------------------
FunctionBodyNode::FunctionBodyNode(SourceElementsNode *s)
: source(s)
{
setLoc(-1, -1, -1);
//fprintf(stderr,"FunctionBodyNode::FunctionBodyNode %p\n",this);
}
void FunctionBodyNode::ref()
{
Node::ref();
if ( source )
source->ref();
//fprintf( stderr, "FunctionBodyNode::ref() %p. Refcount now %d\n", (void*)this, refcount);
}
bool FunctionBodyNode::deref()
{
if ( source && source->deref() )
delete source;
//fprintf( stderr, "FunctionBodyNode::deref() %p. Refcount now %d\n", (void*)this, refcount-1);
return Node::deref();
}
// ECMA 13 + 14 for ProgramNode
Completion FunctionBodyNode::execute(ExecState *exec)
{
/* TODO: workaround for empty body which I don't see covered by the spec */
if (!source)
return Completion(Normal);
source->processFuncDecl(exec);
return source->execute(exec);
}
void FunctionBodyNode::processFuncDecl(ExecState *exec)
{
if (source)
source->processFuncDecl(exec);
}
void FunctionBodyNode::processVarDecls(ExecState *exec)
{
if (source)
source->processVarDecls(exec);
}
// ------------------------------ FuncDeclNode ---------------------------------
void FuncDeclNode::ref()
{
Node::ref();
if ( param )
param->ref();
if ( body )
body->ref();
}
bool FuncDeclNode::deref()
{
if ( param && param->deref() )
delete param;
if ( body && body->deref() )
delete body;
return Node::deref();
}
// ECMA 13
void FuncDeclNode::processFuncDecl(ExecState *exec)
{
const List sc = exec->context().imp()->scopeChain();
// TODO: let this be an object with [[Class]] property "Function"
FunctionImp *fimp = new DeclaredFunctionImp(exec, ident, body, sc);
Object func(fimp); // protect from GC
// Value proto = exec->interpreter()->builtinObject().construct(exec,List::empty());
List empty;
Value proto = exec->interpreter()->builtinObject().construct(exec,empty);
func.put(exec, prototypePropertyName, proto, Internal|DontDelete);
int plen = 0;
for(ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
fimp->addParameter(p->ident());
func.put(exec, lengthPropertyName, Number(plen), ReadOnly|DontDelete|DontEnum);
exec->context().imp()->variableObject().put(exec,ident,func);
if (body) {
// hack the scope so that the function gets put as a property of func, and it's scope
// contains the func as well as our current scope
Object oldVar = exec->context().imp()->variableObject();
exec->context().imp()->setVariableObject(func);
exec->context().imp()->pushScope(func);
body->processFuncDecl(exec);
exec->context().imp()->popScope();
exec->context().imp()->setVariableObject(oldVar);
}
}
// ------------------------------ FuncExprNode ---------------------------------
void FuncExprNode::ref()
{
Node::ref();
if ( param )
param->ref();
if ( body )
body->ref();
}
bool FuncExprNode::deref()
{
if ( param && param->deref() )
delete param;
if ( body && body->deref() )
delete body;
return Node::deref();
}
// ECMA 13
Value FuncExprNode::evaluate(ExecState *exec)
{
const List sc = exec->context().scopeChain();
FunctionImp *fimp = new DeclaredFunctionImp(exec, Identifier::null, body, sc);
Value ret(fimp);
List empty;
Value proto = exec->interpreter()->builtinObject().construct(exec,empty);
fimp->put(exec, prototypePropertyName, proto, Internal|DontDelete);
int plen = 0;
for(ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
fimp->addParameter(p->ident());
fimp->put(exec,lengthPropertyName, Number(plen), ReadOnly|DontDelete|DontEnum);
return ret;
}
// ------------------------------ SourceElementNode ----------------------------
void SourceElementNode::ref()
{
Node::ref();
if ( statement )
statement->ref();
if ( function )
function->ref();
}
bool SourceElementNode::deref()
{
if ( statement && statement->deref() )
delete statement;
if ( function && function->deref() )
delete function;
return Node::deref();
}
// ECMA 14
Completion SourceElementNode::execute(ExecState *exec)
{
if (statement)
return statement->execute(exec);
return Completion(Normal);
}
// ECMA 14
void SourceElementNode::processFuncDecl(ExecState *exec)
{
if (function)
function->processFuncDecl(exec);
}
void SourceElementNode::processVarDecls(ExecState *exec)
{
if (statement)
statement->processVarDecls(exec);
}
// ------------------------------ SourceElementsNode ---------------------------
void SourceElementsNode::ref()
{
Node::ref();
if ( element )
element->ref();
if ( elements )
elements->ref();
}
bool SourceElementsNode::deref()
{
if ( element && element->deref() )
delete element;
if ( elements && elements->deref() )
delete elements;
return Node::deref();
}
// ECMA 14
Completion SourceElementsNode::execute(ExecState *exec)
{
KJS_CHECKEXCEPTION
Completion c1 = element->execute(exec);
KJS_CHECKEXCEPTION;
if (c1.complType() != Normal)
return c1;
for (SourceElementsNode *node = elements; node; node = node->elements) {
Completion c2 = node->element->execute(exec);
if (c2.complType() != Normal)
return c2;
// The spec says to return c2 here, but it seems that mozilla returns c1 if
// c2 doesn't have a value
if (!c2.value().isNull())
c1 = c2;
}
return c1;
}
// ECMA 14
void SourceElementsNode::processFuncDecl(ExecState *exec)
{
for (SourceElementsNode *node = this; node; node = node->elements) {
node->element->processFuncDecl(exec);
}
}
void SourceElementsNode::processVarDecls(ExecState *exec)
{
for (SourceElementsNode *node = this; node; node = node->elements) {
node->element->processVarDecls(exec);
}
}
ProgramNode::ProgramNode(SourceElementsNode *s): FunctionBodyNode(s) {
//fprintf(stderr,"ProgramNode::ProgramNode %p\n",this);
}