blob: ab20d3bf5555fd9b20b48ad1be5b06e74f4b7128 [file] [log] [blame]
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
*
* 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 <assert.h>
#include <stdio.h>
#include <math.h>
#include "kjs.h"
#include "ustring.h"
#include "lexer.h"
#include "types.h"
#include "internal.h"
#include "operations.h"
#include "regexp_object.h"
#include "debugger.h"
using namespace KJS;
#ifdef KJS_DEBUGGER
#define KJS_BREAKPOINT if (!hitStatement()) return Completion(Normal);
#define KJS_ABORTPOINT if (abortStatement()) return Completion(Normal);
#else
#define KJS_BREAKPOINT
#define KJS_ABORTPOINT
#endif
int Node::nodeCount = 0;
Node* Node::first = 0;
Node::Node()
{
assert(Lexer::curr());
line = Lexer::curr()->lineNo();
nodeCount++;
// cout << "Node()" << endl;
// create a list of allocated objects. Makes
// deleting (even after a parse error) quite easy
next = first;
prev = 0L;
if (first)
first->prev = this;
first = this;
}
Node::~Node()
{
// cout << "~Node()" << endl;
if (next)
next->prev = prev;
if (prev)
prev->next = next;
nodeCount--;
}
void Node::deleteAllNodes()
{
Node *tmp, *n = first;
while ((tmp = n)) {
n = n->next;
delete tmp;
}
first = 0L;
// assert(nodeCount == 0);
}
KJSO Node::throwError(ErrorType e, const char *msg)
{
return Error::create(e, msg, lineNo());
}
#ifdef KJS_DEBUGGER
void StatementNode::setLoc(int line0, int line1)
{
l0 = line0;
l1 = line1;
sid = KJScriptImp::current()->sourceId();
}
bool StatementNode::hitStatement()
{
if (KJScriptImp::current()->debugger())
return KJScriptImp::current()->debugger()->hit(firstLine(), breakPoint);
else
return true;
}
// return true if the debugger wants us to stop at this point
bool StatementNode::abortStatement()
{
if (KJScriptImp::current()->debugger() &&
KJScriptImp::current()->debugger()->mode() == Debugger::Stop)
return true;
return false;
}
bool Node::setBreakpoint(Node *firstNode, int id, int line, bool set)
{
while (firstNode) {
if (firstNode->setBreakpoint(id, line, set) && line >= 0) // line<0 for all
return true;
firstNode = firstNode->next;
}
return false;
}
/**
* Try to set or delete a breakpoint depending on the value of set.
* The call will return true if successful, i.e. if line is inside
* of the statement's range. Additionally, a breakpoint had to
* be set already if you tried to delete with set=false.
*/
bool StatementNode::setBreakpoint(int id, int line, bool set)
{
// in our source unit and line range ?
if (id != sid || ((line < l0 || line > l1) && line >= 0))
return false;
if (!set && !breakPoint)
return false;
breakPoint = set;
return true;
}
#endif
KJSO NullNode::evaluate()
{
return Null();
}
KJSO BooleanNode::evaluate()
{
return Boolean(value);
}
KJSO NumberNode::evaluate()
{
return Number(value);
}
KJSO StringNode::evaluate()
{
return String(value);
}
KJSO RegExpNode::evaluate()
{
List list;
String p(pattern);
String f(flags);
list.append(p);
list.append(f);
// very ugly
KJSO r = Global::current().get("RegExp");
RegExpObject *r2 = (RegExpObject*)r.imp();
return r2->construct(list);
}
// ECMA 11.1.1
KJSO ThisNode::evaluate()
{
return Context::current()->thisValue();
}
// ECMA 11.1.2 & 10.1.4
KJSO ResolveNode::evaluate()
{
assert(Context::current());
const List *chain = Context::current()->pScopeChain();
assert(chain);
ListIterator scope = chain->begin();
while (scope != chain->end()) {
if (scope->hasProperty(ident)) {
// cout << "Resolve: found '" << ident.ascii() << "'"
// << " type " << scope->get(ident).imp()->typeInfo()->name << endl;
return Reference(*scope, ident);
}
scope++;
}
// identifier not found
// cout << "Resolve: didn't find '" << ident.ascii() << "'" << endl;
return Reference(Null(), ident);
}
// ECMA 11.1.4
KJSO ArrayNode::evaluate()
{
KJSO array;
int length;
int elisionLen = elision ? elision->evaluate().toInt32() : 0;
if (element) {
array = element->evaluate();
length = opt ? array.get("length").toInt32() : 0;
} else {
array = Object::create(ArrayClass);
length = 0;
}
if (opt)
array.put("length", Number(elisionLen + length), DontEnum | DontDelete);
return array;
}
// ECMA 11.1.4
KJSO ElementNode::evaluate()
{
KJSO array, val;
int length = 0;
int elisionLen = elision ? elision->evaluate().toInt32() : 0;
if (list) {
array = list->evaluate();
val = node->evaluate().getValue();
length = array.get("length").toInt32();
} else {
array = Object::create(ArrayClass);
val = node->evaluate().getValue();
}
array.putArrayElement(UString::from(elisionLen + length), val);
return array;
}
// ECMA 11.1.4
KJSO ElisionNode::evaluate()
{
if (elision)
return Number(elision->evaluate().toNumber().value() + 1);
else
return Number(1);
}
// ECMA 11.1.5
KJSO ObjectLiteralNode::evaluate()
{
if (list)
return list->evaluate();
return Object::create(ObjectClass);
}
// ECMA 11.1.5
KJSO PropertyValueNode::evaluate()
{
KJSO obj;
if (list)
obj = list->evaluate();
else
obj = Object::create(ObjectClass);
KJSO n = name->evaluate();
KJSO a = assign->evaluate();
KJSO v = a.getValue();
obj.put(n.toString().value(), v);
return obj;
}
// ECMA 11.1.5
KJSO PropertyNode::evaluate()
{
KJSO s;
if (str.isNull()) {
s = String(UString::from(numeric));
} else
s = String(str);
return s;
}
// ECMA 11.1.6
KJSO GroupNode::evaluate()
{
return group->evaluate();
}
// ECMA 11.2.1a
KJSO AccessorNode1::evaluate()
{
KJSO e1 = expr1->evaluate();
KJSO v1 = e1.getValue();
KJSO e2 = expr2->evaluate();
KJSO v2 = e2.getValue();
Object o = v1.toObject();
String s = v2.toString();
return Reference(o, s.value());
}
// ECMA 11.2.1b
KJSO AccessorNode2::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
KJSO o = v.toObject();
return Reference(o, ident);
}
// ECMA 11.2.2
KJSO NewExprNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
List *argList = args ? args->evaluateList() : 0;
if (!v.isObject()) {
delete argList;
return throwError(TypeError, "Expression is no object. Cannot be new'ed");
}
Constructor constr = Constructor::dynamicCast(v);
if (constr.isNull()) {
delete argList;
return throwError(TypeError, "Expression is no constructor.");
}
if (!argList)
argList = new List;
KJSO res = constr.construct(*argList);
delete argList;
return res;
}
// ECMA 11.2.3
KJSO FunctionCallNode::evaluate()
{
KJSO e = expr->evaluate();
List *argList = args->evaluateList();
KJSO v = e.getValue();
if (!v.isObject()) {
#ifndef NDEBUG
printInfo("Failed function call attempt on", e);
#endif
delete argList;
return throwError(TypeError, "Expression is no object. Cannot be called.");
}
if (!v.implementsCall()) {
#ifndef NDEBUG
printInfo("Failed function call attempt on", e);
#endif
delete argList;
return throwError(TypeError, "Expression does not allow calls.");
}
KJSO o;
if (e.isA(ReferenceType))
o = e.getBase();
else
o = Null();
if (o.isA(ActivationType))
o = Null();
#ifdef KJS_DEBUGGER
steppingInto(true);
#endif
KJSO result = v.executeCall(o, argList);
#ifdef KJS_DEBUGGER
steppingInto(false);
#endif
delete argList;
return result;
}
#ifdef KJS_DEBUGGER
void FunctionCallNode::steppingInto(bool in)
{
Debugger *dbg = KJScriptImp::current()->debugger();
if (!dbg)
return;
if (in) {
// before entering function. Don't step inside if 'Next' is chosen.
previousMode = dbg->mode();
if (previousMode == Debugger::Next)
dbg->setMode(Debugger::Continue);
} else {
// restore mode after leaving function
dbg->setMode(previousMode);
}
}
#endif
KJSO ArgumentsNode::evaluate()
{
assert(0);
return KJSO(); // dummy, see evaluateList()
}
// ECMA 11.2.4
List* ArgumentsNode::evaluateList()
{
if (!list)
return new List();
return list->evaluateList();
}
KJSO ArgumentListNode::evaluate()
{
assert(0);
return KJSO(); // dummy, see evaluateList()
}
// ECMA 11.2.4
List* ArgumentListNode::evaluateList()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
if (!list) {
List *l = new List();
l->append(v);
return l;
}
List *l = list->evaluateList();
l->append(v);
return l;
}
// ECMA 11.8
KJSO RelationalNode::evaluate()
{
KJSO e1 = expr1->evaluate();
KJSO v1 = e1.getValue();
KJSO e2 = expr2->evaluate();
KJSO v2 = e2.getValue();
bool b;
if (oper == OpLess || oper == OpGreaterEq) {
int r = relation(v1, v2);
if (r < 0)
b = false;
else
b = (oper == OpLess) ? (r == 1) : (r == 0);
} else if (oper == OpGreater || oper == OpLessEq) {
int r = relation(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.isObject())
return throwError( TypeError,
"Shift expression not an object into IN expression." );
b = v2.hasProperty(v1.toString().value());
} else {
/* TODO: should apply to Function _objects_ only */
if (!v2.derivedFrom("Function"))
return throwError(TypeError,
"Called instanceof operator on non-function object." );
return hasInstance(v2, v1); /* TODO: make object member function */
}
return Boolean(b);
}
// ECMA 11.9
KJSO EqualNode::evaluate()
{
KJSO e1 = expr1->evaluate();
KJSO e2 = expr2->evaluate();
KJSO v1 = e1.getValue();
KJSO v2 = e2.getValue();
bool result;
if (oper == OpEqEq || oper == OpNotEq) {
// == and !=
bool eq = equal(v1, v2);
result = oper == OpEqEq ? eq : !eq;
} else {
// === and !==
bool eq = strictEqual(v1, v2);
result = oper == OpStrEq ? eq : !eq;
}
return Boolean(result);
}
// ECMA 11.10
KJSO BitOperNode::evaluate()
{
KJSO e1 = expr1->evaluate();
KJSO v1 = e1.getValue();
KJSO e2 = expr2->evaluate();
KJSO v2 = e2.getValue();
int i1 = v1.toInt32();
int i2 = v2.toInt32();
int result;
if (oper == OpBitAnd)
result = i1 & i2;
else if (oper == OpBitXOr)
result = i1 ^ i2;
else
result = i1 | i2;
return Number(result);
}
// ECMA 11.11
KJSO BinaryLogicalNode::evaluate()
{
KJSO e1 = expr1->evaluate();
KJSO v1 = e1.getValue();
Boolean b1 = v1.toBoolean();
if ((!b1.value() && oper == OpAnd) || (b1.value() && oper == OpOr))
return v1;
KJSO e2 = expr2->evaluate();
KJSO v2 = e2.getValue();
return v2;
}
// ECMA 11.12
KJSO ConditionalNode::evaluate()
{
KJSO e = logical->evaluate();
KJSO v = e.getValue();
Boolean b = v.toBoolean();
if (b.value())
e = expr1->evaluate();
else
e = expr2->evaluate();
return e.getValue();
}
// ECMA 11.13
KJSO AssignNode::evaluate()
{
KJSO l, e, v;
ErrorType err;
if (oper == OpEqual) {
l = left->evaluate();
e = expr->evaluate();
v = e.getValue();
} else {
l = left->evaluate();
KJSO v1 = l.getValue();
e = expr->evaluate();
KJSO v2 = e.getValue();
int i1 = v1.toInt32();
int i2 = v2.toInt32();
switch (oper) {
case OpMultEq:
v = mult(v1, v2, '*');
break;
case OpDivEq:
v = mult(v1, v2, '/');
break;
case OpPlusEq:
v = add(v1, v2, '+');
break;
case OpMinusEq:
v = add(v1, v2, '-');
break;
case OpLShift:
v = Number(i1 <<= i2);
break;
case OpRShift:
v = Number(i1 >>= i2);
break;
case OpURShift:
i1 = v1.toUInt32();
v = Number(i1 >>= 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:
v = Number(i1 %= i2);
break;
default:
v = Undefined();
}
err = l.putValue(v);
};
err = l.putValue(v);
if (err == NoError)
return v;
else
return throwError(err, "Invalid reference.");
}
// ECMA 11.3
KJSO PostfixNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Number n = v.toNumber();
double newValue = (oper == OpPlusPlus) ? n.value() + 1 : n.value() - 1;
KJSO n2 = Number(newValue);
e.putValue(n2);
return n;
}
// ECMA 11.4.1
KJSO DeleteNode::evaluate()
{
KJSO e = expr->evaluate();
if (!e.isA(ReferenceType))
return Boolean(true);
KJSO b = e.getBase();
UString n = e.getPropertyName();
bool ret = b.deleteProperty(n);
return Boolean(ret);
}
// ECMA 11.4.2
KJSO VoidNode::evaluate()
{
KJSO dummy1 = expr->evaluate();
KJSO dummy2 = dummy1.getValue();
return Undefined();
}
// ECMA 11.4.3
KJSO TypeOfNode::evaluate()
{
const char *s = 0L;
KJSO e = expr->evaluate();
if (e.isA(ReferenceType)) {
KJSO b = e.getBase();
if (b.isA(NullType))
return String("undefined");
}
KJSO v = e.getValue();
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.implementsCall())
s = "function";
else
s = "object";
break;
}
return String(s);
}
// ECMA 11.4.4 and 11.4.5
KJSO PrefixNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Number n = v.toNumber();
double newValue = (oper == OpPlusPlus) ? n.value() + 1 : n.value() - 1;
KJSO n2 = Number(newValue);
e.putValue(n2);
return n2;
}
// ECMA 11.4.6
KJSO UnaryPlusNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
return v.toNumber();
}
// ECMA 11.4.7
KJSO NegateNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Number n = v.toNumber();
double d = -n.value();
return Number(d);
}
// ECMA 11.4.8
KJSO BitwiseNotNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
int i32 = v.toInt32();
return Number(~i32);
}
// ECMA 11.4.9
KJSO LogicalNotNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Boolean b = v.toBoolean();
return Boolean(!b.value());
}
// ECMA 11.5
KJSO MultNode::evaluate()
{
KJSO t1 = term1->evaluate();
KJSO v1 = t1.getValue();
KJSO t2 = term2->evaluate();
KJSO v2 = t2.getValue();
return mult(v1, v2, oper);
}
// ECMA 11.7
KJSO ShiftNode::evaluate()
{
KJSO t1 = term1->evaluate();
KJSO v1 = t1.getValue();
KJSO t2 = term2->evaluate();
KJSO v2 = t2.getValue();
unsigned int i2 = v2.toUInt32();
i2 &= 0x1f;
long result;
switch (oper) {
case OpLShift:
result = v1.toInt32() << i2;
break;
case OpRShift:
result = v1.toInt32() >> i2;
break;
case OpURShift:
result = v1.toUInt32() >> i2;
break;
default:
assert(!"ShiftNode: unhandled switch case");
result = 0L;
}
return Number(static_cast<double>(result));
}
// ECMA 11.6
KJSO AddNode::evaluate()
{
KJSO t1 = term1->evaluate();
KJSO v1 = t1.getValue();
KJSO t2 = term2->evaluate();
KJSO v2 = t2.getValue();
return add(v1, v2, oper);
}
// ECMA 11.14
KJSO CommaNode::evaluate()
{
KJSO e = expr1->evaluate();
KJSO dummy = e.getValue(); // ignore return value
e = expr2->evaluate();
return e.getValue();
}
// ECMA 12.1
Completion BlockNode::execute()
{
if (!statlist)
return Completion(Normal);
return statlist->execute();
}
// ECMA 12.1
Completion StatListNode::execute()
{
if (!list) {
Completion c = statement->execute();
KJS_ABORTPOINT
if (KJScriptImp::hadException()) {
KJSO ex = KJScriptImp::exception();
KJScriptImp::clearException();
return Completion(Throw, ex);
} else
return c;
}
Completion l = list->execute();
KJS_ABORTPOINT
if (l.complType() != Normal)
return l;
Completion e = statement->execute();
KJS_ABORTPOINT;
if (KJScriptImp::hadException()) {
KJSO ex = KJScriptImp::exception();
KJScriptImp::clearException();
return Completion(Throw, ex);
}
KJSO v = e.isValueCompletion() ? e.value() : l.value();
return Completion(e.complType(), v, e.target() );
}
// ECMA 12.2
Completion VarStatementNode::execute()
{
KJS_BREAKPOINT;
(void) list->evaluate(); // returns 0L
return Completion(Normal);
}
// ECMA 12.2
KJSO VarDeclNode::evaluate()
{
KJSO variable = Context::current()->variableObject();
KJSO val, tmp;
if (init) {
tmp = init->evaluate();
val = tmp.getValue();
} else {
if ( variable.hasProperty( ident ) ) // already declared ?
return KJSO();
val = Undefined();
}
variable.put(ident, val, DontDelete);
// spec wants to return ident. But what for ? Will be ignored above.
return KJSO();
}
// ECMA 12.2
KJSO VarDeclListNode::evaluate()
{
if (list)
(void) list->evaluate();
(void) var->evaluate();
return KJSO();
}
// ECMA 12.2
KJSO AssignExprNode::evaluate()
{
return expr->evaluate();
}
// ECMA 12.3
Completion EmptyStatementNode::execute()
{
return Completion(Normal);
}
// ECMA 12.6.3
Completion ForNode::execute()
{
KJSO e, v, cval;
Boolean b;
if (expr1) {
e = expr1->evaluate();
v = e.getValue();
}
while (1) {
if (expr2) {
e = expr2->evaluate();
v = e.getValue();
b = v.toBoolean();
if (b.value() == false)
return Completion(Normal, cval);
}
// bail out on error
if (KJScriptImp::hadException())
return Completion(Throw, KJScriptImp::exception());
Completion c = stat->execute();
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) {
e = expr3->evaluate();
v = e.getValue();
}
}
}
// ECMA 12.6.4
Completion ForInNode::execute()
{
KJSO e, v, retval;
Completion c;
VarDeclNode *vd = 0;
const PropList *lst, *curr;
// This should be done in the constructor
if (!lexpr) { // for( var foo = bar in baz )
vd = new VarDeclNode(&ident, init);
vd->evaluate();
lexpr = new ResolveNode(&ident);
}
e = expr->evaluate();
v = e.getValue().toObject();
curr = lst = v.imp()->propList();
while (curr) {
if (!v.hasProperty(curr->name)) {
curr = curr->next;
continue;
}
e = lexpr->evaluate();
e.putValue(String(curr->name));
c = stat->execute();
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) {
delete lst;
return c;
}
}
curr = curr->next;
}
delete lst;
return Completion(Normal, retval);
}
// ECMA 12.4
Completion ExprStatementNode::execute()
{
KJS_BREAKPOINT;
KJSO e = expr->evaluate();
KJSO v = e.getValue();
return Completion(Normal, v);
}
// ECMA 12.5
Completion IfNode::execute()
{
KJS_BREAKPOINT;
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Boolean b = v.toBoolean();
// if ... then
if (b.value())
return statement1->execute();
// no else
if (!statement2)
return Completion(Normal);
// else
return statement2->execute();
}
// ECMA 12.6.1
Completion DoWhileNode::execute()
{
KJS_BREAKPOINT;
KJSO be, bv;
Completion c;
KJSO value;
do {
// bail out on error
if (KJScriptImp::hadException())
return Completion(Throw, KJScriptImp::exception());
c = statement->execute();
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;
}
be = expr->evaluate();
bv = be.getValue();
} while (bv.toBoolean().value());
return Completion(Normal, value);
}
// ECMA 12.6.2
Completion WhileNode::execute()
{
KJS_BREAKPOINT;
KJSO be, bv;
Completion c;
Boolean b(false);
KJSO value;
while (1) {
be = expr->evaluate();
bv = be.getValue();
b = bv.toBoolean();
// bail out on error
if (KJScriptImp::hadException())
return Completion(Throw, KJScriptImp::exception());
if (!b.value())
return Completion(Normal, value);
c = statement->execute();
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;
}
}
// ECMA 12.7
Completion ContinueNode::execute()
{
KJS_BREAKPOINT;
KJSO dummy;
return Context::current()->seenLabels()->contains(ident) ?
Completion(Continue, dummy, ident) :
Completion(Throw,
throwError(SyntaxError, "Label not found in containing block"));
}
// ECMA 12.8
Completion BreakNode::execute()
{
KJS_BREAKPOINT;
KJSO dummy;
return Context::current()->seenLabels()->contains(ident) ?
Completion(Break, dummy, ident) :
Completion(Throw,
throwError(SyntaxError, "Label not found in containing block"));
}
// ECMA 12.9
Completion ReturnNode::execute()
{
KJS_BREAKPOINT;
if (!value)
return Completion(ReturnValue, Undefined());
KJSO e = value->evaluate();
KJSO v = e.getValue();
return Completion(ReturnValue, v);
}
// ECMA 12.10
Completion WithNode::execute()
{
KJS_BREAKPOINT;
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Object o = v.toObject();
Context::current()->pushScope(o);
Completion res = stat->execute();
Context::current()->popScope();
return res;
}
// ECMA 12.11
ClauseListNode* ClauseListNode::append(CaseClauseNode *c)
{
ClauseListNode *l = this;
while (l->nx)
l = l->nx;
l->nx = new ClauseListNode(c);
return this;
}
// ECMA 12.11
Completion SwitchNode::execute()
{
KJS_BREAKPOINT;
KJSO e = expr->evaluate();
KJSO v = e.getValue();
Completion res = block->evalBlock(v);
if ((res.complType() == Break) && ls.contains(res.target()))
return Completion(Normal, res.value());
else
return res;
}
// ECMA 12.11
Completion CaseBlockNode::evalBlock(const KJSO& input)
{
KJSO v;
Completion res;
ClauseListNode *a = list1, *b = list2;
CaseClauseNode *clause;
if (a) {
while (a) {
clause = a->clause();
a = a->next();
v = clause->evaluate();
if (strictEqual(input, v)) {
res = clause->evalStatements();
if (res.complType() != Normal)
return res;
while (a) {
res = a->clause()->evalStatements();
if (res.complType() != Normal)
return res;
a = a->next();
}
break;
}
}
}
while (b) {
clause = b->clause();
b = b->next();
v = clause->evaluate();
if (strictEqual(input, v)) {
res = clause->evalStatements();
if (res.complType() != Normal)
return res;
goto step18;
}
}
// default clause
if (def) {
res = def->evalStatements();
if (res.complType() != Normal)
return res;
}
b = list2;
step18:
while (b) {
clause = b->clause();
res = clause->evalStatements();
if (res.complType() != Normal)
return res;
b = b->next();
}
return Completion(Normal);
}
// ECMA 12.11
KJSO CaseClauseNode::evaluate()
{
KJSO e = expr->evaluate();
KJSO v = e.getValue();
return v;
}
// ECMA 12.11
Completion CaseClauseNode::evalStatements()
{
if (list)
return list->execute();
else
return Completion(Normal, Undefined());
}
// ECMA 12.12
Completion LabelNode::execute()
{
Completion e;
if (!Context::current()->seenLabels()->push(label)) {
return Completion( Throw,
throwError(SyntaxError, "Duplicated label found" ));
};
e = stat->execute();
Context::current()->seenLabels()->pop();
if ((e.complType() == Break) && (e.target() == label))
return Completion(Normal, e.value());
else
return e;
}
// ECMA 12.13
Completion ThrowNode::execute()
{
KJS_BREAKPOINT;
KJSO v = expr->evaluate().getValue();
return Completion(Throw, v);
}
// ECMA 12.14
Completion TryNode::execute()
{
KJS_BREAKPOINT;
Completion c, c2;
c = block->execute();
if (!_final) {
if (c.complType() != Throw)
return c;
return _catch->execute(c.value());
}
if (!_catch) {
c2 = _final->execute();
return (c2.complType() == Normal) ? c : c2;
}
if (c.complType() == Throw)
c = _catch->execute(c.value());
c2 = _final->execute();
return (c2.complType() == Normal) ? c : c2;
}
Completion CatchNode::execute()
{
// should never be reached. execute(const KJS &arg) is used instead
assert(0L);
return Completion();
}
// ECMA 12.14
Completion CatchNode::execute(const KJSO &arg)
{
/* TODO: correct ? Not part of the spec */
KJScriptImp::clearException();
Object obj;
obj.put(ident, arg, DontDelete);
Context::current()->pushScope(obj);
Completion c = block->execute();
Context::current()->popScope();
return c;
}
// ECMA 12.14
Completion FinallyNode::execute()
{
return block->execute();
}
FunctionBodyNode::FunctionBodyNode(SourceElementsNode *s)
: source(s)
{
#ifdef KJS_DEBUGGER
setLoc(-1, -1);
#endif
}
// ECMA 13 + 14 for ProgramNode
Completion FunctionBodyNode::execute()
{
/* TODO: workaround for empty body which I don't see covered by the spec */
if (!source)
return Completion(ReturnValue, Undefined());
source->processFuncDecl();
return source->execute();
}
// ECMA 13
void FuncDeclNode::processFuncDecl()
{
const List *sc = Context::current()->pScopeChain();
/* TODO: let this be an object with [[Class]] property "Function" */
FunctionImp *fimp = new DeclaredFunctionImp(ident, body, sc);
Function func(fimp); // protect from GC
fimp->put("prototype", Object::create(ObjectClass), DontDelete);
int plen = 0;
for(ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
fimp->addParameter(p->ident());
fimp->setLength(plen);
Context::current()->variableObject().put(ident, func);
}
// ECMA 13
KJSO FuncExprNode::evaluate()
{
const List *sc = Context::current()->pScopeChain();
FunctionImp *fimp = new DeclaredFunctionImp(UString::null, body, sc->copy());
Function ret(fimp);
int plen = 0;
for(ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
fimp->addParameter(p->ident());
fimp->setLength(plen);
return ret;
}
ParameterNode* ParameterNode::append(const UString *i)
{
ParameterNode *p = this;
while (p->next)
p = p->next;
p->next = new ParameterNode(i);
return this;
}
// ECMA 13
KJSO ParameterNode::evaluate()
{
return Undefined();
}
void ProgramNode::deleteGlobalStatements()
{
source->deleteStatements();
}
// ECMA 14
Completion SourceElementsNode::execute()
{
if (KJScriptImp::hadException())
return Completion(Throw, KJScriptImp::exception());
if (!elements)
return element->execute();
Completion c1 = elements->execute();
if (KJScriptImp::hadException())
return Completion(Throw, KJScriptImp::exception());
if (c1.complType() != Normal)
return c1;
Completion c2 = element->execute();
if (KJScriptImp::hadException())
return Completion(Throw, KJScriptImp::exception());
return c2;
}
// ECMA 14
void SourceElementsNode::processFuncDecl()
{
if (elements)
elements->processFuncDecl();
element->processFuncDecl();
}
void SourceElementsNode::deleteStatements()
{
element->deleteStatements();
if (elements)
elements->deleteStatements();
}
// ECMA 14
Completion SourceElementNode::execute()
{
if (statement)
return statement->execute();
return Completion(Normal);
}
// ECMA 14
void SourceElementNode::processFuncDecl()
{
if (function)
function->processFuncDecl();
}
void SourceElementNode::deleteStatements()
{
delete statement;
}
ArgumentListNode::ArgumentListNode(Node *e) : list(0L), expr(e) {}
VarDeclNode::VarDeclNode(const UString *id, AssignExprNode *in)
: ident(*id), init(in) { }
ArgumentListNode::ArgumentListNode(ArgumentListNode *l, Node *e) :
list(l), expr(e) {}
ArgumentsNode::ArgumentsNode(ArgumentListNode *l) : list(l) {}