/* * 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 #include #ifdef KJS_DEBUG_MEM #include #include #endif #ifdef KJS_VERBOSE #include 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::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; 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, "[nodes] Node::finalCheck(): list count : %d\n", (int)s_nodes->size() ); std::list::iterator it = s_nodes->begin(); for ( uint i = 0; it != s_nodes->end() ; ++it, ++i ) fprintf( stderr, "[nodes] [%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(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(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(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(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(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(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(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); }