blob: b838a0c9df339afdd5adfdcaaeb71b9b5177513a [file] [log] [blame]
/*
* This file is part of the KDE libraries
* Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
* Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
* Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
*
* 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 "config.h"
#include "nodes.h"
#include <math.h>
#ifdef KJS_DEBUG_MEM
#include <stdio.h>
#include <typeinfo>
#endif
#include "context.h"
#include "debugger.h"
#include "function_object.h"
#include "lexer.h"
#include "operations.h"
#include "PropertyNameArray.h"
#include <wtf/HashSet.h>
#include <wtf/HashCountedSet.h>
#include <wtf/MathExtras.h>
using namespace KJS;
#define KJS_BREAKPOINT \
if (Debugger::debuggersPresent > 0 && !hitStatement(exec)) \
return Completion(Normal);
#define KJS_ABORTPOINT \
if (Debugger::debuggersPresent > 0 && \
exec->dynamicInterpreter()->imp()->debugger() && \
exec->dynamicInterpreter()->imp()->debugger()->imp()->aborted()) \
return Completion(Normal);
#define KJS_CHECKEXCEPTION \
if (exec->hadException()) { \
JSValue *ex = exec->exception(); \
exec->clearException(); \
handleException(exec, ex); \
return Completion(Throw, ex); \
} \
if (Collector::isOutOfMemory()) \
return Completion(Throw, Error::create(exec, GeneralError, "Out of memory"));
#define KJS_CHECKEXCEPTIONVALUE \
if (exec->hadException()) { \
handleException(exec); \
return jsUndefined(); \
} \
if (Collector::isOutOfMemory()) \
return jsUndefined(); // will be picked up by KJS_CHECKEXCEPTION
#define KJS_CHECKEXCEPTIONLIST \
if (exec->hadException()) { \
handleException(exec); \
return List(); \
} \
if (Collector::isOutOfMemory()) \
return List(); // will be picked up by KJS_CHECKEXCEPTION
// ------------------------------ Node -----------------------------------------
#ifndef NDEBUG
#ifndef LOG_CHANNEL_PREFIX
#define LOG_CHANNEL_PREFIX Log
#endif
static WTFLogChannel LogKJSNodeLeaks = { 0x00000000, "", WTFLogChannelOn };
struct NodeCounter {
static unsigned count;
~NodeCounter()
{
if (count)
LOG(KJSNodeLeaks, "LEAK: %u KJS::Node\n", count);
}
};
unsigned NodeCounter::count = 0;
static NodeCounter nodeCounter;
#endif
static HashSet<Node*>* newNodes;
static HashCountedSet<Node*>* nodeExtraRefCounts;
Node::Node()
{
#ifndef NDEBUG
++NodeCounter::count;
#endif
m_line = Lexer::curr()->lineNo();
if (!newNodes)
newNodes = new HashSet<Node*>;
newNodes->add(this);
}
Node::~Node()
{
#ifndef NDEBUG
--NodeCounter::count;
#endif
}
void Node::ref()
{
// bumping from 0 to 1 is just removing from the new nodes set
if (newNodes) {
HashSet<Node*>::iterator it = newNodes->find(this);
if (it != newNodes->end()) {
newNodes->remove(it);
ASSERT(!nodeExtraRefCounts || !nodeExtraRefCounts->contains(this));
return;
}
}
ASSERT(!newNodes || !newNodes->contains(this));
if (!nodeExtraRefCounts)
nodeExtraRefCounts = new HashCountedSet<Node*>;
nodeExtraRefCounts->add(this);
}
void Node::deref()
{
ASSERT(!newNodes || !newNodes->contains(this));
if (!nodeExtraRefCounts) {
delete this;
return;
}
HashCountedSet<Node*>::iterator it = nodeExtraRefCounts->find(this);
if (it == nodeExtraRefCounts->end())
delete this;
else
nodeExtraRefCounts->remove(it);
}
unsigned Node::refcount()
{
if (newNodes && newNodes->contains(this)) {
ASSERT(!nodeExtraRefCounts || !nodeExtraRefCounts->contains(this));
return 0;
}
ASSERT(!newNodes || !newNodes->contains(this));
if (!nodeExtraRefCounts)
return 1;
return 1 + nodeExtraRefCounts->count(this);
}
void Node::clearNewNodes()
{
if (!newNodes)
return;
#ifndef NDEBUG
HashSet<Node*>::iterator end = newNodes->end();
for (HashSet<Node*>::iterator it = newNodes->begin(); it != end; ++it)
ASSERT(!nodeExtraRefCounts || !nodeExtraRefCounts->contains(*it));
#endif
deleteAllValues(*newNodes);
delete newNodes;
newNodes = 0;
}
static void substitute(UString &string, const UString &substring) KJS_FAST_CALL;
static void substitute(UString &string, const UString &substring)
{
int position = string.find("%s");
assert(position != -1);
string = string.substr(0, position) + substring + string.substr(position + 2);
}
static inline int currentSourceId(ExecState* exec) KJS_FAST_CALL;
static inline int currentSourceId(ExecState* exec)
{
return exec->context()->currentBody()->sourceId();
}
static inline const UString& currentSourceURL(ExecState* exec) KJS_FAST_CALL;
static inline const UString& currentSourceURL(ExecState* exec)
{
return exec->context()->currentBody()->sourceURL();
}
Completion Node::createErrorCompletion(ExecState* exec, ErrorType e, const char *msg)
{
return Completion(Throw, Error::create(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec)));
}
Completion Node::createErrorCompletion(ExecState *exec, ErrorType e, const char *msg, const Identifier &ident)
{
UString message = msg;
substitute(message, ident.ustring());
return Completion(Throw, Error::create(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)));
}
JSValue *Node::throwError(ExecState* exec, ErrorType e, const char *msg)
{
return KJS::throwError(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwError(ExecState* exec, ErrorType e, const char* msg, const char* string)
{
UString message = msg;
substitute(message, string);
return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwError(ExecState *exec, ErrorType e, const char *msg, JSValue *v, Node *expr)
{
UString message = msg;
substitute(message, v->toString(exec));
substitute(message, expr->toString());
return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwError(ExecState *exec, ErrorType e, const char *msg, const Identifier &label)
{
UString message = msg;
substitute(message, label.ustring());
return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwError(ExecState *exec, ErrorType e, const char *msg, JSValue *v, Node *e1, Node *e2)
{
UString message = msg;
substitute(message, v->toString(exec));
substitute(message, e1->toString());
substitute(message, e2->toString());
return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwError(ExecState *exec, ErrorType e, const char *msg, JSValue *v, Node *expr, const Identifier &label)
{
UString message = msg;
substitute(message, v->toString(exec));
substitute(message, expr->toString());
substitute(message, label.ustring());
return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwError(ExecState *exec, ErrorType e, const char *msg, JSValue *v, const Identifier &label)
{
UString message = msg;
substitute(message, v->toString(exec));
substitute(message, label.ustring());
return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}
JSValue *Node::throwUndefinedVariableError(ExecState *exec, const Identifier &ident)
{
return throwError(exec, ReferenceError, "Can't find variable: %s", ident);
}
void Node::handleException(ExecState* exec)
{
handleException(exec, exec->exception());
}
void Node::handleException(ExecState* exec, JSValue* exceptionValue)
{
if (exceptionValue->isObject()) {
JSObject* exception = static_cast<JSObject*>(exceptionValue);
if (!exception->hasProperty(exec, "line") && !exception->hasProperty(exec, "sourceURL")) {
exception->put(exec, "line", jsNumber(m_line));
exception->put(exec, "sourceURL", jsString(currentSourceURL(exec)));
}
}
Debugger* dbg = exec->dynamicInterpreter()->debugger();
if (dbg && !dbg->hasHandledException(exec, exceptionValue)) {
bool cont = dbg->exception(exec, currentSourceId(exec), m_line, exceptionValue);
if (!cont)
dbg->imp()->abort();
}
}
Node *Node::nodeInsideAllParens()
{
return this;
}
// ------------------------------ StatementNode --------------------------------
StatementNode::StatementNode()
: m_lastLine(-1)
{
m_line = -1;
}
void StatementNode::setLoc(int firstLine, int lastLine)
{
m_line = firstLine;
m_lastLine = lastLine;
}
// return true if the debugger wants us to stop at this point
bool StatementNode::hitStatement(ExecState* exec)
{
Debugger *dbg = exec->dynamicInterpreter()->debugger();
if (dbg)
return dbg->atStatement(exec, currentSourceId(exec), firstLine(), lastLine());
else
return true; // continue
}
void StatementNode::processFuncDecl(ExecState*)
{
}
// ------------------------------ NullNode -------------------------------------
JSValue *NullNode::evaluate(ExecState *)
{
return jsNull();
}
// ------------------------------ BooleanNode ----------------------------------
JSValue *BooleanNode::evaluate(ExecState *)
{
return jsBoolean(value);
}
// ------------------------------ NumberNode -----------------------------------
JSValue *NumberNode::evaluate(ExecState *)
{
return jsNumber(value);
}
// ------------------------------ StringNode -----------------------------------
JSValue *StringNode::evaluate(ExecState *)
{
return jsOwnedString(value);
}
// ------------------------------ RegExpNode -----------------------------------
JSValue *RegExpNode::evaluate(ExecState *exec)
{
List list;
list.append(jsOwnedString(pattern));
list.append(jsOwnedString(flags));
JSObject *reg = exec->lexicalInterpreter()->builtinRegExp();
return reg->construct(exec,list);
}
// ------------------------------ ThisNode -------------------------------------
// ECMA 11.1.1
JSValue *ThisNode::evaluate(ExecState *exec)
{
return exec->context()->thisValue();
}
// ------------------------------ ResolveNode ----------------------------------
// ECMA 11.1.2 & 10.1.4
JSValue *ResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
do {
JSObject *o = *iter;
if (o->getPropertySlot(exec, ident, slot))
return slot.getValue(exec, o, ident);
++iter;
} while (iter != end);
return throwUndefinedVariableError(exec, ident);
}
// ------------------------------ GroupNode ------------------------------------
// ECMA 11.1.6
JSValue *GroupNode::evaluate(ExecState *exec)
{
return group->evaluate(exec);
}
Node *GroupNode::nodeInsideAllParens()
{
Node *n = this;
do
n = static_cast<GroupNode *>(n)->group.get();
while (n->isGroupNode());
return n;
}
// ------------------------------ ElementNode ----------------------------------
// ECMA 11.1.4
JSValue *ElementNode::evaluate(ExecState *exec)
{
JSObject *array = exec->lexicalInterpreter()->builtinArray()->construct(exec, List::empty());
int length = 0;
for (ElementNode *n = this; n; n = n->next.get()) {
JSValue *val = n->node->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
length += n->elision;
array->put(exec, length++, val);
}
return array;
}
void ElementNode::breakCycle()
{
next = 0;
}
// ------------------------------ ArrayNode ------------------------------------
// ECMA 11.1.4
JSValue *ArrayNode::evaluate(ExecState *exec)
{
JSObject *array;
int length;
if (element) {
array = static_cast<JSObject*>(element->evaluate(exec));
KJS_CHECKEXCEPTIONVALUE
length = opt ? array->get(exec, exec->propertyNames().length)->toInt32(exec) : 0;
} else {
JSValue *newArr = exec->lexicalInterpreter()->builtinArray()->construct(exec,List::empty());
array = static_cast<JSObject*>(newArr);
length = 0;
}
if (opt)
array->put(exec, exec->propertyNames().length, jsNumber(elision + length), DontEnum | DontDelete);
return array;
}
// ------------------------------ ObjectLiteralNode ----------------------------
// ECMA 11.1.5
JSValue *ObjectLiteralNode::evaluate(ExecState *exec)
{
if (list)
return list->evaluate(exec);
return exec->lexicalInterpreter()->builtinObject()->construct(exec,List::empty());
}
// ------------------------------ PropertyListNode -----------------------------
// ECMA 11.1.5
JSValue *PropertyListNode::evaluate(ExecState *exec)
{
JSObject *obj = exec->lexicalInterpreter()->builtinObject()->construct(exec, List::empty());
for (PropertyListNode *p = this; p; p = p->next.get()) {
JSValue *n = p->node->name->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *v = p->node->assign->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
Identifier propertyName = Identifier(n->toString(exec));
switch (p->node->type) {
case PropertyNode::Getter:
assert(v->isObject());
obj->defineGetter(exec, propertyName, static_cast<JSObject *>(v));
break;
case PropertyNode::Setter:
assert(v->isObject());
obj->defineSetter(exec, propertyName, static_cast<JSObject *>(v));
break;
case PropertyNode::Constant:
obj->put(exec, propertyName, v);
break;
}
}
return obj;
}
void PropertyListNode::breakCycle()
{
next = 0;
}
// ------------------------------ PropertyNode -----------------------------
// ECMA 11.1.5
JSValue *PropertyNode::evaluate(ExecState*)
{
assert(false);
return jsNull();
}
// ---------------------------- PropertyNameNode -------------------------------
// ECMA 11.1.5
JSValue *PropertyNameNode::evaluate(ExecState*)
{
JSValue *s;
if (str.isNull()) {
s = jsString(UString::from(numeric));
} else {
s = jsOwnedString(str.ustring());
}
return s;
}
// ------------------------------ BracketAccessorNode --------------------------------
// ECMA 11.2.1a
JSValue *BracketAccessorNode::evaluate(ExecState *exec)
{
JSValue *v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *o = v1->toObject(exec);
uint32_t i;
if (v2->getUInt32(i))
return o->get(exec, i);
return o->get(exec, Identifier(v2->toString(exec)));
}
// ------------------------------ DotAccessorNode --------------------------------
// ECMA 11.2.1b
JSValue *DotAccessorNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v->toObject(exec)->get(exec, ident);
}
// ------------------------------ ArgumentListNode -----------------------------
JSValue *ArgumentListNode::evaluate(ExecState *)
{
assert(0);
return 0; // dummy, see evaluateList()
}
// ECMA 11.2.4
List ArgumentListNode::evaluateList(ExecState *exec)
{
List l;
for (ArgumentListNode *n = this; n; n = n->next.get()) {
JSValue *v = n->expr->evaluate(exec);
KJS_CHECKEXCEPTIONLIST
l.append(v);
}
return l;
}
void ArgumentListNode::breakCycle()
{
next = 0;
}
// ------------------------------ ArgumentsNode --------------------------------
JSValue *ArgumentsNode::evaluate(ExecState *)
{
assert(0);
return 0; // dummy, see evaluateList()
}
// ------------------------------ NewExprNode ----------------------------------
// ECMA 11.2.2
JSValue *NewExprNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
List argList;
if (args) {
argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
}
if (!v->isObject()) {
return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with new.", v, expr.get());
}
JSObject *constr = static_cast<JSObject*>(v);
if (!constr->implementsConstruct()) {
return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, expr.get());
}
return constr->construct(exec, argList);
}
// ECMA 11.2.3
JSValue *FunctionCallValueNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
if (!v->isObject()) {
return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, expr.get());
}
JSObject *func = static_cast<JSObject*>(v);
if (!func->implementsCall()) {
return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, expr.get());
}
List argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *thisObj = exec->dynamicInterpreter()->globalObject();
return func->call(exec, thisObj, argList);
}
// ECMA 11.2.3
JSValue *FunctionCallResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *base;
do {
base = *iter;
if (base->getPropertySlot(exec, ident, slot)) {
JSValue *v = slot.getValue(exec, base, ident);
KJS_CHECKEXCEPTIONVALUE
if (!v->isObject()) {
return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, ident);
}
JSObject *func = static_cast<JSObject*>(v);
if (!func->implementsCall()) {
return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, ident);
}
List argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *thisObj = base;
// 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.
if (thisObj->isActivation())
thisObj = exec->dynamicInterpreter()->globalObject();
return func->call(exec, thisObj, argList);
}
++iter;
} while (iter != end);
return throwUndefinedVariableError(exec, ident);
}
// ECMA 11.2.3
JSValue *FunctionCallBracketNode::evaluate(ExecState *exec)
{
JSValue *baseVal = base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *subscriptVal = subscript->evaluate(exec);
JSObject *baseObj = baseVal->toObject(exec);
uint32_t i;
PropertySlot slot;
JSValue *funcVal;
if (subscriptVal->getUInt32(i)) {
if (baseObj->getPropertySlot(exec, i, slot))
funcVal = slot.getValue(exec, baseObj, i);
else
funcVal = jsUndefined();
} else {
Identifier ident(subscriptVal->toString(exec));
if (baseObj->getPropertySlot(exec, ident, slot))
funcVal = baseObj->get(exec, ident);
else
funcVal = jsUndefined();
}
KJS_CHECKEXCEPTIONVALUE
if (!funcVal->isObject()) {
return throwError(exec, TypeError, "Value %s (result of expression %s[%s]) is not object.", funcVal, base.get(), subscript.get());
}
JSObject *func = static_cast<JSObject*>(funcVal);
if (!func->implementsCall()) {
return throwError(exec, TypeError, "Object %s (result of expression %s[%s]) does not allow calls.", funcVal, base.get(), subscript.get());
}
List argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *thisObj = baseObj;
assert(thisObj);
assert(thisObj->isObject());
assert(!thisObj->isActivation());
return func->call(exec, thisObj, argList);
}
static const char *dotExprNotAnObjectString() KJS_FAST_CALL;
static const char *dotExprNotAnObjectString()
{
return "Value %s (result of expression %s.%s) is not object.";
}
static const char *dotExprDoesNotAllowCallsString() KJS_FAST_CALL;
static const char *dotExprDoesNotAllowCallsString()
{
return "Object %s (result of expression %s.%s) does not allow calls.";
}
// ECMA 11.2.3
JSValue *FunctionCallDotNode::evaluate(ExecState *exec)
{
JSValue *baseVal = base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *baseObj = baseVal->toObject(exec);
PropertySlot slot;
JSValue *funcVal = baseObj->getPropertySlot(exec, ident, slot) ? slot.getValue(exec, baseObj, ident) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
if (!funcVal->isObject())
return throwError(exec, TypeError, dotExprNotAnObjectString(), funcVal, base.get(), ident);
JSObject *func = static_cast<JSObject*>(funcVal);
if (!func->implementsCall())
return throwError(exec, TypeError, dotExprDoesNotAllowCallsString(), funcVal, base.get(), ident);
List argList = args->evaluateList(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *thisObj = baseObj;
assert(thisObj);
assert(thisObj->isObject());
assert(!thisObj->isActivation());
return func->call(exec, thisObj, argList);
}
// ECMA 11.3
// ------------------------------ PostfixResolveNode ----------------------------------
JSValue *PostfixResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *base;
do {
base = *iter;
if (base->getPropertySlot(exec, m_ident, slot)) {
JSValue *v = slot.getValue(exec, base, m_ident);
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
base->put(exec, m_ident, jsNumber(newValue));
return jsNumber(n);
}
++iter;
} while (iter != end);
return throwUndefinedVariableError(exec, m_ident);
}
// ------------------------------ PostfixBracketNode ----------------------------------
JSValue *PostfixBracketNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *subscript = m_subscript->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
uint32_t propertyIndex;
if (subscript->getUInt32(propertyIndex)) {
PropertySlot slot;
JSValue *v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
base->put(exec, propertyIndex, jsNumber(newValue));
return jsNumber(n);
}
Identifier propertyName(subscript->toString(exec));
PropertySlot slot;
JSValue *v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
base->put(exec, propertyName, jsNumber(newValue));
return jsNumber(n);
}
// ------------------------------ PostfixDotNode ----------------------------------
JSValue *PostfixDotNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
PropertySlot slot;
JSValue *v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
base->put(exec, m_ident, jsNumber(newValue));
return jsNumber(n);
}
// ------------------------------ PostfixErrorNode -----------------------------------
JSValue* PostfixErrorNode::evaluate(ExecState* exec)
{
throwError(exec, ReferenceError, "Postfix %s operator applied to value that is not a reference.",
m_oper == OpPlusPlus ? "++" : "--");
handleException(exec);
return jsUndefined();
}
// ------------------------------ DeleteResolveNode -----------------------------------
// ECMA 11.4.1
JSValue *DeleteResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *base;
do {
base = *iter;
if (base->getPropertySlot(exec, m_ident, slot)) {
return jsBoolean(base->deleteProperty(exec, m_ident));
}
++iter;
} while (iter != end);
return jsBoolean(true);
}
// ------------------------------ DeleteBracketNode -----------------------------------
JSValue *DeleteBracketNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *subscript = m_subscript->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
uint32_t propertyIndex;
if (subscript->getUInt32(propertyIndex))
return jsBoolean(base->deleteProperty(exec, propertyIndex));
Identifier propertyName(subscript->toString(exec));
return jsBoolean(base->deleteProperty(exec, propertyName));
}
// ------------------------------ DeleteDotNode -----------------------------------
JSValue *DeleteDotNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
JSObject *base = baseValue->toObject(exec);
KJS_CHECKEXCEPTIONVALUE
return jsBoolean(base->deleteProperty(exec, m_ident));
}
// ------------------------------ DeleteValueNode -----------------------------------
JSValue *DeleteValueNode::evaluate(ExecState *exec)
{
m_expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
// delete on a non-location expression ignores the value and returns true
return jsBoolean(true);
}
// ------------------------------ VoidNode -------------------------------------
// ECMA 11.4.2
JSValue *VoidNode::evaluate(ExecState *exec)
{
expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return jsUndefined();
}
// ECMA 11.4.3
// ------------------------------ TypeOfValueNode -----------------------------------
static JSValue *typeStringForValue(JSValue *v) KJS_FAST_CALL;
static JSValue *typeStringForValue(JSValue *v)
{
switch (v->type()) {
case UndefinedType:
return jsString("undefined");
case NullType:
return jsString("object");
case BooleanType:
return jsString("boolean");
case NumberType:
return jsString("number");
case StringType:
return jsString("string");
default:
if (v->isObject()) {
// Return "undefined" for objects that should be treated
// as null when doing comparisons.
if (static_cast<JSObject*>(v)->masqueradeAsUndefined())
return jsString("undefined");
else if (static_cast<JSObject*>(v)->implementsCall())
return jsString("function");
}
return jsString("object");
}
}
JSValue *TypeOfResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *base;
do {
base = *iter;
if (base->getPropertySlot(exec, m_ident, slot)) {
JSValue *v = slot.getValue(exec, base, m_ident);
return typeStringForValue(v);
}
++iter;
} while (iter != end);
return jsString("undefined");
}
// ------------------------------ TypeOfValueNode -----------------------------------
JSValue *TypeOfValueNode::evaluate(ExecState *exec)
{
JSValue *v = m_expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return typeStringForValue(v);
}
// ECMA 11.4.4 and 11.4.5
// ------------------------------ PrefixResolveNode ----------------------------------
JSValue *PrefixResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *base;
do {
base = *iter;
if (base->getPropertySlot(exec, m_ident, slot)) {
JSValue *v = slot.getValue(exec, base, m_ident);
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
JSValue *n2 = jsNumber(newValue);
base->put(exec, m_ident, n2);
return n2;
}
++iter;
} while (iter != end);
return throwUndefinedVariableError(exec, m_ident);
}
// ------------------------------ PrefixBracketNode ----------------------------------
JSValue *PrefixBracketNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *subscript = m_subscript->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
uint32_t propertyIndex;
if (subscript->getUInt32(propertyIndex)) {
PropertySlot slot;
JSValue *v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
JSValue *n2 = jsNumber(newValue);
base->put(exec, propertyIndex, n2);
return n2;
}
Identifier propertyName(subscript->toString(exec));
PropertySlot slot;
JSValue *v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
JSValue *n2 = jsNumber(newValue);
base->put(exec, propertyName, n2);
return n2;
}
// ------------------------------ PrefixDotNode ----------------------------------
JSValue *PrefixDotNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
PropertySlot slot;
JSValue *v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
double newValue = (m_oper == OpPlusPlus) ? n + 1 : n - 1;
JSValue *n2 = jsNumber(newValue);
base->put(exec, m_ident, n2);
return n2;
}
// ------------------------------ PrefixErrorNode -----------------------------------
JSValue* PrefixErrorNode::evaluate(ExecState* exec)
{
throwError(exec, ReferenceError, "Prefix %s operator applied to value that is not a reference.",
m_oper == OpPlusPlus ? "++" : "--");
handleException(exec);
return jsUndefined();
}
// ------------------------------ UnaryPlusNode --------------------------------
// ECMA 11.4.6
JSValue *UnaryPlusNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return jsNumber(v->toNumber(exec));
}
// ------------------------------ NegateNode -----------------------------------
// ECMA 11.4.7
JSValue *NegateNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
double n = v->toNumber(exec);
return jsNumber(-n);
}
// ------------------------------ BitwiseNotNode -------------------------------
// ECMA 11.4.8
JSValue *BitwiseNotNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return jsNumber(~v->toInt32(exec));
}
// ------------------------------ LogicalNotNode -------------------------------
// ECMA 11.4.9
JSValue *LogicalNotNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return jsBoolean(!v->toBoolean(exec));
}
// ------------------------------ MultNode -------------------------------------
// ECMA 11.5
JSValue *MultNode::evaluate(ExecState *exec)
{
JSValue *v1 = term1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = term2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return mult(exec, v1, v2, oper);
}
// ------------------------------ AddNode --------------------------------------
// ECMA 11.6
JSValue *AddNode::evaluate(ExecState *exec)
{
JSValue *v1 = term1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = term2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return add(exec, v1, v2, oper);
}
// ------------------------------ ShiftNode ------------------------------------
// ECMA 11.7
JSValue *ShiftNode::evaluate(ExecState *exec)
{
JSValue *v1 = term1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = term2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
unsigned int i2 = v2->toUInt32(exec);
i2 &= 0x1f;
switch (oper) {
case OpLShift:
return jsNumber(v1->toInt32(exec) << i2);
case OpRShift:
return jsNumber(v1->toInt32(exec) >> i2);
case OpURShift:
return jsNumber(v1->toUInt32(exec) >> i2);
default:
assert(!"ShiftNode: unhandled switch case");
return jsUndefined();
}
}
// ------------------------------ RelationalNode -------------------------------
// ECMA 11.8
JSValue *RelationalNode::evaluate(ExecState *exec)
{
JSValue *v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *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->isObject())
return throwError(exec, TypeError,
"Value %s (result of expression %s) is not an object. Cannot be used with IN expression.", v2, expr2.get());
JSObject *o2(static_cast<JSObject*>(v2));
b = o2->hasProperty(exec, Identifier(v1->toString(exec)));
} else {
if (!v2->isObject())
return throwError(exec, TypeError,
"Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, expr2.get());
JSObject *o2(static_cast<JSObject*>(v2));
if (!o2->implementsHasInstance())
// According to the spec, only some types of objects "implement" 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 jsBoolean(false);
return jsBoolean(o2->hasInstance(exec, v1));
}
return jsBoolean(b);
}
// ------------------------------ EqualNode ------------------------------------
// ECMA 11.9
JSValue *EqualNode::evaluate(ExecState *exec)
{
JSValue *v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *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 jsBoolean(result);
}
// ------------------------------ BitOperNode ----------------------------------
// ECMA 11.10
JSValue *BitOperNode::evaluate(ExecState *exec)
{
JSValue *v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *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 jsNumber(result);
}
// ------------------------------ BinaryLogicalNode ----------------------------
// ECMA 11.11
JSValue *BinaryLogicalNode::evaluate(ExecState *exec)
{
JSValue *v1 = expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool b1 = v1->toBoolean(exec);
if ((!b1 && oper == OpAnd) || (b1 && oper == OpOr))
return v1;
JSValue *v2 = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v2;
}
// ------------------------------ ConditionalNode ------------------------------
// ECMA 11.12
JSValue *ConditionalNode::evaluate(ExecState *exec)
{
JSValue *v = logical->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
bool b = v->toBoolean(exec);
if (b)
v = expr1->evaluate(exec);
else
v = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ECMA 11.13
static ALWAYS_INLINE JSValue *valueForReadModifyAssignment(ExecState * exec, JSValue *v1, JSValue *v2, Operator oper) KJS_FAST_CALL;
static ALWAYS_INLINE JSValue *valueForReadModifyAssignment(ExecState * exec, JSValue *v1, JSValue *v2, Operator oper)
{
JSValue *v;
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 = jsNumber(i1 << i2);
break;
case OpRShift:
i1 = v1->toInt32(exec);
i2 = v2->toInt32(exec);
v = jsNumber(i1 >> i2);
break;
case OpURShift:
ui = v1->toUInt32(exec);
i2 = v2->toInt32(exec);
v = jsNumber(ui >> i2);
break;
case OpAndEq:
i1 = v1->toInt32(exec);
i2 = v2->toInt32(exec);
v = jsNumber(i1 & i2);
break;
case OpXOrEq:
i1 = v1->toInt32(exec);
i2 = v2->toInt32(exec);
v = jsNumber(i1 ^ i2);
break;
case OpOrEq:
i1 = v1->toInt32(exec);
i2 = v2->toInt32(exec);
v = jsNumber(i1 | i2);
break;
case OpModEq: {
double d1 = v1->toNumber(exec);
double d2 = v2->toNumber(exec);
v = jsNumber(fmod(d1, d2));
}
break;
default:
assert(0);
v = jsUndefined();
}
return v;
}
// ------------------------------ AssignResolveNode -----------------------------------
JSValue *AssignResolveNode::evaluate(ExecState *exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *base;
do {
base = *iter;
if (base->getPropertySlot(exec, m_ident, slot))
goto found;
++iter;
} while (iter != end);
if (m_oper != OpEqual)
return throwUndefinedVariableError(exec, m_ident);
found:
JSValue *v;
if (m_oper == OpEqual) {
v = m_right->evaluate(exec);
} else {
JSValue *v1 = slot.getValue(exec, base, m_ident);
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = m_right->evaluate(exec);
v = valueForReadModifyAssignment(exec, v1, v2, m_oper);
}
KJS_CHECKEXCEPTIONVALUE
base->put(exec, m_ident, v);
return v;
}
// ------------------------------ AssignDotNode -----------------------------------
JSValue *AssignDotNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
JSValue *v;
if (m_oper == OpEqual) {
v = m_right->evaluate(exec);
} else {
PropertySlot slot;
JSValue *v1 = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = m_right->evaluate(exec);
v = valueForReadModifyAssignment(exec, v1, v2, m_oper);
}
KJS_CHECKEXCEPTIONVALUE
base->put(exec, m_ident, v);
return v;
}
// ------------------------------ AssignErrorNode -----------------------------------
JSValue* AssignErrorNode::evaluate(ExecState* exec)
{
throwError(exec, ReferenceError, "Left side of assignment is not a reference.");
handleException(exec);
return jsUndefined();
}
// ------------------------------ AssignBracketNode -----------------------------------
JSValue *AssignBracketNode::evaluate(ExecState *exec)
{
JSValue *baseValue = m_base->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *subscript = m_subscript->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSObject *base = baseValue->toObject(exec);
uint32_t propertyIndex;
if (subscript->getUInt32(propertyIndex)) {
JSValue *v;
if (m_oper == OpEqual) {
v = m_right->evaluate(exec);
} else {
PropertySlot slot;
JSValue *v1 = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = m_right->evaluate(exec);
v = valueForReadModifyAssignment(exec, v1, v2, m_oper);
}
KJS_CHECKEXCEPTIONVALUE
base->put(exec, propertyIndex, v);
return v;
}
Identifier propertyName(subscript->toString(exec));
JSValue *v;
if (m_oper == OpEqual) {
v = m_right->evaluate(exec);
} else {
PropertySlot slot;
JSValue *v1 = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
KJS_CHECKEXCEPTIONVALUE
JSValue *v2 = m_right->evaluate(exec);
v = valueForReadModifyAssignment(exec, v1, v2, m_oper);
}
KJS_CHECKEXCEPTIONVALUE
base->put(exec, propertyName, v);
return v;
}
// ------------------------------ CommaNode ------------------------------------
// ECMA 11.14
JSValue *CommaNode::evaluate(ExecState *exec)
{
expr1->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
JSValue *v = expr2->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ------------------------------ AssignExprNode -------------------------------
// ECMA 12.2
JSValue *AssignExprNode::evaluate(ExecState *exec)
{
return expr->evaluate(exec);
}
// ------------------------------ VarDeclNode ----------------------------------
VarDeclNode::VarDeclNode(const Identifier &id, AssignExprNode *in, Type t)
: varType(t), ident(id), init(in)
{
}
JSValue* VarDeclNode::handleSlowCase(ExecState* exec, const ScopeChain& chain, JSValue* val)
{
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
ASSERT(iter != end);
PropertySlot slot;
JSObject* base;
do {
base = *iter;
if (base->getPropertySlot(exec, ident, slot))
break;
++iter;
} while (iter != end);
unsigned flags = 0;
base->getPropertyAttributes(ident, flags);
if (varType == VarDeclNode::Constant)
flags |= ReadOnly;
base->put(exec, ident, val, flags);
return 0;
}
// ECMA 12.2
JSValue* VarDeclNode::evaluate(ExecState* exec)
{
const ScopeChain& chain = exec->context()->scopeChain();
JSObject* variableObject = exec->context()->variableObject();
ASSERT(!chain.isEmpty());
bool inGlobalScope = ++chain.begin() == chain.end();
if (inGlobalScope && (init || !variableObject->getDirect(ident))) {
JSValue* val = init ? init->evaluate(exec) : jsUndefined();
int flags = Internal;
if (exec->context()->codeType() != EvalCode)
flags |= DontDelete;
if (varType == VarDeclNode::Constant)
flags |= ReadOnly;
variableObject->putDirect(ident, val, flags);
} else if (init) {
JSValue* val = init->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
// if the variable object is the top of the scope chain, then that must
// be where this variable is declared, processVarDecls would have put
// it there. Don't search the scope chain, to optimize this very common case.
if (chain.top() != variableObject)
return handleSlowCase(exec, chain, val);
unsigned flags = 0;
variableObject->getPropertyAttributes(ident, flags);
if (varType == VarDeclNode::Constant)
flags |= ReadOnly;
ASSERT(variableObject->getDirect(ident) || ident == exec->propertyNames().arguments);
variableObject->put(exec, ident, val, flags);
}
// no caller of this function actually uses the return value.
// FIXME: It would be better to change the inheritence hierarchy so this
// node doesn't even have an evaluate method, but instead a differently named
// one with a void return.
return 0;
}
void VarDeclNode::processVarDecls(ExecState *exec)
{
JSObject* variable = exec->context()->variableObject();
// If a variable by this name already exists, don't clobber it -
// it might be a function parameter
if (!variable->hasProperty(exec, ident)) {
int flags = Internal;
if (exec->context()->codeType() != EvalCode)
flags |= DontDelete;
if (varType == VarDeclNode::Constant)
flags |= ReadOnly;
variable->put(exec, ident, jsUndefined(), flags);
}
}
// ------------------------------ VarDeclListNode ------------------------------
// ECMA 12.2
JSValue *VarDeclListNode::evaluate(ExecState *exec)
{
for (VarDeclListNode *n = this; n; n = n->next.get()) {
n->var->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
}
return jsUndefined();
}
void VarDeclListNode::processVarDecls(ExecState *exec)
{
for (VarDeclListNode *n = this; n; n = n->next.get())
n->var->processVarDecls(exec);
}
void VarDeclListNode::breakCycle()
{
next = 0;
}
// ------------------------------ VarStatementNode -----------------------------
// ECMA 12.2
Completion VarStatementNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
(void) next->evaluate(exec);
KJS_CHECKEXCEPTION
return Completion(Normal);
}
void VarStatementNode::processVarDecls(ExecState *exec)
{
next->processVarDecls(exec);
}
// ------------------------------ BlockNode ------------------------------------
BlockNode::BlockNode(SourceElementsNode *s)
{
if (s) {
source = s->next.release();
Parser::removeNodeCycle(source.get());
setLoc(s->firstLine(), s->lastLine());
} else {
source = 0;
}
}
// 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 *)
{
return Completion(Normal);
}
// ------------------------------ ExprStatementNode ----------------------------
// ECMA 12.4
Completion ExprStatementNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
return Completion(Normal, v);
}
// ------------------------------ IfNode ---------------------------------------
// ECMA 12.5
Completion IfNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
bool b = v->toBoolean(exec);
// if ... then
if (b)
return statement1->execute(exec);
// no else
if (!statement2)
return Completion(Normal);
// else
return statement2->execute(exec);
}
void IfNode::processVarDecls(ExecState *exec)
{
statement1->processVarDecls(exec);
if (statement2)
statement2->processVarDecls(exec);
}
// ------------------------------ DoWhileNode ----------------------------------
// ECMA 12.6.1
Completion DoWhileNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *bv;
Completion c;
do {
// bail out on error
KJS_CHECKEXCEPTION
exec->context()->pushIteration();
c = statement->execute(exec);
exec->context()->popIteration();
if (exec->dynamicInterpreter()->timedOut())
return Completion(Interrupted);
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
if ((c.complType() == Break) && ls.contains(c.target()))
return Completion(Normal, 0);
if (c.complType() != Normal)
return c;
}
bv = expr->evaluate(exec);
KJS_CHECKEXCEPTION
} while (bv->toBoolean(exec));
return Completion(Normal, 0);
}
void DoWhileNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ WhileNode ------------------------------------
// ECMA 12.6.2
Completion WhileNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *bv;
Completion c;
bool b(false);
JSValue *value = 0;
while (1) {
bv = expr->evaluate(exec);
KJS_CHECKEXCEPTION
b = bv->toBoolean(exec);
// bail out on error
KJS_CHECKEXCEPTION
if (!b)
return Completion(Normal, value);
exec->context()->pushIteration();
c = statement->execute(exec);
exec->context()->popIteration();
if (exec->dynamicInterpreter()->timedOut())
return Completion(Interrupted);
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;
}
return Completion(); // work around gcc 4.0 bug
}
void WhileNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ ForNode --------------------------------------
// ECMA 12.6.3
Completion ForNode::execute(ExecState *exec)
{
JSValue *v, *cval = 0;
if (expr1) {
v = expr1->evaluate(exec);
KJS_CHECKEXCEPTION
}
while (1) {
if (expr2) {
v = expr2->evaluate(exec);
KJS_CHECKEXCEPTION
if (!v->toBoolean(exec))
return Completion(Normal, cval);
}
// bail out on error
KJS_CHECKEXCEPTION
exec->context()->pushIteration();
Completion c = statement->execute(exec);
exec->context()->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 (exec->dynamicInterpreter()->timedOut())
return Completion(Interrupted);
if (expr3) {
v = expr3->evaluate(exec);
KJS_CHECKEXCEPTION
}
}
return Completion(); // work around gcc 4.0 bug
}
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.get(), VarDeclNode::Variable);
lexpr = new ResolveNode(ident);
}
// ECMA 12.6.4
Completion ForInNode::execute(ExecState *exec)
{
JSValue *e;
JSValue *retval = 0;
JSObject *v;
Completion c;
PropertyNameArray propertyNames;
if (varDecl) {
varDecl->evaluate(exec);
KJS_CHECKEXCEPTION
}
e = 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 (e->isUndefinedOrNull()) {
return Completion(Normal, 0);
}
KJS_CHECKEXCEPTION
v = e->toObject(exec);
v->getPropertyNames(exec, propertyNames);
PropertyNameArrayIterator end = propertyNames.end();
for (PropertyNameArrayIterator it = propertyNames.begin(); it != end; ++it) {
const Identifier &name = *it;
if (!v->hasProperty(exec, name))
continue;
JSValue *str = jsOwnedString(name.ustring());
if (lexpr->isResolveNode()) {
const Identifier &ident = static_cast<ResolveNode *>(lexpr.get())->identifier();
const ScopeChain& chain = exec->context()->scopeChain();
ScopeChainIterator iter = chain.begin();
ScopeChainIterator end = chain.end();
// we must always have something in the scope chain
assert(iter != end);
PropertySlot slot;
JSObject *o;
do {
o = *iter;
if (o->getPropertySlot(exec, ident, slot)) {
o->put(exec, ident, str);
break;
}
++iter;
} while (iter != end);
if (iter == end)
o->put(exec, ident, str);
} else if (lexpr->isDotAccessorNode()) {
const Identifier& ident = static_cast<DotAccessorNode *>(lexpr.get())->identifier();
JSValue *v = static_cast<DotAccessorNode *>(lexpr.get())->base()->evaluate(exec);
KJS_CHECKEXCEPTION
JSObject *o = v->toObject(exec);
o->put(exec, ident, str);
} else {
assert(lexpr->isBracketAccessorNode());
JSValue *v = static_cast<BracketAccessorNode *>(lexpr.get())->base()->evaluate(exec);
KJS_CHECKEXCEPTION
JSValue *v2 = static_cast<BracketAccessorNode *>(lexpr.get())->subscript()->evaluate(exec);
KJS_CHECKEXCEPTION
JSObject *o = v->toObject(exec);
uint32_t i;
if (v2->getUInt32(i))
o->put(exec, i, str);
o->put(exec, Identifier(v2->toString(exec)), str);
}
KJS_CHECKEXCEPTION
exec->context()->pushIteration();
c = statement->execute(exec);
exec->context()->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;
}
}
}
// bail out on error
KJS_CHECKEXCEPTION
return Completion(Normal, retval);
}
void ForInNode::processVarDecls(ExecState *exec)
{
if (varDecl)
varDecl->processVarDecls(exec);
statement->processVarDecls(exec);
}
// ------------------------------ ContinueNode ---------------------------------
// ECMA 12.7
Completion ContinueNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
if (ident.isEmpty() && !exec->context()->inIteration())
return createErrorCompletion(exec, SyntaxError, "Invalid continue statement.");
else if (!ident.isEmpty() && !exec->context()->seenLabels()->contains(ident))
return createErrorCompletion(exec, SyntaxError, "Label %s not found.", ident);
else
return Completion(Continue, 0, ident);
}
// ------------------------------ BreakNode ------------------------------------
// ECMA 12.8
Completion BreakNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
if (ident.isEmpty() && !exec->context()->inIteration() &&
!exec->context()->inSwitch())
return createErrorCompletion(exec, SyntaxError, "Invalid break statement.");
else if (!ident.isEmpty() && !exec->context()->seenLabels()->contains(ident))
return createErrorCompletion(exec, SyntaxError, "Label %s not found.");
else
return Completion(Break, 0, ident);
}
// ------------------------------ ReturnNode -----------------------------------
// ECMA 12.9
Completion ReturnNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
CodeType codeType = exec->context()->codeType();
if (codeType != FunctionCode && codeType != AnonymousCode ) {
return createErrorCompletion(exec, SyntaxError, "Invalid return statement.");
}
if (!value)
return Completion(ReturnValue, jsUndefined());
JSValue *v = value->evaluate(exec);
KJS_CHECKEXCEPTION
return Completion(ReturnValue, v);
}
// ------------------------------ WithNode -------------------------------------
// ECMA 12.10
Completion WithNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
JSObject *o = v->toObject(exec);
KJS_CHECKEXCEPTION
exec->context()->pushScope(o);
Completion res = statement->execute(exec);
exec->context()->popScope();
return res;
}
void WithNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ CaseClauseNode -------------------------------
// ECMA 12.11
JSValue *CaseClauseNode::evaluate(ExecState *exec)
{
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTIONVALUE
return v;
}
// ECMA 12.11
Completion CaseClauseNode::evalStatements(ExecState *exec)
{
if (source)
return source->execute(exec);
else
return Completion(Normal, jsUndefined());
}
void CaseClauseNode::processVarDecls(ExecState *exec)
{
if (source)
source->processVarDecls(exec);
}
void CaseClauseNode::processFuncDecl(ExecState* exec)
{
if (source)
source->processFuncDecl(exec);
}
// ------------------------------ ClauseListNode -------------------------------
JSValue *ClauseListNode::evaluate(ExecState *)
{
// should never be called
assert(false);
return 0;
}
// ECMA 12.11
void ClauseListNode::processVarDecls(ExecState *exec)
{
for (ClauseListNode *n = this; n; n = n->next.get())
if (n->clause)
n->clause->processVarDecls(exec);
}
void ClauseListNode::processFuncDecl(ExecState* exec)
{
for (ClauseListNode* n = this; n; n = n->next.get())
if (n->clause)
n->clause->processFuncDecl(exec);
}
void ClauseListNode::breakCycle()
{
next = 0;
}
// ------------------------------ CaseBlockNode --------------------------------
CaseBlockNode::CaseBlockNode(ClauseListNode *l1, CaseClauseNode *d,
ClauseListNode *l2)
{
if (l1) {
list1 = l1->next.release();
Parser::removeNodeCycle(list1.get());
} else {
list1 = 0;
}
def = d;
if (l2) {
list2 = l2->next.release();
Parser::removeNodeCycle(list2.get());
} else {
list2 = 0;
}
}
JSValue *CaseBlockNode::evaluate(ExecState *)
{
// should never be called
assert(false);
return 0;
}
// ECMA 12.11
Completion CaseBlockNode::evalBlock(ExecState *exec, JSValue *input)
{
JSValue *v;
Completion res;
ClauseListNode *a = list1.get();
ClauseListNode *b = list2.get();
CaseClauseNode *clause;
while (a) {
clause = a->getClause();
a = a->getNext();
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->getClause()->evalStatements(exec);
if (res.complType() != Normal)
return res;
a = a->getNext();
}
break;
}
}
while (b) {
clause = b->getClause();
b = b->getNext();
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.get();
step18:
while (b) {
clause = b->getClause();
res = clause->evalStatements(exec);
if (res.complType() != Normal)
return res;
b = b->getNext();
}
// 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);
}
void CaseBlockNode::processFuncDecl(ExecState* exec)
{
if (list1)
list1->processFuncDecl(exec);
if (def)
def->processFuncDecl(exec);
if (list2)
list2->processFuncDecl(exec);
}
// ------------------------------ SwitchNode -----------------------------------
// ECMA 12.11
Completion SwitchNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
exec->context()->pushSwitch();
Completion res = block->evalBlock(exec,v);
exec->context()->popSwitch();
if ((res.complType() == Break) && ls.contains(res.target()))
return Completion(Normal, res.value());
return res;
}
void SwitchNode::processVarDecls(ExecState *exec)
{
block->processVarDecls(exec);
}
void SwitchNode::processFuncDecl(ExecState* exec)
{
block->processFuncDecl(exec);
}
// ------------------------------ LabelNode ------------------------------------
// ECMA 12.12
Completion LabelNode::execute(ExecState *exec)
{
if (!exec->context()->seenLabels()->push(label))
return createErrorCompletion(exec, SyntaxError, "Duplicated label %s found.", label);
Completion e = statement->execute(exec);
exec->context()->seenLabels()->pop();
if ((e.complType() == Break) && (e.target() == label))
return Completion(Normal, e.value());
return e;
}
void LabelNode::processVarDecls(ExecState *exec)
{
statement->processVarDecls(exec);
}
// ------------------------------ ThrowNode ------------------------------------
// ECMA 12.13
Completion ThrowNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
JSValue *v = expr->evaluate(exec);
KJS_CHECKEXCEPTION
handleException(exec, v);
return Completion(Throw, v);
}
// ------------------------------ TryNode --------------------------------------
// ECMA 12.14
Completion TryNode::execute(ExecState *exec)
{
KJS_BREAKPOINT;
Completion c = tryBlock->execute(exec);
if (catchBlock && c.complType() == Throw) {
JSObject *obj = new JSObject;
obj->put(exec, exceptionIdent, c.value(), DontDelete);
exec->context()->pushScope(obj);
c = catchBlock->execute(exec);
exec->context()->popScope();
}
if (finallyBlock) {
Completion c2 = finallyBlock->execute(exec);
if (c2.complType() != Normal)
c = c2;
}
return c;
}
void TryNode::processVarDecls(ExecState *exec)
{
tryBlock->processVarDecls(exec);
if (catchBlock)
catchBlock->processVarDecls(exec);
if (finallyBlock)
finallyBlock->processVarDecls(exec);
}
// ------------------------------ ParameterNode --------------------------------
// ECMA 13
JSValue *ParameterNode::evaluate(ExecState *)
{
return jsUndefined();
}
void ParameterNode::breakCycle()
{
next = 0;
}
// ------------------------------ FunctionBodyNode -----------------------------
FunctionBodyNode::FunctionBodyNode(SourceElementsNode *s)
: BlockNode(s)
, m_sourceURL(Lexer::curr()->sourceURL())
, m_sourceId(Parser::sid)
{
setLoc(-1, -1);
}
void FunctionBodyNode::processFuncDecl(ExecState *exec)
{
if (source)
source->processFuncDecl(exec);
}
void FunctionBodyNode::addParam(const Identifier& ident)
{
m_parameters.append(Parameter(ident));
}
UString FunctionBodyNode::paramString() const
{
UString s("");
size_t count = numParams();
for (size_t pos = 0; pos < count; ++pos) {
if (!s.isEmpty())
s += ", ";
s += paramName(pos).ustring();
}
return s;
}
// ------------------------------ FuncDeclNode ---------------------------------
void FuncDeclNode::addParams()
{
for (ParameterNode *p = param.get(); p != 0L; p = p->nextParam())
body->addParam(p->ident());
}
// ECMA 13
void FuncDeclNode::processFuncDecl(ExecState *exec)
{
Context *context = exec->context();
// TODO: let this be an object with [[Class]] property "Function"
FunctionImp *func = new DeclaredFunctionImp(exec, ident, body.get(), context->scopeChain());
JSObject *proto = exec->lexicalInterpreter()->builtinObject()->construct(exec, List::empty());
proto->put(exec, exec->propertyNames().constructor, func, ReadOnly | DontDelete | DontEnum);
func->put(exec, exec->propertyNames().prototype, proto, Internal|DontDelete);
func->put(exec, exec->propertyNames().length, jsNumber(body->numParams()), ReadOnly|DontDelete|DontEnum);
// ECMA 10.2.2
context->variableObject()->put(exec, ident, func, Internal | (context->codeType() == EvalCode ? 0 : DontDelete));
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
JSObject *oldVar = context->variableObject();
context->setVariableObject(func);
context->pushScope(func);
body->processFuncDecl(exec);
context->popScope();
context->setVariableObject(oldVar);
}
}
Completion FuncDeclNode::execute(ExecState *)
{
return Completion(Normal);
}
// ------------------------------ FuncExprNode ---------------------------------
// ECMA 13
void FuncExprNode::addParams()
{
for (ParameterNode *p = param.get(); p != 0L; p = p->nextParam())
body->addParam(p->ident());
}
JSValue *FuncExprNode::evaluate(ExecState *exec)
{
Context *context = exec->context();
bool named = !ident.isNull();
JSObject *functionScopeObject = 0;
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 = new JSObject;
context->pushScope(functionScopeObject);
}
FunctionImp* func = new DeclaredFunctionImp(exec, ident, body.get(), context->scopeChain());
JSObject* proto = exec->lexicalInterpreter()->builtinObject()->construct(exec, List::empty());
proto->put(exec, exec->propertyNames().constructor, func, ReadOnly | DontDelete | DontEnum);
func->put(exec, exec->propertyNames().prototype, proto, Internal | DontDelete);
if (named) {
functionScopeObject->put(exec, ident, func, Internal | ReadOnly | (context->codeType() == EvalCode ? 0 : DontDelete));
context->popScope();
}
return func;
}
// ------------------------------ SourceElementsNode ---------------------------
int SourceElementsNode::count = 0;
SourceElementsNode::SourceElementsNode(StatementNode *s1)
: node(s1), next(this)
{
Parser::noteNodeCycle(this);
setLoc(s1->firstLine(), s1->lastLine());
}
SourceElementsNode::SourceElementsNode(SourceElementsNode *s1, StatementNode *s2)
: node(s2), next(s1->next)
{
s1->next = this;
setLoc(s1->firstLine(), s2->lastLine());
}
// ECMA 14
Completion SourceElementsNode::execute(ExecState *exec)
{
KJS_CHECKEXCEPTION
Completion c1 = node->execute(exec);
KJS_CHECKEXCEPTION;
if (c1.complType() != Normal)
return c1;
for (SourceElementsNode *n = next.get(); n; n = n->next.get()) {
Completion c2 = n->node->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())
c1 = c2;
}
return c1;
}
// ECMA 14
void SourceElementsNode::processFuncDecl(ExecState *exec)
{
for (SourceElementsNode *n = this; n; n = n->next.get())
n->node->processFuncDecl(exec);
}
void SourceElementsNode::processVarDecls(ExecState *exec)
{
for (SourceElementsNode *n = this; n; n = n->next.get())
n->node->processVarDecls(exec);
}
void SourceElementsNode::breakCycle()
{
next = 0;
}
ProgramNode::ProgramNode(SourceElementsNode *s) : FunctionBodyNode(s)
{
}