summaryrefslogtreecommitdiffstats
path: root/kjs/nodes.cpp
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kjs/nodes.cpp
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kjs/nodes.cpp')
-rw-r--r--kjs/nodes.cpp3137
1 files changed, 3137 insertions, 0 deletions
diff --git a/kjs/nodes.cpp b/kjs/nodes.cpp
new file mode 100644
index 000000000..c6ee18bf1
--- /dev/null
+++ b/kjs/nodes.cpp
@@ -0,0 +1,3137 @@
+// -*- c-basic-offset: 2 -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 1999-2002, 2003 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 "nodes.h"
+
+#include <math.h>
+#include <assert.h>
+#ifdef KJS_DEBUG_MEM
+#include <stdio.h>
+#include <typeinfo>
+#endif
+#ifdef KJS_VERBOSE
+#include <iostream>
+using namespace std;
+#endif
+
+#include "collector.h"
+#include "context.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->dynamicInterpreter()->imp()->debugger() && \
+ exec->dynamicInterpreter()->imp()->debugger()->imp()->aborted()) \
+ return Completion(Normal);
+
+#define KJS_CHECKEXCEPTION \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return Completion(Throw, exec->exception()); \
+ } \
+ if (Collector::outOfMemory()) \
+ return Completion(Throw, Error::create(exec,GeneralError,"Out of memory"));
+
+#define KJS_CHECKEXCEPTIONVALUE \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return exec->exception(); \
+ } \
+ if (Collector::outOfMemory()) \
+ return Undefined(); // will be picked up by KJS_CHECKEXCEPTION
+
+#define KJS_CHECKEXCEPTIONREFERENCE \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ return Reference::makeValueReference(Undefined()); \
+ } \
+ if (Collector::outOfMemory()) \
+ return Reference::makeValueReference(Undefined()); // will be picked up by KJS_CHECKEXCEPTION
+
+#define KJS_CHECKEXCEPTIONLIST \
+ if (exec->hadException()) { \
+ setExceptionDetailsIfNeeded(exec); \
+ 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) const
+{
+ Value v = evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+ return Reference::makeValueReference(v);
+}
+
+// fallback for those nodes without a evaluate() reimplementation
+// TODO: reimplemint in each sub class, make Node::evaluate() pure virtual
+Value Node::evaluate(ExecState *exec) const
+{
+ // fprintf(stderr, "%s::evaluate()\n", typeid(*this).name());
+ return evaluateReference(exec).getValue(exec);
+}
+
+bool Node::toBoolean(ExecState *exec) const
+{
+// fprintf(stderr, "Node(%s)::toBoolean()\n", typeid(*this).name());
+ return evaluate(exec).toBoolean(exec);
+}
+
+double Node::toNumber(ExecState *exec) const
+{
+// fprintf(stderr, "Node(%s)::toNumber()\n", typeid(*this).name());
+ return evaluate(exec).toNumber(exec);
+}
+
+UString Node::toString(ExecState *exec) const
+{
+ return evaluate(exec).toString(exec);
+}
+
+#ifdef KJS_DEBUG_MEM
+void Node::finalCheck()
+{
+ if (!s_nodes) {
+ fprintf(stderr, "Node::finalCheck(): list 0\n");
+ return;
+ }
+ fprintf( stderr, "Node::finalCheck(): list count : %d\n", (int)s_nodes->size() );
+ std::list<Node *>::iterator it = s_nodes->begin();
+ for ( uint i = 0; it != s_nodes->end() ; ++it, ++i )
+ 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) const
+{
+ Object err = Error::create(exec, e, msg, lineNo(), sourceId());
+ exec->setException(err);
+ return err;
+}
+
+Value Node::throwError(ExecState *exec, ErrorType e, const char *msg,
+ const Value &v, const Node *expr) const
+{
+ char *vStr = strdup(v.toString(exec).ascii());
+ char *exprStr = strdup(expr->toCode().ascii());
+
+ int length = strlen(msg) - 4 /* two %s */ + strlen(vStr) + strlen(exprStr) + 1 /* null terminator */;
+ char *str = new char[length];
+ sprintf(str, msg, vStr, exprStr);
+ free(vStr);
+ free(exprStr);
+
+ Value result = throwError(exec, e, str);
+ delete [] str;
+
+ return result;
+}
+
+Value Node::throwError(ExecState *exec, ErrorType e, const char *msg, Identifier label) const
+{
+ const char *l = label.ascii();
+ int length = strlen(msg) - 2 /* %s */ + strlen(l) + 1 /* null terminator */;
+ char *message = new char[length];
+ sprintf(message, msg, l);
+
+ Value result = throwError(exec, e, message);
+ delete [] message;
+
+ return result;
+}
+
+
+void Node::setExceptionDetailsIfNeeded(ExecState *exec) const
+{
+ if (exec->hadException()) {
+ Object exception = exec->exception().toObject(exec);
+ if (!exception.hasProperty(exec, "line") /* &&
+ !exception.hasProperty(exec, "sourceURL")*/ ) {
+ exception.put(exec, "line", Number(line));
+// exception.put(exec, "sourceURL", String(sourceURL));
+ }
+ }
+}
+
+// ----------------------------- StatementNode --------------------------------
+StatementNode::StatementNode() : l0(-1), l1(-1), sourceCode(0), breakPoint(false)
+{
+}
+
+StatementNode::~StatementNode()
+{
+ if (sourceCode)
+ sourceCode->deref();
+}
+
+void StatementNode::setLoc(int line0, int line1, SourceCode *src)
+{
+ // ### require these to be passed to the constructor
+ l0 = line0;
+ l1 = line1;
+ if (sourceCode != src) {
+ if (sourceCode)
+ sourceCode->deref();
+ sourceCode = src;
+ sourceCode->ref();
+ }
+}
+
+// return true if the debugger wants us to stop at this point
+bool StatementNode::hitStatement(ExecState *exec)
+{
+ assert(sourceCode);
+ assert(exec->context().imp()->sourceId == sourceCode->sid);
+ exec->context().imp()->setLines(l0,l1);
+ Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
+ if (dbg)
+ return dbg->atStatement(exec);
+ else
+ return true; // continue
+}
+
+// return true if the debugger wants us to stop at this point
+bool StatementNode::abortStatement(ExecState *exec)
+{
+ Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
+ if (dbg)
+ return dbg->imp()->aborted();
+ else
+ return false;
+}
+
+void StatementNode::processFuncDecl(ExecState *)
+{
+}
+
+// ----------------------------- NullNode -------------------------------------
+
+Value NullNode::evaluate(ExecState *) const
+{
+ return Null();
+}
+
+bool NullNode::toBoolean(ExecState *) const
+{
+ return false;
+}
+
+double NullNode::toNumber(ExecState *) const
+{
+ return 0.0;
+}
+
+UString NullNode::toString(ExecState *) const
+{
+ return "null";
+}
+
+// ----------------------------- BooleanNode ----------------------------------
+
+Value BooleanNode::evaluate(ExecState *) const
+{
+ return Boolean(val);
+}
+
+bool BooleanNode::toBoolean(ExecState *) const
+{
+ return val;
+}
+
+double BooleanNode::toNumber(ExecState *) const
+{
+ return val ? 1.0 : 0.0;
+}
+
+UString BooleanNode::toString(ExecState *) const
+{
+ return val ? "true" : "false";
+}
+
+// ----------------------------- NumberNode -----------------------------------
+
+Value NumberNode::evaluate(ExecState *) const
+{
+ return Number(val);
+}
+
+bool NumberNode::toBoolean(ExecState *) const
+{
+ return !((val == 0) /* || (iVal() == N0) */ || isNaN(val));
+}
+
+double NumberNode::toNumber(ExecState *) const
+{
+ return val;
+}
+
+UString NumberNode::toString(ExecState *) const
+{
+ return UString::from(val);
+}
+
+// ----------------------------- StringNode -----------------------------------
+
+Value StringNode::evaluate(ExecState *) const
+{
+ return String(val);
+}
+
+bool StringNode::toBoolean(ExecState *) const
+{
+ return !val.isEmpty();
+}
+
+double StringNode::toNumber(ExecState *) const
+{
+ return val.toDouble();
+}
+
+UString StringNode::toString(ExecState *) const
+{
+ return val;
+}
+
+// ----------------------------- RegExpNode -----------------------------------
+
+Value RegExpNode::evaluate(ExecState *exec) const
+{
+ List list;
+ String p(pattern);
+ String f(flags);
+ list.append(p);
+ list.append(f);
+
+ Object reg = exec->lexicalInterpreter()->imp()->builtinRegExp();
+ return reg.construct(exec,list);
+}
+
+bool RegExpNode::toBoolean(ExecState *) const
+{
+ return true;
+}
+
+// ----------------------------- ThisNode -------------------------------------
+
+// ECMA 11.1.1
+Value ThisNode::evaluate(ExecState *exec) const
+{
+ return exec->context().imp()->thisValue();
+}
+
+// ----------------------------- ResolveNode ----------------------------------
+
+// ECMA 11.1.2 & 10.1.4
+Value ResolveNode::evaluate(ExecState *exec) const
+{
+ return evaluateReference(exec).getValue(exec);
+}
+
+Reference ResolveNode::evaluateReference(ExecState *exec) const
+{
+ ScopeChain chain = exec->context().imp()->scopeChain();
+
+ while (!chain.isEmpty()) {
+ ObjectImp *o = chain.top();
+
+ //cerr << "Resolve: looking at '" << ident.ascii() << "'"
+ // << " in " << (void*)o << " " << o->classInfo()->className << endl;
+ if (o->hasProperty(exec,ident)) {
+ //cerr << "Resolve: FOUND '" << ident.ascii() << "'"
+ // << " in " << (void*)o << " " << o->classInfo()->className << endl;
+ return Reference(o, ident);
+ }
+
+ chain.pop();
+ }
+
+ // identifier not found
+#ifdef KJS_VERBOSE
+ cerr << "Resolve::evaluateReference: didn't find '" << ident.ustring().ascii() << "'" << endl;
+#endif
+ 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) const
+{
+ return group->evaluate(exec);
+}
+
+Reference GroupNode::evaluateReference(ExecState *exec) const
+{
+ return group->evaluateReference(exec);
+}
+
+// ----------------------------- ElementNode ----------------------------------
+
+void ElementNode::ref()
+{
+ for (ElementNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->node)
+ n->node->ref();
+ }
+}
+
+bool ElementNode::deref()
+{
+ ElementNode *next;
+ for (ElementNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->node && n->node->deref())
+ delete n->node;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+// ECMA 11.1.4
+Value ElementNode::evaluate(ExecState *exec) const
+{
+ Object array = exec->lexicalInterpreter()->builtinArray().construct(exec, List::empty());
+ int length = 0;
+ for (const ElementNode *n = this; n; n = n->list) {
+ Value val = n->node->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ length += n->elision;
+ array.put(exec, length++, val);
+ }
+ return array;
+}
+
+// ----------------------------- ArrayNode ------------------------------------
+
+void ArrayNode::ref()
+{
+ Node::ref();
+ if ( element )
+ element->ref();
+}
+
+bool ArrayNode::deref()
+{
+ if ( element && element->deref() )
+ delete element;
+ return Node::deref();
+}
+
+// ECMA 11.1.4
+Value ArrayNode::evaluate(ExecState *exec) const
+{
+ Object array;
+ int length;
+
+ 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->lexicalInterpreter()->builtinArray().construct(exec,List::empty());
+ array = Object(static_cast<ObjectImp*>(newArr.imp()));
+ length = 0;
+ }
+
+ if (opt)
+ array.put(exec,lengthPropertyName, Number(elision + 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) const
+{
+ if (list)
+ return list->evaluate(exec);
+
+ return exec->lexicalInterpreter()->builtinObject().construct(exec,List::empty());
+}
+
+// ----------------------------- PropertyValueNode ----------------------------
+
+void PropertyValueNode::ref()
+{
+ for (PropertyValueNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->name)
+ n->name->ref();
+ if (n->assign)
+ n->assign->ref();
+ }
+}
+
+bool PropertyValueNode::deref()
+{
+ PropertyValueNode *next;
+ for (PropertyValueNode *n = this; n; n = next) {
+ next = n->list;
+ if ( n->name && n->name->deref() )
+ delete n->name;
+ if ( n->assign && n->assign->deref() )
+ delete n->assign;
+ if (n != this && n->Node::deref() )
+ delete n;
+ }
+ return Node::deref();
+}
+
+// ECMA 11.1.5
+Value PropertyValueNode::evaluate(ExecState *exec) const
+{
+ Object obj = exec->lexicalInterpreter()->builtinObject().construct(exec, List::empty());
+
+ for (const PropertyValueNode *p = this; p; p = p->list) {
+ Value n = p->name->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = p->assign->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ obj.put(exec, Identifier(n.toString(exec)), v);
+ }
+
+ return obj;
+}
+
+// ----------------------------- PropertyNode ---------------------------------
+
+// ECMA 11.1.5
+Value PropertyNode::evaluate(ExecState * /*exec*/) const
+{
+ 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
+Reference AccessorNode1::evaluateReference(ExecState *exec) const
+{
+ Value v1 = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+ Value v2 = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+#ifndef NDEBUG
+ // catch errors before being caught in toObject(). better error message.
+ if (v1.isA(UndefinedType) || v1.isA(NullType)) {
+ UString s = "Attempted to access property on %s object "
+ "(result of expression %s)";
+ (void)throwError(exec, TypeError, s.cstring().c_str(), v1, this);
+ return Reference::makeValueReference(Undefined());
+ }
+#endif
+ Object o = v1.toObject(exec);
+ unsigned i;
+ if (v2.toUInt32(i))
+ return Reference(o, i);
+ UString s = v2.toString(exec);
+ return Reference(o, Identifier(s));
+}
+
+// ----------------------------- 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
+Reference AccessorNode2::evaluateReference(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONREFERENCE
+ assert(v.isValid());
+#ifndef NDEBUG
+ // catch errors before being caught in toObject(). better error message.
+ if (v.isA(UndefinedType) || v.isA(NullType)) {
+ UString s = "Attempted to access '" + ident.ustring() +
+ "' property on %s object (result of expression %s)";
+ (void)throwError(exec, TypeError, s.cstring().c_str(), v, this);
+ return Reference::makeValueReference(Undefined());
+ }
+#endif
+ Object o = v.toObject(exec);
+ return Reference(o, ident);
+}
+
+// ----------------------------- ArgumentListNode -----------------------------
+
+void ArgumentListNode::ref()
+{
+ for (ArgumentListNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->expr)
+ n->expr->ref();
+ }
+}
+
+bool ArgumentListNode::deref()
+{
+ ArgumentListNode *next;
+ for (ArgumentListNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->expr && n->expr->deref())
+ delete n->expr;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+Value ArgumentListNode::evaluate(ExecState * /*exec*/) const
+{
+ assert(0);
+ return Value(); // dummy, see evaluateList()
+}
+
+// ECMA 11.2.4
+List ArgumentListNode::evaluateList(ExecState *exec) const
+{
+ List l;
+
+ for (const ArgumentListNode *n = this; n; n = n->list) {
+ Value v = n->expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONLIST
+ l.append(v);
+ }
+
+ return l;
+}
+
+// ----------------------------- ArgumentsNode --------------------------------
+
+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*/) const
+{
+ assert(0);
+ return Value(); // dummy, see evaluateList()
+}
+
+// ECMA 11.2.4
+List ArgumentsNode::evaluateList(ExecState *exec) const
+{
+ 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) const
+{
+ 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, "Value %s (result of expression %s) is not an object. Cannot be used with new.", v, expr);
+ }
+
+ Object constr = Object(static_cast<ObjectImp*>(v.imp()));
+ if (!constr.implementsConstruct()) {
+ return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, expr);
+ }
+
+ 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) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ List argList = args->evaluateList(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v = ref.getValue(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ if (v.type() != ObjectType) {
+ return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be called.", v, expr);
+ }
+
+ Object func = Object(static_cast<ObjectImp*>(v.imp()));
+
+ if (!func.implementsCall()) {
+ return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, expr);
+ }
+
+ Value thisVal;
+ 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->dynamicInterpreter()->globalObject();
+ }
+
+ Object thisObj = Object::dynamicCast(thisVal);
+ Value result = func.call(exec,thisObj, argList);
+
+ 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) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = ref.getValue(exec);
+ double n = v.toNumber(exec);
+
+ double newValue = (oper == OpPlusPlus) ? n + 1 : n - 1;
+
+ ref.putValue(exec, Number(newValue));
+
+ return Number(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) const
+{
+ 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) const
+{
+ 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
+{
+ const char *s = 0L;
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ 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) const
+{
+ Reference ref = expr->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = ref.getValue(exec);
+ double n = v.toNumber(exec);
+
+ double newValue = (oper == OpPlusPlus) ? n + 1 : n - 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
+double UnaryPlusNode::toNumber(ExecState *exec) const
+{
+ return expr->toNumber(exec);
+}
+
+// could go
+Value UnaryPlusNode::evaluate(ExecState *exec) const
+{
+ 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
+double NegateNode::toNumber(ExecState *exec) const
+{
+ return -expr->toNumber(exec);
+}
+
+Value NegateNode::evaluate(ExecState *exec) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ double d = -v.toNumber(exec);
+
+ 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) const
+{
+ 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
+bool LogicalNotNode::toBoolean(ExecState *exec) const
+{
+ return !expr->toBoolean(exec);
+}
+
+// could remove this
+Value LogicalNotNode::evaluate(ExecState *exec) const
+{
+ bool b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ 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) const
+{
+ Value v1 = term1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v2 = term2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return mult(exec,v1, v2, oper);
+}
+
+// ----------------------------- AddNode --------------------------------------
+
+// factory for an appropriate addition or substraction node
+Node* AddNode::create(Node *t1, Node *t2, char op)
+{
+ // ### many more combinations to check for
+ // fold constants
+ if ((t1->type() == NumberType || t1->type() == BooleanType) &&
+ (t2->type() == NumberType || t2->type() == BooleanType)) {
+ double d = t2->toNumber(0);
+ Node* n = new NumberNode(t1->toNumber(0) + (op == '+' ? d : -d));
+ delete t1;
+ delete t2;
+ return n;
+ }
+
+ if (op == '+' && t2->type() == StringType)
+ return new AppendStringNode(t1, t2->toString(0));
+
+ // fall back to generic node
+ return new AddNode(t1, t2, op);
+}
+
+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) const
+{
+ Value v1 = term1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v2 = term2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return add(exec,v1, v2, oper);
+}
+
+// ------------------------ AddNumberNode ------------------------------------
+
+void AppendStringNode::ref()
+{
+ Node::ref();
+ term->ref();
+}
+
+bool AppendStringNode::deref()
+{
+ if (term->deref())
+ delete term;
+ return Node::deref();
+}
+
+// ECMA 11.6 (special case of string appending)
+Value AppendStringNode::evaluate(ExecState *exec) const
+{
+ UString s = term->toString(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return String(s + str);
+}
+
+// ----------------------------- 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) const
+{
+ Value v1 = term1->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value v2 = term2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ unsigned int i2 = v2.toUInt32(exec);
+ i2 &= 0x1f;
+
+ switch (oper) {
+ case OpLShift:
+ return Number(v1.toInt32(exec) << i2);
+ case OpRShift:
+ return Number(v1.toInt32(exec) >> i2);
+ case OpURShift:
+ return Number(v1.toUInt32(exec) >> i2);
+ default:
+ assert(!"ShiftNode: unhandled switch case");
+ return Undefined();
+ }
+}
+
+// ----------------------------- 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) const
+{
+ 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,
+ "Value %s (result of expression %s) is not an object. Cannot be used with IN expression.", v2, expr2);
+ Object o2(static_cast<ObjectImp*>(v2.imp()));
+ b = o2.hasProperty(exec,Identifier(v1.toString(exec)));
+ } else {
+ if (v2.type() != ObjectType)
+ return throwError(exec, TypeError,
+ "Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, expr2);
+
+ 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) const
+{
+ 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) const
+{
+ 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) const
+{
+ 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) const
+{
+ bool b = logical->toBoolean(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ Value v = b ? expr1->evaluate(exec) : 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) const
+{
+ Reference l = left->evaluateReference(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ Value 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;
+ int i2;
+ 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:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 << i2);
+ break;
+ case OpRShift:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 >> i2);
+ break;
+ case OpURShift:
+ ui = v1.toUInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(ui >> i2);
+ break;
+ case OpAndEq:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 & i2);
+ break;
+ case OpXOrEq:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ v = Number(i1 ^ i2);
+ break;
+ case OpOrEq:
+ i1 = v1.toInt32(exec);
+ i2 = v2.toInt32(exec);
+ 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) const
+{
+ (void) expr1->evaluate(exec); // ignore return value
+ KJS_CHECKEXCEPTIONVALUE
+ Value v = expr2->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v;
+}
+
+// ----------------------------- StatListNode ---------------------------------
+
+StatListNode::StatListNode(StatementNode *s)
+ : statement(s), list(this)
+{
+ setLoc(s->firstLine(), s->lastLine(), s->code());
+}
+
+StatListNode::StatListNode(StatListNode *l, StatementNode *s)
+ : statement(s), list(l->list)
+{
+ l->list = this;
+ setLoc(l->firstLine(),s->lastLine(),l->code());
+}
+
+void StatListNode::ref()
+{
+ for (StatListNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->statement)
+ n->statement->ref();
+ }
+}
+
+bool StatListNode::deref()
+{
+ StatListNode *next;
+ for (StatListNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->statement && n->statement->deref())
+ delete n->statement;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return StatementNode::deref();
+}
+
+// ECMA 12.1
+Completion StatListNode::execute(ExecState *exec)
+{
+ Completion c = statement->execute(exec);
+ KJS_ABORTPOINT
+ if (exec->hadException()) {
+ Value ex = exec->exception();
+ exec->clearException();
+ return Completion(Throw, ex);
+ }
+
+ if (c.complType() != Normal)
+ return c;
+
+ Value v = c.value();
+
+ for (StatListNode *n = list; n; n = n->list) {
+ Completion c2 = n->statement->execute(exec);
+ KJS_ABORTPOINT
+ if (c2.complType() != Normal)
+ return c2;
+
+ if (exec->hadException()) {
+ Value ex = exec->exception();
+ exec->clearException();
+ return Completion(Throw, ex);
+ }
+
+ if (c2.isValueCompletion())
+ v = c2.value();
+ c = c2;
+ }
+
+ return Completion(c.complType(), v, c.target());
+}
+
+void StatListNode::processVarDecls(ExecState *exec)
+{
+ for (StatListNode *n = this; n; n = n->list)
+ n->statement->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) const
+{
+ return expr->evaluate(exec);
+}
+
+// ----------------------------- VarDeclNode ----------------------------------
+
+VarDeclNode::VarDeclNode(const Identifier &id, AssignExprNode *in, Type t)
+ : varType(t), 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) const
+{
+ Object variable = Object::dynamicCast(exec->context().imp()->variableObject());
+
+ Value val;
+ if (init) {
+ val = init->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ } else {
+ // ### check attributes? reuse check done in processVarDecls()?
+ if (variable.imp()->getDirect(ident)) // already declared ?
+ return Value();
+ val = Undefined();
+ }
+
+#ifdef KJS_VERBOSE
+ printInfo(exec,(UString("new variable ")+ident.ustring()).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.
+ int flags = Internal;
+ if (exec->context().imp()->codeType() != EvalCode)
+ flags |= DontDelete;
+ if (varType == VarDeclNode::Constant)
+ flags |= ReadOnly;
+ variable.put(exec, ident, val, flags);
+
+ // the spec wants us to return the name of the identifier here
+ // but we'll save the construction and copying as the return
+ // value isn't used by the caller
+ return Value();
+}
+
+void VarDeclNode::processVarDecls(ExecState *exec)
+{
+ Object variable = exec->context().variableObject();
+ // ### use getDirect()? Check attributes?
+ // ### avoid duplication with actions performed in evaluate()?
+ if ( !variable.hasProperty( exec, ident ) ) { // already declared ?
+ int flags = None;
+ if (exec->_context->codeType() != EvalCode)
+ flags |= DontDelete;
+ if (varType == VarDeclNode::Constant)
+ flags |= ReadOnly;
+ // TODO: check for forbidden redeclaration of consts
+ variable.put(exec, ident, Undefined(), flags);
+ }
+}
+
+// ----------------------------- VarDeclListNode ------------------------------
+
+void VarDeclListNode::ref()
+{
+ for (VarDeclListNode *n = this; n; n = n->list) {
+ n->Node::ref();
+ if (n->var)
+ n->var->ref();
+ }
+}
+
+bool VarDeclListNode::deref()
+{
+ VarDeclListNode *next;
+ for (VarDeclListNode *n = this; n; n = next) {
+ next = n->list;
+ if (n->var && n->var->deref())
+ delete n->var;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+
+// ECMA 12.2
+Value VarDeclListNode::evaluate(ExecState *exec) const
+{
+ for (const VarDeclListNode *n = this; n; n = n->list) {
+ (void)n->var->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+ }
+ return Undefined();
+}
+
+void VarDeclListNode::processVarDecls(ExecState *exec)
+{
+ for (VarDeclListNode *n = this; n; n = n->list)
+ n->var->processVarDecls(exec);
+}
+
+// ----------------------------- VarStatementNode -----------------------------
+
+void VarStatementNode::ref()
+{
+ StatementNode::ref();
+ if ( list )
+ list->ref();
+}
+
+bool VarStatementNode::deref()
+{
+ if ( list && list->deref() )
+ delete list;
+ return StatementNode::deref();
+}
+
+// ECMA 12.2
+Completion VarStatementNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ (void) list->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ return Completion(Normal);
+}
+
+void VarStatementNode::processVarDecls(ExecState *exec)
+{
+ list->processVarDecls(exec);
+}
+
+// ----------------------------- BlockNode ------------------------------------
+
+BlockNode::BlockNode(SourceElementsNode *s)
+{
+ if (s) {
+ source = s->elements;
+ s->elements = 0;
+ setLoc(s->firstLine(), s->lastLine(), s->code());
+ } else {
+ source = 0;
+ }
+}
+
+void BlockNode::ref()
+{
+ StatementNode::ref();
+ if ( source )
+ source->ref();
+}
+
+bool BlockNode::deref()
+{
+ if ( source && source->deref() )
+ delete source;
+ return StatementNode::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()
+{
+ StatementNode::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool ExprStatementNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::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()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.5
+Completion IfNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ assert(expr);
+ bool b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+
+ // 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()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.6.1
+Completion DoWhileNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Completion c;
+ Value value;
+ bool b;
+
+ do {
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ 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;
+ }
+ b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+ } while (b);
+
+ return Completion(Normal, value);
+}
+
+void DoWhileNode::processVarDecls(ExecState *exec)
+{
+ statement->processVarDecls(exec);
+}
+
+// ----------------------------- WhileNode ------------------------------------
+
+void WhileNode::ref()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.6.2
+Completion WhileNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Completion c;
+ Value value;
+
+ while (1) {
+ bool b = expr->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ if (!b)
+ return Completion(Normal, value);
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ 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()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.6.3
+Completion ForNode::execute(ExecState *exec)
+{
+ Value v, cval;
+
+ if (expr1) {
+ v = expr1->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ }
+ for (;;) {
+ if (expr2) {
+ bool b = expr2->toBoolean(exec);
+ KJS_CHECKEXCEPTION
+ if (!b)
+ return Completion(Normal, cval);
+ }
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ Completion c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ 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, VarDeclNode::Variable);
+ lexpr = new ResolveNode(ident);
+}
+
+void ForInNode::ref()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.6.4
+Completion ForInNode::execute(ExecState *exec)
+{
+ Value retval;
+ Completion c;
+
+ if ( varDecl ) {
+ (void)varDecl->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ }
+
+ Value v = expr->evaluate(exec);
+ // for Null and Undefined, we want to make sure not to go through
+ // the loop at all, because their object wrappers will have a
+ // property list but will throw an exception if you attempt to
+ // access any property.
+ if (v.isA(NullType) || v.isA(UndefinedType))
+ return Completion(Normal, retval);
+
+ Object o = v.toObject(exec);
+ KJS_CHECKEXCEPTION
+ ReferenceList propList = o.propList(exec);
+
+ ReferenceListIterator propIt = propList.begin();
+
+ while (propIt != propList.end()) {
+ Identifier name = propIt->getPropertyName(exec);
+ if (!o.hasProperty(exec,name)) {
+ propIt++;
+ continue;
+ }
+
+ Reference ref = lexpr->evaluateReference(exec);
+ KJS_CHECKEXCEPTION
+ ref.putValue(exec, String(name.ustring()));
+
+ exec->context().imp()->seenLabels()->pushIteration();
+ c = statement->execute(exec);
+ exec->context().imp()->seenLabels()->popIteration();
+ 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;
+
+ if (ident.isEmpty() && !exec->context().imp()->seenLabels()->inIteration())
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "continue used outside of iteration statement"));
+ else if (!ident.isEmpty() && !exec->context().imp()->seenLabels()->contains(ident))
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "Label %s not found in containing block. Can't continue.", ident));
+ else
+ return Completion(Continue, dummy, ident);
+}
+
+// ----------------------------- BreakNode ------------------------------------
+
+// ECMA 12.8
+Completion BreakNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value dummy;
+
+ if (ident.isEmpty() && !exec->context().imp()->seenLabels()->inIteration() &&
+ !exec->context().imp()->seenLabels()->inSwitch())
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "break used outside of iteration or switch statement"));
+ else if (!ident.isEmpty() && !exec->context().imp()->seenLabels()->contains(ident))
+ return Completion(Throw,
+ throwError(exec, SyntaxError, "Label %s not found in containing block. Can't break.", ident));
+ else
+ return Completion(Break, dummy, ident);
+}
+
+// ----------------------------- ReturnNode -----------------------------------
+
+void ReturnNode::ref()
+{
+ StatementNode::ref();
+ if ( value )
+ value->ref();
+}
+
+bool ReturnNode::deref()
+{
+ if ( value && value->deref() )
+ delete value;
+ return StatementNode::deref();
+}
+
+// ECMA 12.9
+Completion ReturnNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ CodeType codeType = exec->context().imp()->codeType();
+ if (codeType != FunctionCode) {
+ return Completion(Throw, throwError(exec, SyntaxError, "Invalid return statement."));
+ }
+
+ if (!value)
+ return Completion(ReturnValue, Undefined());
+
+ Value v = value->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ return Completion(ReturnValue, v);
+}
+
+// ----------------------------- WithNode -------------------------------------
+
+void WithNode::ref()
+{
+ StatementNode::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 StatementNode::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) const
+{
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTIONVALUE
+
+ return v;
+}
+
+// ECMA 12.11
+Completion CaseClauseNode::evalStatements(ExecState *exec) const
+{
+ 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()
+{
+ for (ClauseListNode *n = this; n; n = n->nx) {
+ n->Node::ref();
+ if (n->cl)
+ n->cl->ref();
+ }
+}
+
+bool ClauseListNode::deref()
+{
+ ClauseListNode *next;
+ for (ClauseListNode *n = this; n; n = next) {
+ next = n->nx;
+ if (n->cl && n->cl->deref())
+ delete n->cl;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+Value ClauseListNode::evaluate(ExecState * /*exec*/) const
+{
+ /* should never be called */
+ assert(false);
+ return Value();
+}
+
+// ECMA 12.11
+void ClauseListNode::processVarDecls(ExecState *exec)
+{
+ for (ClauseListNode *n = this; n; n = n->nx)
+ if (n->cl)
+ n->cl->processVarDecls(exec);
+}
+
+// ----------------------------- CaseBlockNode --------------------------------
+
+CaseBlockNode::CaseBlockNode(ClauseListNode *l1, CaseClauseNode *d,
+ ClauseListNode *l2)
+{
+ def = d;
+ if (l1) {
+ list1 = l1->nx;
+ l1->nx = 0;
+ } else {
+ list1 = 0;
+ }
+ if (l2) {
+ list2 = l2->nx;
+ l2->nx = 0;
+ } else {
+ list2 = 0;
+ }
+}
+
+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*/) const
+{
+ /* should never be called */
+ assert(false);
+ return Value();
+}
+
+// ECMA 12.11
+Completion CaseBlockNode::evalBlock(ExecState *exec, const Value& input) const
+{
+ 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()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.11
+Completion SwitchNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTION
+ exec->context().imp()->seenLabels()->pushSwitch();
+ Completion res = block->evalBlock(exec,v);
+ exec->context().imp()->seenLabels()->popSwitch();
+
+ 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()
+{
+ StatementNode::ref();
+ if ( statement )
+ statement->ref();
+}
+
+bool LabelNode::deref()
+{
+ if ( statement && statement->deref() )
+ delete statement;
+ return StatementNode::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 %s found.", label));
+ };
+ 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()
+{
+ StatementNode::ref();
+ if ( expr )
+ expr->ref();
+}
+
+bool ThrowNode::deref()
+{
+ if ( expr && expr->deref() )
+ delete expr;
+ return StatementNode::deref();
+}
+
+// ECMA 12.13
+Completion ThrowNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Value v = expr->evaluate(exec);
+ KJS_CHECKEXCEPTION
+
+ // bail out on error
+ KJS_CHECKEXCEPTION
+
+ Debugger *dbg = exec->interpreter()->imp()->debugger();
+ if (dbg)
+ dbg->exception(exec,v,exec->context().imp()->inTryCatch());
+
+ return Completion(Throw, v);
+}
+
+// ----------------------------- CatchNode ------------------------------------
+
+void CatchNode::ref()
+{
+ StatementNode::ref();
+ if ( block )
+ block->ref();
+}
+
+bool CatchNode::deref()
+{
+ if ( block && block->deref() )
+ delete block;
+ return StatementNode::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()
+{
+ StatementNode::ref();
+ if ( block )
+ block->ref();
+}
+
+bool FinallyNode::deref()
+{
+ if ( block && block->deref() )
+ delete block;
+ return StatementNode::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()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 12.14
+Completion TryNode::execute(ExecState *exec)
+{
+ KJS_BREAKPOINT;
+
+ Completion c, c2;
+
+ if (_catch)
+ exec->context().imp()->pushTryCatch();
+ c = block->execute(exec);
+ if (_catch)
+ exec->context().imp()->popTryCatch();
+
+ if (!_final) {
+ if (c.complType() != Throw)
+ return c;
+ return _catch->execute(exec,c.value());
+ }
+
+ if (!_catch) {
+ Value exception = exec->_exception;
+ exec->_exception = Value();
+
+ c2 = _final->execute(exec);
+
+ if (!exec->hadException() && c2.complType() != Throw)
+ exec->_exception = exception;
+
+ 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()
+{
+ for (ParameterNode *n = this; n; n = n->next)
+ n->Node::ref();
+}
+
+bool ParameterNode::deref()
+{
+ ParameterNode *next;
+ for (ParameterNode *n = this; n; n = next) {
+ next = n->next;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return Node::deref();
+}
+
+// ECMA 13
+Value ParameterNode::evaluate(ExecState * /*exec*/) const
+{
+ return Undefined();
+}
+
+// ----------------------------- FunctionBodyNode -----------------------------
+
+
+FunctionBodyNode::FunctionBodyNode(SourceElementsNode *s)
+ : BlockNode(s)
+{
+ //fprintf(stderr,"FunctionBodyNode::FunctionBodyNode %p\n",this);
+}
+
+void FunctionBodyNode::processFuncDecl(ExecState *exec)
+{
+ if (source)
+ source->processFuncDecl(exec);
+}
+
+// ----------------------------- FuncDeclNode ---------------------------------
+
+void FuncDeclNode::ref()
+{
+ StatementNode::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 StatementNode::deref();
+}
+
+// ECMA 13
+void FuncDeclNode::processFuncDecl(ExecState *exec)
+{
+ ContextImp *ctx = exec->context().imp();
+ // TODO: let this be an object with [[Class]] property "Function"
+ FunctionImp *fimp = new DeclaredFunctionImp(exec, ident, body, exec->context().imp()->scopeChain());
+ Object func(fimp); // protect from GC
+
+ // Value proto = exec->lexicalInterpreter()->builtinObject().construct(exec,List::empty());
+ List empty;
+ Object proto = exec->lexicalInterpreter()->builtinObject().construct(exec,empty);
+ proto.put(exec, constructorPropertyName, func, ReadOnly|DontDelete|DontEnum);
+ func.put(exec, prototypePropertyName, proto, Internal|DontDelete);
+
+ int plen = 0;
+ for(const ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
+ fimp->addParameter(p->ident());
+
+ func.put(exec, lengthPropertyName, Number(plen), ReadOnly|DontDelete|DontEnum);
+
+#ifdef KJS_VERBOSE
+ fprintf(stderr,"KJS: new function %s in %p\n", ident.ustring().cstring().c_str(), ctx->variableObject().imp());
+#endif
+ if (exec->_context->codeType() == EvalCode) {
+ // ECMA 10.2.2
+ ctx->variableObject().put(exec, ident, func, Internal);
+ } else {
+ ctx->variableObject().put(exec, ident, func, DontDelete | Internal);
+ }
+
+ 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 = ctx->variableObject();
+ ctx->setVariableObject(func);
+ ctx->pushScope(func);
+ body->processFuncDecl(exec);
+ ctx->popScope();
+ ctx->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
+{
+ ContextImp *context = exec->context().imp();
+ bool named = !ident.isNull();
+ Object functionScopeObject;
+
+ if (named) {
+ // named FunctionExpressions can recursively call themselves,
+ // but they won't register with the current scope chain and should
+ // be contained as single property in an anonymous object.
+ functionScopeObject = Object(new ObjectImp());
+ context->pushScope(functionScopeObject);
+ }
+
+ FunctionImp *fimp = new DeclaredFunctionImp(exec, Identifier::null(), body, exec->context().imp()->scopeChain());
+ Value ret(fimp);
+ List empty;
+ Value proto = exec->lexicalInterpreter()->builtinObject().construct(exec,empty);
+ fimp->put(exec, prototypePropertyName, proto, Internal|DontDelete);
+
+ for(const ParameterNode *p = param; p != 0L; p = p->nextParam())
+ fimp->addParameter(p->ident());
+
+ if (named) {
+ functionScopeObject.put(exec, ident, Value(fimp), ReadOnly|DontDelete);
+ context->popScope();
+ }
+
+ return ret;
+}
+
+// ----------------------------- SourceElementsNode ---------------------------
+
+SourceElementsNode::SourceElementsNode(StatementNode *s1)
+{
+ element = s1;
+ elements = this;
+ setLoc(s1->firstLine(), s1->lastLine(), s1->code());
+}
+
+SourceElementsNode::SourceElementsNode(SourceElementsNode *s1, StatementNode *s2)
+{
+ elements = s1->elements;
+ s1->elements = this;
+ element = s2;
+ setLoc(s1->firstLine(), s2->lastLine(), s1->code());
+}
+
+void SourceElementsNode::ref()
+{
+ for (SourceElementsNode *n = this; n; n = n->elements) {
+ n->Node::ref();
+ if (n->element)
+ n->element->ref();
+ }
+}
+
+bool SourceElementsNode::deref()
+{
+ SourceElementsNode *next;
+ for (SourceElementsNode *n = this; n; n = next) {
+ next = n->elements;
+ if (n->element && n->element->deref())
+ delete n->element;
+ if (n != this && n->Node::deref())
+ delete n;
+ }
+ return StatementNode::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 *n = elements; n; n = n->elements) {
+ Completion c2 = n->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().isValid())
+ c1 = c2;
+ }
+
+ return c1;
+}
+
+// ECMA 14
+void SourceElementsNode::processFuncDecl(ExecState *exec)
+{
+ for (SourceElementsNode *n = this; n; n = n->elements)
+ n->element->processFuncDecl(exec);
+}
+
+void SourceElementsNode::processVarDecls(ExecState *exec)
+{
+ for (SourceElementsNode *n = this; n; n = n->elements)
+ n->element->processVarDecls(exec);
+}