You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3138 lines
67 KiB
3138 lines
67 KiB
15 years ago
|
// -*- c-basic-offset: 2 -*-
|
||
|
/*
|
||
|
* This file is part of the KDE libraries
|
||
|
* Copyright (C) 1999-2002, 2003 Harri Porten (porten@kde.org)
|
||
|
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
|
||
|
* Copyright (C) 2003 Apple Computer, Inc.
|
||
|
*
|
||
|
* This library is free software; you can redistribute it and/or
|
||
|
* modify it under the terms of the GNU Library General Public
|
||
|
* License as published by the Free Software Foundation; either
|
||
|
* version 2 of the License, or (at your option) any later version.
|
||
|
*
|
||
|
* This library is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
|
* Library General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Library General Public License
|
||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||
|
* Boston, MA 02110-1301, USA.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "nodes.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
#include <assert.h>
|
||
|
#ifdef KJS_DEBUG_MEM
|
||
|
#include <stdio.h>
|
||
|
#include <typeinfo>
|
||
|
#endif
|
||
|
#ifdef KJS_VERBOSE
|
||
|
#include <iostream>
|
||
|
using namespace std;
|
||
|
#endif
|
||
|
|
||
|
#include "collector.h"
|
||
|
#include "context.h"
|
||
|
#include "debugger.h"
|
||
|
#include "function_object.h"
|
||
|
#include "internal.h"
|
||
|
#include "value.h"
|
||
|
#include "object.h"
|
||
|
#include "types.h"
|
||
|
#include "interpreter.h"
|
||
|
#include "lexer.h"
|
||
|
#include "operations.h"
|
||
|
#include "ustring.h"
|
||
|
|
||
|
using namespace KJS;
|
||
|
|
||
|
#define KJS_BREAKPOINT \
|
||
|
if (!hitStatement(exec)) \
|
||
|
return Completion(Normal);
|
||
|
|
||
|
#define KJS_ABORTPOINT \
|
||
|
if (exec->dynamicInterpreter()->imp()->debugger() && \
|
||
|
exec->dynamicInterpreter()->imp()->debugger()->imp()->aborted()) \
|
||
|
return Completion(Normal);
|
||
|
|
||
|
#define KJS_CHECKEXCEPTION \
|
||
|
if (exec->hadException()) { \
|
||
|
setExceptionDetailsIfNeeded(exec); \
|
||
|
return Completion(Throw, exec->exception()); \
|
||
|
} \
|
||
|
if (Collector::outOfMemory()) \
|
||
|
return Completion(Throw, Error::create(exec,GeneralError,"Out of memory"));
|
||
|
|
||
|
#define KJS_CHECKEXCEPTIONVALUE \
|
||
|
if (exec->hadException()) { \
|
||
|
setExceptionDetailsIfNeeded(exec); \
|
||
|
return exec->exception(); \
|
||
|
} \
|
||
|
if (Collector::outOfMemory()) \
|
||
|
return Undefined(); // will be picked up by KJS_CHECKEXCEPTION
|
||
|
|
||
|
#define KJS_CHECKEXCEPTIONREFERENCE \
|
||
|
if (exec->hadException()) { \
|
||
|
setExceptionDetailsIfNeeded(exec); \
|
||
|
return Reference::makeValueReference(Undefined()); \
|
||
|
} \
|
||
|
if (Collector::outOfMemory()) \
|
||
|
return Reference::makeValueReference(Undefined()); // will be picked up by KJS_CHECKEXCEPTION
|
||
|
|
||
|
#define KJS_CHECKEXCEPTIONLIST \
|
||
|
if (exec->hadException()) { \
|
||
|
setExceptionDetailsIfNeeded(exec); \
|
||
|
return List(); \
|
||
|
} \
|
||
|
if (Collector::outOfMemory()) \
|
||
|
return List(); // will be picked up by KJS_CHECKEXCEPTION
|
||
|
|
||
|
#ifdef KJS_DEBUG_MEM
|
||
|
std::list<Node *> * Node::s_nodes = 0L;
|
||
|
#endif
|
||
|
|
||
|
// ----------------------------- Node -----------------------------------------
|
||
|
|
||
|
Node::Node()
|
||
|
{
|
||
|
line = Lexer::curr()->lineNo();
|
||
|
refcount = 0;
|
||
|
#ifdef KJS_DEBUG_MEM
|
||
|
if (!s_nodes)
|
||
|
s_nodes = new std::list<Node *>;
|
||
|
s_nodes->push_back(this);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
Node::~Node()
|
||
|
{
|
||
|
#ifdef KJS_DEBUG_MEM
|
||
|
s_nodes->remove( this );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
Reference Node::evaluateReference(ExecState *exec) const
|
||
|
{
|
||
|
Value v = evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONREFERENCE
|
||
|
return Reference::makeValueReference(v);
|
||
|
}
|
||
|
|
||
|
// fallback for those nodes without a evaluate() reimplementation
|
||
|
// TODO: reimplemint in each sub class, make Node::evaluate() pure virtual
|
||
|
Value Node::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
// fprintf(stderr, "%s::evaluate()\n", typeid(*this).name());
|
||
|
return evaluateReference(exec).getValue(exec);
|
||
|
}
|
||
|
|
||
|
bool Node::toBoolean(ExecState *exec) const
|
||
|
{
|
||
|
// fprintf(stderr, "Node(%s)::toBoolean()\n", typeid(*this).name());
|
||
|
return evaluate(exec).toBoolean(exec);
|
||
|
}
|
||
|
|
||
|
double Node::toNumber(ExecState *exec) const
|
||
|
{
|
||
|
// fprintf(stderr, "Node(%s)::toNumber()\n", typeid(*this).name());
|
||
|
return evaluate(exec).toNumber(exec);
|
||
|
}
|
||
|
|
||
|
UString Node::toString(ExecState *exec) const
|
||
|
{
|
||
|
return evaluate(exec).toString(exec);
|
||
|
}
|
||
|
|
||
|
#ifdef KJS_DEBUG_MEM
|
||
|
void Node::finalCheck()
|
||
|
{
|
||
|
if (!s_nodes) {
|
||
|
fprintf(stderr, "Node::finalCheck(): list 0\n");
|
||
|
return;
|
||
|
}
|
||
|
fprintf( stderr, "Node::finalCheck(): list count : %d\n", (int)s_nodes->size() );
|
||
|
std::list<Node *>::iterator it = s_nodes->begin();
|
||
|
for ( uint i = 0; it != s_nodes->end() ; ++it, ++i )
|
||
|
fprintf( stderr, "[%d] Still having node %p (%s) (refcount %d)\n", i, (void*)*it, typeid( **it ).name(), (*it)->refcount );
|
||
|
delete s_nodes;
|
||
|
s_nodes = 0L;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
Value Node::throwError(ExecState *exec, ErrorType e, const char *msg) const
|
||
|
{
|
||
|
Object err = Error::create(exec, e, msg, lineNo(), sourceId());
|
||
|
exec->setException(err);
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
Value Node::throwError(ExecState *exec, ErrorType e, const char *msg,
|
||
|
const Value &v, const Node *expr) const
|
||
|
{
|
||
|
char *vStr = strdup(v.toString(exec).ascii());
|
||
|
char *exprStr = strdup(expr->toCode().ascii());
|
||
|
|
||
|
int length = strlen(msg) - 4 /* two %s */ + strlen(vStr) + strlen(exprStr) + 1 /* null terminator */;
|
||
|
char *str = new char[length];
|
||
|
sprintf(str, msg, vStr, exprStr);
|
||
|
free(vStr);
|
||
|
free(exprStr);
|
||
|
|
||
|
Value result = throwError(exec, e, str);
|
||
|
delete [] str;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
Value Node::throwError(ExecState *exec, ErrorType e, const char *msg, Identifier label) const
|
||
|
{
|
||
|
const char *l = label.ascii();
|
||
|
int length = strlen(msg) - 2 /* %s */ + strlen(l) + 1 /* null terminator */;
|
||
|
char *message = new char[length];
|
||
|
sprintf(message, msg, l);
|
||
|
|
||
|
Value result = throwError(exec, e, message);
|
||
|
delete [] message;
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void Node::setExceptionDetailsIfNeeded(ExecState *exec) const
|
||
|
{
|
||
|
if (exec->hadException()) {
|
||
|
Object exception = exec->exception().toObject(exec);
|
||
|
if (!exception.hasProperty(exec, "line") /* &&
|
||
|
!exception.hasProperty(exec, "sourceURL")*/ ) {
|
||
|
exception.put(exec, "line", Number(line));
|
||
|
// exception.put(exec, "sourceURL", String(sourceURL));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------- StatementNode --------------------------------
|
||
|
StatementNode::StatementNode() : l0(-1), l1(-1), sourceCode(0), breakPoint(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
StatementNode::~StatementNode()
|
||
|
{
|
||
|
if (sourceCode)
|
||
|
sourceCode->deref();
|
||
|
}
|
||
|
|
||
|
void StatementNode::setLoc(int line0, int line1, SourceCode *src)
|
||
|
{
|
||
|
// ### require these to be passed to the constructor
|
||
|
l0 = line0;
|
||
|
l1 = line1;
|
||
|
if (sourceCode != src) {
|
||
|
if (sourceCode)
|
||
|
sourceCode->deref();
|
||
|
sourceCode = src;
|
||
|
sourceCode->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// return true if the debugger wants us to stop at this point
|
||
|
bool StatementNode::hitStatement(ExecState *exec)
|
||
|
{
|
||
|
assert(sourceCode);
|
||
|
assert(exec->context().imp()->sourceId == sourceCode->sid);
|
||
|
exec->context().imp()->setLines(l0,l1);
|
||
|
Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
|
||
|
if (dbg)
|
||
|
return dbg->atStatement(exec);
|
||
|
else
|
||
|
return true; // continue
|
||
|
}
|
||
|
|
||
|
// return true if the debugger wants us to stop at this point
|
||
|
bool StatementNode::abortStatement(ExecState *exec)
|
||
|
{
|
||
|
Debugger *dbg = exec->dynamicInterpreter()->imp()->debugger();
|
||
|
if (dbg)
|
||
|
return dbg->imp()->aborted();
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void StatementNode::processFuncDecl(ExecState *)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// ----------------------------- NullNode -------------------------------------
|
||
|
|
||
|
Value NullNode::evaluate(ExecState *) const
|
||
|
{
|
||
|
return Null();
|
||
|
}
|
||
|
|
||
|
bool NullNode::toBoolean(ExecState *) const
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
double NullNode::toNumber(ExecState *) const
|
||
|
{
|
||
|
return 0.0;
|
||
|
}
|
||
|
|
||
|
UString NullNode::toString(ExecState *) const
|
||
|
{
|
||
|
return "null";
|
||
|
}
|
||
|
|
||
|
// ----------------------------- BooleanNode ----------------------------------
|
||
|
|
||
|
Value BooleanNode::evaluate(ExecState *) const
|
||
|
{
|
||
|
return Boolean(val);
|
||
|
}
|
||
|
|
||
|
bool BooleanNode::toBoolean(ExecState *) const
|
||
|
{
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
double BooleanNode::toNumber(ExecState *) const
|
||
|
{
|
||
|
return val ? 1.0 : 0.0;
|
||
|
}
|
||
|
|
||
|
UString BooleanNode::toString(ExecState *) const
|
||
|
{
|
||
|
return val ? "true" : "false";
|
||
|
}
|
||
|
|
||
|
// ----------------------------- NumberNode -----------------------------------
|
||
|
|
||
|
Value NumberNode::evaluate(ExecState *) const
|
||
|
{
|
||
|
return Number(val);
|
||
|
}
|
||
|
|
||
|
bool NumberNode::toBoolean(ExecState *) const
|
||
|
{
|
||
|
return !((val == 0) /* || (iVal() == N0) */ || isNaN(val));
|
||
|
}
|
||
|
|
||
|
double NumberNode::toNumber(ExecState *) const
|
||
|
{
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
UString NumberNode::toString(ExecState *) const
|
||
|
{
|
||
|
return UString::from(val);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- StringNode -----------------------------------
|
||
|
|
||
|
Value StringNode::evaluate(ExecState *) const
|
||
|
{
|
||
|
return String(val);
|
||
|
}
|
||
|
|
||
|
bool StringNode::toBoolean(ExecState *) const
|
||
|
{
|
||
|
return !val.isEmpty();
|
||
|
}
|
||
|
|
||
|
double StringNode::toNumber(ExecState *) const
|
||
|
{
|
||
|
return val.toDouble();
|
||
|
}
|
||
|
|
||
|
UString StringNode::toString(ExecState *) const
|
||
|
{
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- RegExpNode -----------------------------------
|
||
|
|
||
|
Value RegExpNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
List list;
|
||
|
String p(pattern);
|
||
|
String f(flags);
|
||
|
list.append(p);
|
||
|
list.append(f);
|
||
|
|
||
|
Object reg = exec->lexicalInterpreter()->imp()->builtinRegExp();
|
||
|
return reg.construct(exec,list);
|
||
|
}
|
||
|
|
||
|
bool RegExpNode::toBoolean(ExecState *) const
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ThisNode -------------------------------------
|
||
|
|
||
|
// ECMA 11.1.1
|
||
|
Value ThisNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
return exec->context().imp()->thisValue();
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ResolveNode ----------------------------------
|
||
|
|
||
|
// ECMA 11.1.2 & 10.1.4
|
||
|
Value ResolveNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
return evaluateReference(exec).getValue(exec);
|
||
|
}
|
||
|
|
||
|
Reference ResolveNode::evaluateReference(ExecState *exec) const
|
||
|
{
|
||
|
ScopeChain chain = exec->context().imp()->scopeChain();
|
||
|
|
||
|
while (!chain.isEmpty()) {
|
||
|
ObjectImp *o = chain.top();
|
||
|
|
||
|
//cerr << "Resolve: looking at '" << ident.ascii() << "'"
|
||
|
// << " in " << (void*)o << " " << o->classInfo()->className << endl;
|
||
|
if (o->hasProperty(exec,ident)) {
|
||
|
//cerr << "Resolve: FOUND '" << ident.ascii() << "'"
|
||
|
// << " in " << (void*)o << " " << o->classInfo()->className << endl;
|
||
|
return Reference(o, ident);
|
||
|
}
|
||
|
|
||
|
chain.pop();
|
||
|
}
|
||
|
|
||
|
// identifier not found
|
||
|
#ifdef KJS_VERBOSE
|
||
|
cerr << "Resolve::evaluateReference: didn't find '" << ident.ustring().ascii() << "'" << endl;
|
||
|
#endif
|
||
|
return Reference(Null(), ident);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- GroupNode ------------------------------------
|
||
|
|
||
|
void GroupNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( group )
|
||
|
group->ref();
|
||
|
}
|
||
|
|
||
|
bool GroupNode::deref()
|
||
|
{
|
||
|
if ( group && group->deref() )
|
||
|
delete group;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.1.6
|
||
|
Value GroupNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
return group->evaluate(exec);
|
||
|
}
|
||
|
|
||
|
Reference GroupNode::evaluateReference(ExecState *exec) const
|
||
|
{
|
||
|
return group->evaluateReference(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ElementNode ----------------------------------
|
||
|
|
||
|
void ElementNode::ref()
|
||
|
{
|
||
|
for (ElementNode *n = this; n; n = n->list) {
|
||
|
n->Node::ref();
|
||
|
if (n->node)
|
||
|
n->node->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ElementNode::deref()
|
||
|
{
|
||
|
ElementNode *next;
|
||
|
for (ElementNode *n = this; n; n = next) {
|
||
|
next = n->list;
|
||
|
if (n->node && n->node->deref())
|
||
|
delete n->node;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.1.4
|
||
|
Value ElementNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Object array = exec->lexicalInterpreter()->builtinArray().construct(exec, List::empty());
|
||
|
int length = 0;
|
||
|
for (const ElementNode *n = this; n; n = n->list) {
|
||
|
Value val = n->node->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
length += n->elision;
|
||
|
array.put(exec, length++, val);
|
||
|
}
|
||
|
return array;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ArrayNode ------------------------------------
|
||
|
|
||
|
void ArrayNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( element )
|
||
|
element->ref();
|
||
|
}
|
||
|
|
||
|
bool ArrayNode::deref()
|
||
|
{
|
||
|
if ( element && element->deref() )
|
||
|
delete element;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.1.4
|
||
|
Value ArrayNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Object array;
|
||
|
int length;
|
||
|
|
||
|
if (element) {
|
||
|
array = Object(static_cast<ObjectImp*>(element->evaluate(exec).imp()));
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
length = opt ? array.get(exec,lengthPropertyName).toInt32(exec) : 0;
|
||
|
} else {
|
||
|
Value newArr = exec->lexicalInterpreter()->builtinArray().construct(exec,List::empty());
|
||
|
array = Object(static_cast<ObjectImp*>(newArr.imp()));
|
||
|
length = 0;
|
||
|
}
|
||
|
|
||
|
if (opt)
|
||
|
array.put(exec,lengthPropertyName, Number(elision + length), DontEnum | DontDelete);
|
||
|
|
||
|
return array;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ObjectLiteralNode ----------------------------
|
||
|
|
||
|
void ObjectLiteralNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( list )
|
||
|
list->ref();
|
||
|
}
|
||
|
|
||
|
bool ObjectLiteralNode::deref()
|
||
|
{
|
||
|
if ( list && list->deref() )
|
||
|
delete list;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.1.5
|
||
|
Value ObjectLiteralNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
if (list)
|
||
|
return list->evaluate(exec);
|
||
|
|
||
|
return exec->lexicalInterpreter()->builtinObject().construct(exec,List::empty());
|
||
|
}
|
||
|
|
||
|
// ----------------------------- PropertyValueNode ----------------------------
|
||
|
|
||
|
void PropertyValueNode::ref()
|
||
|
{
|
||
|
for (PropertyValueNode *n = this; n; n = n->list) {
|
||
|
n->Node::ref();
|
||
|
if (n->name)
|
||
|
n->name->ref();
|
||
|
if (n->assign)
|
||
|
n->assign->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool PropertyValueNode::deref()
|
||
|
{
|
||
|
PropertyValueNode *next;
|
||
|
for (PropertyValueNode *n = this; n; n = next) {
|
||
|
next = n->list;
|
||
|
if ( n->name && n->name->deref() )
|
||
|
delete n->name;
|
||
|
if ( n->assign && n->assign->deref() )
|
||
|
delete n->assign;
|
||
|
if (n != this && n->Node::deref() )
|
||
|
delete n;
|
||
|
}
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.1.5
|
||
|
Value PropertyValueNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Object obj = exec->lexicalInterpreter()->builtinObject().construct(exec, List::empty());
|
||
|
|
||
|
for (const PropertyValueNode *p = this; p; p = p->list) {
|
||
|
Value n = p->name->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v = p->assign->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
obj.put(exec, Identifier(n.toString(exec)), v);
|
||
|
}
|
||
|
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- PropertyNode ---------------------------------
|
||
|
|
||
|
// ECMA 11.1.5
|
||
|
Value PropertyNode::evaluate(ExecState * /*exec*/) const
|
||
|
{
|
||
|
Value s;
|
||
|
|
||
|
if (str.isNull()) {
|
||
|
s = String(UString::from(numeric));
|
||
|
} else {
|
||
|
s = String(str.ustring());
|
||
|
}
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- AccessorNode1 --------------------------------
|
||
|
|
||
|
void AccessorNode1::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
}
|
||
|
|
||
|
bool AccessorNode1::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.2.1a
|
||
|
Reference AccessorNode1::evaluateReference(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = expr1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONREFERENCE
|
||
|
Value v2 = expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONREFERENCE
|
||
|
#ifndef NDEBUG
|
||
|
// catch errors before being caught in toObject(). better error message.
|
||
|
if (v1.isA(UndefinedType) || v1.isA(NullType)) {
|
||
|
UString s = "Attempted to access property on %s object "
|
||
|
"(result of expression %s)";
|
||
|
(void)throwError(exec, TypeError, s.cstring().c_str(), v1, this);
|
||
|
return Reference::makeValueReference(Undefined());
|
||
|
}
|
||
|
#endif
|
||
|
Object o = v1.toObject(exec);
|
||
|
unsigned i;
|
||
|
if (v2.toUInt32(i))
|
||
|
return Reference(o, i);
|
||
|
UString s = v2.toString(exec);
|
||
|
return Reference(o, Identifier(s));
|
||
|
}
|
||
|
|
||
|
// ----------------------------- AccessorNode2 --------------------------------
|
||
|
|
||
|
void AccessorNode2::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool AccessorNode2::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.2.1b
|
||
|
Reference AccessorNode2::evaluateReference(ExecState *exec) const
|
||
|
{
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONREFERENCE
|
||
|
assert(v.isValid());
|
||
|
#ifndef NDEBUG
|
||
|
// catch errors before being caught in toObject(). better error message.
|
||
|
if (v.isA(UndefinedType) || v.isA(NullType)) {
|
||
|
UString s = "Attempted to access '" + ident.ustring() +
|
||
|
"' property on %s object (result of expression %s)";
|
||
|
(void)throwError(exec, TypeError, s.cstring().c_str(), v, this);
|
||
|
return Reference::makeValueReference(Undefined());
|
||
|
}
|
||
|
#endif
|
||
|
Object o = v.toObject(exec);
|
||
|
return Reference(o, ident);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ArgumentListNode -----------------------------
|
||
|
|
||
|
void ArgumentListNode::ref()
|
||
|
{
|
||
|
for (ArgumentListNode *n = this; n; n = n->list) {
|
||
|
n->Node::ref();
|
||
|
if (n->expr)
|
||
|
n->expr->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ArgumentListNode::deref()
|
||
|
{
|
||
|
ArgumentListNode *next;
|
||
|
for (ArgumentListNode *n = this; n; n = next) {
|
||
|
next = n->list;
|
||
|
if (n->expr && n->expr->deref())
|
||
|
delete n->expr;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
Value ArgumentListNode::evaluate(ExecState * /*exec*/) const
|
||
|
{
|
||
|
assert(0);
|
||
|
return Value(); // dummy, see evaluateList()
|
||
|
}
|
||
|
|
||
|
// ECMA 11.2.4
|
||
|
List ArgumentListNode::evaluateList(ExecState *exec) const
|
||
|
{
|
||
|
List l;
|
||
|
|
||
|
for (const ArgumentListNode *n = this; n; n = n->list) {
|
||
|
Value v = n->expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONLIST
|
||
|
l.append(v);
|
||
|
}
|
||
|
|
||
|
return l;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ArgumentsNode --------------------------------
|
||
|
|
||
|
void ArgumentsNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( list )
|
||
|
list->ref();
|
||
|
}
|
||
|
|
||
|
bool ArgumentsNode::deref()
|
||
|
{
|
||
|
if ( list && list->deref() )
|
||
|
delete list;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
Value ArgumentsNode::evaluate(ExecState * /*exec*/) const
|
||
|
{
|
||
|
assert(0);
|
||
|
return Value(); // dummy, see evaluateList()
|
||
|
}
|
||
|
|
||
|
// ECMA 11.2.4
|
||
|
List ArgumentsNode::evaluateList(ExecState *exec) const
|
||
|
{
|
||
|
if (!list)
|
||
|
return List();
|
||
|
|
||
|
return list->evaluateList(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- NewExprNode ----------------------------------
|
||
|
|
||
|
// ECMA 11.2.2
|
||
|
|
||
|
void NewExprNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
if ( args )
|
||
|
args->ref();
|
||
|
}
|
||
|
|
||
|
bool NewExprNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
if ( args && args->deref() )
|
||
|
delete args;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
Value NewExprNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
List argList;
|
||
|
if (args) {
|
||
|
argList = args->evaluateList(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
}
|
||
|
|
||
|
if (v.type() != ObjectType) {
|
||
|
return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with new.", v, expr);
|
||
|
}
|
||
|
|
||
|
Object constr = Object(static_cast<ObjectImp*>(v.imp()));
|
||
|
if (!constr.implementsConstruct()) {
|
||
|
return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, expr);
|
||
|
}
|
||
|
|
||
|
Value res = constr.construct(exec,argList);
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- FunctionCallNode -----------------------------
|
||
|
|
||
|
void FunctionCallNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
if ( args )
|
||
|
args->ref();
|
||
|
}
|
||
|
|
||
|
bool FunctionCallNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
if ( args && args->deref() )
|
||
|
delete args;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.2.3
|
||
|
Value FunctionCallNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Reference ref = expr->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
List argList = args->evaluateList(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
Value v = ref.getValue(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
if (v.type() != ObjectType) {
|
||
|
return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be called.", v, expr);
|
||
|
}
|
||
|
|
||
|
Object func = Object(static_cast<ObjectImp*>(v.imp()));
|
||
|
|
||
|
if (!func.implementsCall()) {
|
||
|
return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, expr);
|
||
|
}
|
||
|
|
||
|
Value thisVal;
|
||
|
if (ref.isMutable())
|
||
|
thisVal = ref.getBase(exec);
|
||
|
else
|
||
|
thisVal = Null();
|
||
|
|
||
|
if (thisVal.type() == ObjectType &&
|
||
|
Object::dynamicCast(thisVal).inherits(&ActivationImp::info))
|
||
|
thisVal = Null();
|
||
|
|
||
|
if (thisVal.type() != ObjectType) {
|
||
|
// ECMA 11.2.3 says that in this situation the this value should be null.
|
||
|
// However, section 10.2.3 says that in the case where the value provided
|
||
|
// by the caller is null, the global object should be used. It also says
|
||
|
// that the section does not apply to interal functions, but for simplicity
|
||
|
// of implementation we use the global object anyway here. This guarantees
|
||
|
// that in host objects you always get a valid object for this.
|
||
|
// thisVal = Null();
|
||
|
thisVal = exec->dynamicInterpreter()->globalObject();
|
||
|
}
|
||
|
|
||
|
Object thisObj = Object::dynamicCast(thisVal);
|
||
|
Value result = func.call(exec,thisObj, argList);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- PostfixNode ----------------------------------
|
||
|
|
||
|
void PostfixNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool PostfixNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.3
|
||
|
Value PostfixNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Reference ref = expr->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v = ref.getValue(exec);
|
||
|
double n = v.toNumber(exec);
|
||
|
|
||
|
double newValue = (oper == OpPlusPlus) ? n + 1 : n - 1;
|
||
|
|
||
|
ref.putValue(exec, Number(newValue));
|
||
|
|
||
|
return Number(n);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- DeleteNode -----------------------------------
|
||
|
|
||
|
void DeleteNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool DeleteNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.1
|
||
|
Value DeleteNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Reference ref = expr->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
return Boolean(ref.deleteValue(exec));
|
||
|
}
|
||
|
|
||
|
// ----------------------------- VoidNode -------------------------------------
|
||
|
|
||
|
void VoidNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool VoidNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.2
|
||
|
Value VoidNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value dummy1 = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return Undefined();
|
||
|
}
|
||
|
|
||
|
// ----------------------------- TypeOfNode -----------------------------------
|
||
|
|
||
|
void TypeOfNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool TypeOfNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.3
|
||
|
Value TypeOfNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
const char *s = 0L;
|
||
|
Reference ref = expr->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
if (ref.isMutable()) {
|
||
|
Value b = ref.getBase(exec);
|
||
|
if (b.type() == NullType)
|
||
|
return String("undefined");
|
||
|
}
|
||
|
Value v = ref.getValue(exec);
|
||
|
switch (v.type())
|
||
|
{
|
||
|
case UndefinedType:
|
||
|
s = "undefined";
|
||
|
break;
|
||
|
case NullType:
|
||
|
s = "object";
|
||
|
break;
|
||
|
case BooleanType:
|
||
|
s = "boolean";
|
||
|
break;
|
||
|
case NumberType:
|
||
|
s = "number";
|
||
|
break;
|
||
|
case StringType:
|
||
|
s = "string";
|
||
|
break;
|
||
|
default:
|
||
|
if (v.type() == ObjectType && static_cast<ObjectImp*>(v.imp())->implementsCall())
|
||
|
s = "function";
|
||
|
else
|
||
|
s = "object";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return String(s);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- PrefixNode -----------------------------------
|
||
|
|
||
|
void PrefixNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool PrefixNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.4 and 11.4.5
|
||
|
Value PrefixNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Reference ref = expr->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v = ref.getValue(exec);
|
||
|
double n = v.toNumber(exec);
|
||
|
|
||
|
double newValue = (oper == OpPlusPlus) ? n + 1 : n - 1;
|
||
|
Value n2 = Number(newValue);
|
||
|
|
||
|
ref.putValue(exec,n2);
|
||
|
|
||
|
return n2;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- UnaryPlusNode --------------------------------
|
||
|
|
||
|
void UnaryPlusNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool UnaryPlusNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.6
|
||
|
double UnaryPlusNode::toNumber(ExecState *exec) const
|
||
|
{
|
||
|
return expr->toNumber(exec);
|
||
|
}
|
||
|
|
||
|
// could go
|
||
|
Value UnaryPlusNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return Number(v.toNumber(exec)); /* TODO: optimize */
|
||
|
}
|
||
|
|
||
|
// ----------------------------- NegateNode -----------------------------------
|
||
|
|
||
|
void NegateNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool NegateNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.7
|
||
|
double NegateNode::toNumber(ExecState *exec) const
|
||
|
{
|
||
|
return -expr->toNumber(exec);
|
||
|
}
|
||
|
|
||
|
Value NegateNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
double d = -v.toNumber(exec);
|
||
|
|
||
|
return Number(d);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- BitwiseNotNode -------------------------------
|
||
|
|
||
|
void BitwiseNotNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool BitwiseNotNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.8
|
||
|
Value BitwiseNotNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
int i32 = v.toInt32(exec);
|
||
|
|
||
|
return Number(~i32);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- LogicalNotNode -------------------------------
|
||
|
|
||
|
void LogicalNotNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool LogicalNotNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.4.9
|
||
|
bool LogicalNotNode::toBoolean(ExecState *exec) const
|
||
|
{
|
||
|
return !expr->toBoolean(exec);
|
||
|
}
|
||
|
|
||
|
// could remove this
|
||
|
Value LogicalNotNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
bool b = expr->toBoolean(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return Boolean(!b);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- MultNode -------------------------------------
|
||
|
|
||
|
void MultNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( term1 )
|
||
|
term1->ref();
|
||
|
if ( term2 )
|
||
|
term2->ref();
|
||
|
}
|
||
|
|
||
|
bool MultNode::deref()
|
||
|
{
|
||
|
if ( term1 && term1->deref() )
|
||
|
delete term1;
|
||
|
if ( term2 && term2->deref() )
|
||
|
delete term2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.5
|
||
|
Value MultNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = term1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
Value v2 = term2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return mult(exec,v1, v2, oper);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- AddNode --------------------------------------
|
||
|
|
||
|
// factory for an appropriate addition or substraction node
|
||
|
Node* AddNode::create(Node *t1, Node *t2, char op)
|
||
|
{
|
||
|
// ### many more combinations to check for
|
||
|
// fold constants
|
||
|
if ((t1->type() == NumberType || t1->type() == BooleanType) &&
|
||
|
(t2->type() == NumberType || t2->type() == BooleanType)) {
|
||
|
double d = t2->toNumber(0);
|
||
|
Node* n = new NumberNode(t1->toNumber(0) + (op == '+' ? d : -d));
|
||
|
delete t1;
|
||
|
delete t2;
|
||
|
return n;
|
||
|
}
|
||
|
|
||
|
if (op == '+' && t2->type() == StringType)
|
||
|
return new AppendStringNode(t1, t2->toString(0));
|
||
|
|
||
|
// fall back to generic node
|
||
|
return new AddNode(t1, t2, op);
|
||
|
}
|
||
|
|
||
|
void AddNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( term1 )
|
||
|
term1->ref();
|
||
|
if ( term2 )
|
||
|
term2->ref();
|
||
|
}
|
||
|
|
||
|
bool AddNode::deref()
|
||
|
{
|
||
|
if ( term1 && term1->deref() )
|
||
|
delete term1;
|
||
|
if ( term2 && term2->deref() )
|
||
|
delete term2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.6
|
||
|
Value AddNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = term1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
Value v2 = term2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return add(exec,v1, v2, oper);
|
||
|
}
|
||
|
|
||
|
// ------------------------ AddNumberNode ------------------------------------
|
||
|
|
||
|
void AppendStringNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
term->ref();
|
||
|
}
|
||
|
|
||
|
bool AppendStringNode::deref()
|
||
|
{
|
||
|
if (term->deref())
|
||
|
delete term;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.6 (special case of string appending)
|
||
|
Value AppendStringNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
UString s = term->toString(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return String(s + str);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ShiftNode ------------------------------------
|
||
|
|
||
|
void ShiftNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( term1 )
|
||
|
term1->ref();
|
||
|
if ( term2 )
|
||
|
term2->ref();
|
||
|
}
|
||
|
|
||
|
bool ShiftNode::deref()
|
||
|
{
|
||
|
if ( term1 && term1->deref() )
|
||
|
delete term1;
|
||
|
if ( term2 && term2->deref() )
|
||
|
delete term2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.7
|
||
|
Value ShiftNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = term1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v2 = term2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
unsigned int i2 = v2.toUInt32(exec);
|
||
|
i2 &= 0x1f;
|
||
|
|
||
|
switch (oper) {
|
||
|
case OpLShift:
|
||
|
return Number(v1.toInt32(exec) << i2);
|
||
|
case OpRShift:
|
||
|
return Number(v1.toInt32(exec) >> i2);
|
||
|
case OpURShift:
|
||
|
return Number(v1.toUInt32(exec) >> i2);
|
||
|
default:
|
||
|
assert(!"ShiftNode: unhandled switch case");
|
||
|
return Undefined();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------- RelationalNode -------------------------------
|
||
|
|
||
|
void RelationalNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
}
|
||
|
|
||
|
bool RelationalNode::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.8
|
||
|
Value RelationalNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = expr1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v2 = expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
bool b;
|
||
|
if (oper == OpLess || oper == OpGreaterEq) {
|
||
|
int r = relation(exec, v1, v2);
|
||
|
if (r < 0)
|
||
|
b = false;
|
||
|
else
|
||
|
b = (oper == OpLess) ? (r == 1) : (r == 0);
|
||
|
} else if (oper == OpGreater || oper == OpLessEq) {
|
||
|
int r = relation(exec, v2, v1);
|
||
|
if (r < 0)
|
||
|
b = false;
|
||
|
else
|
||
|
b = (oper == OpGreater) ? (r == 1) : (r == 0);
|
||
|
} else if (oper == OpIn) {
|
||
|
// Is all of this OK for host objects?
|
||
|
if (v2.type() != ObjectType)
|
||
|
return throwError(exec, TypeError,
|
||
|
"Value %s (result of expression %s) is not an object. Cannot be used with IN expression.", v2, expr2);
|
||
|
Object o2(static_cast<ObjectImp*>(v2.imp()));
|
||
|
b = o2.hasProperty(exec,Identifier(v1.toString(exec)));
|
||
|
} else {
|
||
|
if (v2.type() != ObjectType)
|
||
|
return throwError(exec, TypeError,
|
||
|
"Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, expr2);
|
||
|
|
||
|
Object o2(static_cast<ObjectImp*>(v2.imp()));
|
||
|
if (!o2.implementsHasInstance()) {
|
||
|
// According to the spec, only some types of objects "imlement" the [[HasInstance]] property.
|
||
|
// But we are supposed to throw an exception where the object does not "have" the [[HasInstance]]
|
||
|
// property. It seems that all object have the property, but not all implement it, so in this
|
||
|
// case we return false (consistent with mozilla)
|
||
|
return Boolean(false);
|
||
|
// return throwError(exec, TypeError,
|
||
|
// "Object does not implement the [[HasInstance]] method." );
|
||
|
}
|
||
|
return o2.hasInstance(exec, v1);
|
||
|
}
|
||
|
|
||
|
return Boolean(b);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- EqualNode ------------------------------------
|
||
|
|
||
|
void EqualNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
}
|
||
|
|
||
|
bool EqualNode::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.9
|
||
|
Value EqualNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = expr1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v2 = expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
bool result;
|
||
|
if (oper == OpEqEq || oper == OpNotEq) {
|
||
|
// == and !=
|
||
|
bool eq = equal(exec,v1, v2);
|
||
|
result = oper == OpEqEq ? eq : !eq;
|
||
|
} else {
|
||
|
// === and !==
|
||
|
bool eq = strictEqual(exec,v1, v2);
|
||
|
result = oper == OpStrEq ? eq : !eq;
|
||
|
}
|
||
|
return Boolean(result);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- BitOperNode ----------------------------------
|
||
|
|
||
|
void BitOperNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
}
|
||
|
|
||
|
bool BitOperNode::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.10
|
||
|
Value BitOperNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = expr1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v2 = expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
int i1 = v1.toInt32(exec);
|
||
|
int i2 = v2.toInt32(exec);
|
||
|
int result;
|
||
|
if (oper == OpBitAnd)
|
||
|
result = i1 & i2;
|
||
|
else if (oper == OpBitXOr)
|
||
|
result = i1 ^ i2;
|
||
|
else
|
||
|
result = i1 | i2;
|
||
|
|
||
|
return Number(result);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- BinaryLogicalNode ----------------------------
|
||
|
|
||
|
void BinaryLogicalNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
}
|
||
|
|
||
|
bool BinaryLogicalNode::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.11
|
||
|
Value BinaryLogicalNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v1 = expr1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
bool b1 = v1.toBoolean(exec);
|
||
|
if ((!b1 && oper == OpAnd) || (b1 && oper == OpOr))
|
||
|
return v1;
|
||
|
|
||
|
Value v2 = expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return v2;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ConditionalNode ------------------------------
|
||
|
|
||
|
void ConditionalNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
if ( logical )
|
||
|
logical->ref();
|
||
|
}
|
||
|
|
||
|
bool ConditionalNode::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
if ( logical && logical->deref() )
|
||
|
delete logical;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.12
|
||
|
Value ConditionalNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
bool b = logical->toBoolean(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
Value v = b ? expr1->evaluate(exec) : expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- AssignNode -----------------------------------
|
||
|
|
||
|
void AssignNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( left )
|
||
|
left->ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool AssignNode::deref()
|
||
|
{
|
||
|
if ( left && left->deref() )
|
||
|
delete left;
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.13
|
||
|
Value AssignNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Reference l = left->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v;
|
||
|
if (oper == OpEqual) {
|
||
|
v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
} else {
|
||
|
Value v1 = l.getValue(exec);
|
||
|
Value v2 = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
int i1;
|
||
|
int i2;
|
||
|
unsigned int ui;
|
||
|
switch (oper) {
|
||
|
case OpMultEq:
|
||
|
v = mult(exec, v1, v2, '*');
|
||
|
break;
|
||
|
case OpDivEq:
|
||
|
v = mult(exec, v1, v2, '/');
|
||
|
break;
|
||
|
case OpPlusEq:
|
||
|
v = add(exec, v1, v2, '+');
|
||
|
break;
|
||
|
case OpMinusEq:
|
||
|
v = add(exec, v1, v2, '-');
|
||
|
break;
|
||
|
case OpLShift:
|
||
|
i1 = v1.toInt32(exec);
|
||
|
i2 = v2.toInt32(exec);
|
||
|
v = Number(i1 << i2);
|
||
|
break;
|
||
|
case OpRShift:
|
||
|
i1 = v1.toInt32(exec);
|
||
|
i2 = v2.toInt32(exec);
|
||
|
v = Number(i1 >> i2);
|
||
|
break;
|
||
|
case OpURShift:
|
||
|
ui = v1.toUInt32(exec);
|
||
|
i2 = v2.toInt32(exec);
|
||
|
v = Number(ui >> i2);
|
||
|
break;
|
||
|
case OpAndEq:
|
||
|
i1 = v1.toInt32(exec);
|
||
|
i2 = v2.toInt32(exec);
|
||
|
v = Number(i1 & i2);
|
||
|
break;
|
||
|
case OpXOrEq:
|
||
|
i1 = v1.toInt32(exec);
|
||
|
i2 = v2.toInt32(exec);
|
||
|
v = Number(i1 ^ i2);
|
||
|
break;
|
||
|
case OpOrEq:
|
||
|
i1 = v1.toInt32(exec);
|
||
|
i2 = v2.toInt32(exec);
|
||
|
v = Number(i1 | i2);
|
||
|
break;
|
||
|
case OpModEq: {
|
||
|
double d1 = v1.toNumber(exec);
|
||
|
double d2 = v2.toNumber(exec);
|
||
|
v = Number(fmod(d1,d2));
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
v = Undefined();
|
||
|
}
|
||
|
};
|
||
|
l.putValue(exec,v);
|
||
|
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- CommaNode ------------------------------------
|
||
|
|
||
|
void CommaNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
}
|
||
|
|
||
|
bool CommaNode::deref()
|
||
|
{
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 11.14
|
||
|
Value CommaNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
(void) expr1->evaluate(exec); // ignore return value
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
Value v = expr2->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- StatListNode ---------------------------------
|
||
|
|
||
|
StatListNode::StatListNode(StatementNode *s)
|
||
|
: statement(s), list(this)
|
||
|
{
|
||
|
setLoc(s->firstLine(), s->lastLine(), s->code());
|
||
|
}
|
||
|
|
||
|
StatListNode::StatListNode(StatListNode *l, StatementNode *s)
|
||
|
: statement(s), list(l->list)
|
||
|
{
|
||
|
l->list = this;
|
||
|
setLoc(l->firstLine(),s->lastLine(),l->code());
|
||
|
}
|
||
|
|
||
|
void StatListNode::ref()
|
||
|
{
|
||
|
for (StatListNode *n = this; n; n = n->list) {
|
||
|
n->Node::ref();
|
||
|
if (n->statement)
|
||
|
n->statement->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool StatListNode::deref()
|
||
|
{
|
||
|
StatListNode *next;
|
||
|
for (StatListNode *n = this; n; n = next) {
|
||
|
next = n->list;
|
||
|
if (n->statement && n->statement->deref())
|
||
|
delete n->statement;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.1
|
||
|
Completion StatListNode::execute(ExecState *exec)
|
||
|
{
|
||
|
Completion c = statement->execute(exec);
|
||
|
KJS_ABORTPOINT
|
||
|
if (exec->hadException()) {
|
||
|
Value ex = exec->exception();
|
||
|
exec->clearException();
|
||
|
return Completion(Throw, ex);
|
||
|
}
|
||
|
|
||
|
if (c.complType() != Normal)
|
||
|
return c;
|
||
|
|
||
|
Value v = c.value();
|
||
|
|
||
|
for (StatListNode *n = list; n; n = n->list) {
|
||
|
Completion c2 = n->statement->execute(exec);
|
||
|
KJS_ABORTPOINT
|
||
|
if (c2.complType() != Normal)
|
||
|
return c2;
|
||
|
|
||
|
if (exec->hadException()) {
|
||
|
Value ex = exec->exception();
|
||
|
exec->clearException();
|
||
|
return Completion(Throw, ex);
|
||
|
}
|
||
|
|
||
|
if (c2.isValueCompletion())
|
||
|
v = c2.value();
|
||
|
c = c2;
|
||
|
}
|
||
|
|
||
|
return Completion(c.complType(), v, c.target());
|
||
|
}
|
||
|
|
||
|
void StatListNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
for (StatListNode *n = this; n; n = n->list)
|
||
|
n->statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- AssignExprNode -------------------------------
|
||
|
|
||
|
void AssignExprNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool AssignExprNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.2
|
||
|
Value AssignExprNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
return expr->evaluate(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- VarDeclNode ----------------------------------
|
||
|
|
||
|
VarDeclNode::VarDeclNode(const Identifier &id, AssignExprNode *in, Type t)
|
||
|
: varType(t), ident(id), init(in)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void VarDeclNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( init )
|
||
|
init->ref();
|
||
|
}
|
||
|
|
||
|
bool VarDeclNode::deref()
|
||
|
{
|
||
|
if ( init && init->deref() )
|
||
|
delete init;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.2
|
||
|
Value VarDeclNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Object variable = Object::dynamicCast(exec->context().imp()->variableObject());
|
||
|
|
||
|
Value val;
|
||
|
if (init) {
|
||
|
val = init->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
} else {
|
||
|
// ### check attributes? reuse check done in processVarDecls()?
|
||
|
if (variable.imp()->getDirect(ident)) // already declared ?
|
||
|
return Value();
|
||
|
val = Undefined();
|
||
|
}
|
||
|
|
||
|
#ifdef KJS_VERBOSE
|
||
|
printInfo(exec,(UString("new variable ")+ident.ustring()).cstring().c_str(),val);
|
||
|
#endif
|
||
|
// We use Internal to bypass all checks in derived objects, e.g. so that
|
||
|
// "var location" creates a dynamic property instead of activating window.location.
|
||
|
int flags = Internal;
|
||
|
if (exec->context().imp()->codeType() != EvalCode)
|
||
|
flags |= DontDelete;
|
||
|
if (varType == VarDeclNode::Constant)
|
||
|
flags |= ReadOnly;
|
||
|
variable.put(exec, ident, val, flags);
|
||
|
|
||
|
// the spec wants us to return the name of the identifier here
|
||
|
// but we'll save the construction and copying as the return
|
||
|
// value isn't used by the caller
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
void VarDeclNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
Object variable = exec->context().variableObject();
|
||
|
// ### use getDirect()? Check attributes?
|
||
|
// ### avoid duplication with actions performed in evaluate()?
|
||
|
if ( !variable.hasProperty( exec, ident ) ) { // already declared ?
|
||
|
int flags = None;
|
||
|
if (exec->_context->codeType() != EvalCode)
|
||
|
flags |= DontDelete;
|
||
|
if (varType == VarDeclNode::Constant)
|
||
|
flags |= ReadOnly;
|
||
|
// TODO: check for forbidden redeclaration of consts
|
||
|
variable.put(exec, ident, Undefined(), flags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------- VarDeclListNode ------------------------------
|
||
|
|
||
|
void VarDeclListNode::ref()
|
||
|
{
|
||
|
for (VarDeclListNode *n = this; n; n = n->list) {
|
||
|
n->Node::ref();
|
||
|
if (n->var)
|
||
|
n->var->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool VarDeclListNode::deref()
|
||
|
{
|
||
|
VarDeclListNode *next;
|
||
|
for (VarDeclListNode *n = this; n; n = next) {
|
||
|
next = n->list;
|
||
|
if (n->var && n->var->deref())
|
||
|
delete n->var;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
|
||
|
// ECMA 12.2
|
||
|
Value VarDeclListNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
for (const VarDeclListNode *n = this; n; n = n->list) {
|
||
|
(void)n->var->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
}
|
||
|
return Undefined();
|
||
|
}
|
||
|
|
||
|
void VarDeclListNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
for (VarDeclListNode *n = this; n; n = n->list)
|
||
|
n->var->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- VarStatementNode -----------------------------
|
||
|
|
||
|
void VarStatementNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( list )
|
||
|
list->ref();
|
||
|
}
|
||
|
|
||
|
bool VarStatementNode::deref()
|
||
|
{
|
||
|
if ( list && list->deref() )
|
||
|
delete list;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.2
|
||
|
Completion VarStatementNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
(void) list->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
return Completion(Normal);
|
||
|
}
|
||
|
|
||
|
void VarStatementNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
list->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- BlockNode ------------------------------------
|
||
|
|
||
|
BlockNode::BlockNode(SourceElementsNode *s)
|
||
|
{
|
||
|
if (s) {
|
||
|
source = s->elements;
|
||
|
s->elements = 0;
|
||
|
setLoc(s->firstLine(), s->lastLine(), s->code());
|
||
|
} else {
|
||
|
source = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BlockNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( source )
|
||
|
source->ref();
|
||
|
}
|
||
|
|
||
|
bool BlockNode::deref()
|
||
|
{
|
||
|
if ( source && source->deref() )
|
||
|
delete source;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.1
|
||
|
Completion BlockNode::execute(ExecState *exec)
|
||
|
{
|
||
|
if (!source)
|
||
|
return Completion(Normal);
|
||
|
|
||
|
source->processFuncDecl(exec);
|
||
|
|
||
|
return source->execute(exec);
|
||
|
}
|
||
|
|
||
|
void BlockNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
if (source)
|
||
|
source->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- EmptyStatementNode ---------------------------
|
||
|
|
||
|
// ECMA 12.3
|
||
|
Completion EmptyStatementNode::execute(ExecState * /*exec*/)
|
||
|
{
|
||
|
return Completion(Normal);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ExprStatementNode ----------------------------
|
||
|
|
||
|
void ExprStatementNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool ExprStatementNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.4
|
||
|
Completion ExprStatementNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
return Completion(Normal, v);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- IfNode ---------------------------------------
|
||
|
|
||
|
void IfNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement1 )
|
||
|
statement1->ref();
|
||
|
if ( statement2 )
|
||
|
statement2->ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool IfNode::deref()
|
||
|
{
|
||
|
if ( statement1 && statement1->deref() )
|
||
|
delete statement1;
|
||
|
if ( statement2 && statement2->deref() )
|
||
|
delete statement2;
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.5
|
||
|
Completion IfNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
assert(expr);
|
||
|
bool b = expr->toBoolean(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
// if ... then
|
||
|
if (b)
|
||
|
return statement1->execute(exec);
|
||
|
|
||
|
// no else
|
||
|
if (!statement2)
|
||
|
return Completion(Normal);
|
||
|
|
||
|
// else
|
||
|
return statement2->execute(exec);
|
||
|
}
|
||
|
|
||
|
void IfNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
statement1->processVarDecls(exec);
|
||
|
|
||
|
if (statement2)
|
||
|
statement2->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- DoWhileNode ----------------------------------
|
||
|
|
||
|
void DoWhileNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement )
|
||
|
statement->ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool DoWhileNode::deref()
|
||
|
{
|
||
|
if ( statement && statement->deref() )
|
||
|
delete statement;
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.6.1
|
||
|
Completion DoWhileNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Completion c;
|
||
|
Value value;
|
||
|
bool b;
|
||
|
|
||
|
do {
|
||
|
// bail out on error
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
exec->context().imp()->seenLabels()->pushIteration();
|
||
|
c = statement->execute(exec);
|
||
|
exec->context().imp()->seenLabels()->popIteration();
|
||
|
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
|
||
|
if ((c.complType() == Break) && ls.contains(c.target()))
|
||
|
return Completion(Normal, value);
|
||
|
if (c.complType() != Normal)
|
||
|
return c;
|
||
|
}
|
||
|
b = expr->toBoolean(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
} while (b);
|
||
|
|
||
|
return Completion(Normal, value);
|
||
|
}
|
||
|
|
||
|
void DoWhileNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- WhileNode ------------------------------------
|
||
|
|
||
|
void WhileNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement )
|
||
|
statement->ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool WhileNode::deref()
|
||
|
{
|
||
|
if ( statement && statement->deref() )
|
||
|
delete statement;
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.6.2
|
||
|
Completion WhileNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Completion c;
|
||
|
Value value;
|
||
|
|
||
|
while (1) {
|
||
|
bool b = expr->toBoolean(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
// bail out on error
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
if (!b)
|
||
|
return Completion(Normal, value);
|
||
|
|
||
|
exec->context().imp()->seenLabels()->pushIteration();
|
||
|
c = statement->execute(exec);
|
||
|
exec->context().imp()->seenLabels()->popIteration();
|
||
|
if (c.isValueCompletion())
|
||
|
value = c.value();
|
||
|
|
||
|
if ((c.complType() == Continue) && ls.contains(c.target()))
|
||
|
continue;
|
||
|
if ((c.complType() == Break) && ls.contains(c.target()))
|
||
|
return Completion(Normal, value);
|
||
|
if (c.complType() != Normal)
|
||
|
return c;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WhileNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ForNode --------------------------------------
|
||
|
|
||
|
void ForNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement )
|
||
|
statement->ref();
|
||
|
if ( expr1 )
|
||
|
expr1->ref();
|
||
|
if ( expr2 )
|
||
|
expr2->ref();
|
||
|
if ( expr3 )
|
||
|
expr3->ref();
|
||
|
}
|
||
|
|
||
|
bool ForNode::deref()
|
||
|
{
|
||
|
if ( statement && statement->deref() )
|
||
|
delete statement;
|
||
|
if ( expr1 && expr1->deref() )
|
||
|
delete expr1;
|
||
|
if ( expr2 && expr2->deref() )
|
||
|
delete expr2;
|
||
|
if ( expr3 && expr3->deref() )
|
||
|
delete expr3;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.6.3
|
||
|
Completion ForNode::execute(ExecState *exec)
|
||
|
{
|
||
|
Value v, cval;
|
||
|
|
||
|
if (expr1) {
|
||
|
v = expr1->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
}
|
||
|
for (;;) {
|
||
|
if (expr2) {
|
||
|
bool b = expr2->toBoolean(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
if (!b)
|
||
|
return Completion(Normal, cval);
|
||
|
}
|
||
|
// bail out on error
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
exec->context().imp()->seenLabels()->pushIteration();
|
||
|
Completion c = statement->execute(exec);
|
||
|
exec->context().imp()->seenLabels()->popIteration();
|
||
|
if (c.isValueCompletion())
|
||
|
cval = c.value();
|
||
|
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
|
||
|
if ((c.complType() == Break) && ls.contains(c.target()))
|
||
|
return Completion(Normal, cval);
|
||
|
if (c.complType() != Normal)
|
||
|
return c;
|
||
|
}
|
||
|
if (expr3) {
|
||
|
v = expr3->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ForNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
if (expr1)
|
||
|
expr1->processVarDecls(exec);
|
||
|
|
||
|
statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ForInNode ------------------------------------
|
||
|
|
||
|
ForInNode::ForInNode(Node *l, Node *e, StatementNode *s)
|
||
|
: init(0L), lexpr(l), expr(e), varDecl(0L), statement(s)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
ForInNode::ForInNode(const Identifier &i, AssignExprNode *in, Node *e, StatementNode *s)
|
||
|
: ident(i), init(in), expr(e), statement(s)
|
||
|
{
|
||
|
// for( var foo = bar in baz )
|
||
|
varDecl = new VarDeclNode(ident, init, VarDeclNode::Variable);
|
||
|
lexpr = new ResolveNode(ident);
|
||
|
}
|
||
|
|
||
|
void ForInNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement )
|
||
|
statement->ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
if ( lexpr )
|
||
|
lexpr->ref();
|
||
|
if ( init )
|
||
|
init->ref();
|
||
|
if ( varDecl )
|
||
|
varDecl->ref();
|
||
|
}
|
||
|
|
||
|
bool ForInNode::deref()
|
||
|
{
|
||
|
if ( statement && statement->deref() )
|
||
|
delete statement;
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
if ( lexpr && lexpr->deref() )
|
||
|
delete lexpr;
|
||
|
if ( init && init->deref() )
|
||
|
delete init;
|
||
|
if ( varDecl && varDecl->deref() )
|
||
|
delete varDecl;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.6.4
|
||
|
Completion ForInNode::execute(ExecState *exec)
|
||
|
{
|
||
|
Value retval;
|
||
|
Completion c;
|
||
|
|
||
|
if ( varDecl ) {
|
||
|
(void)varDecl->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
}
|
||
|
|
||
|
Value v = expr->evaluate(exec);
|
||
|
// for Null and Undefined, we want to make sure not to go through
|
||
|
// the loop at all, because their object wrappers will have a
|
||
|
// property list but will throw an exception if you attempt to
|
||
|
// access any property.
|
||
|
if (v.isA(NullType) || v.isA(UndefinedType))
|
||
|
return Completion(Normal, retval);
|
||
|
|
||
|
Object o = v.toObject(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
ReferenceList propList = o.propList(exec);
|
||
|
|
||
|
ReferenceListIterator propIt = propList.begin();
|
||
|
|
||
|
while (propIt != propList.end()) {
|
||
|
Identifier name = propIt->getPropertyName(exec);
|
||
|
if (!o.hasProperty(exec,name)) {
|
||
|
propIt++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Reference ref = lexpr->evaluateReference(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
ref.putValue(exec, String(name.ustring()));
|
||
|
|
||
|
exec->context().imp()->seenLabels()->pushIteration();
|
||
|
c = statement->execute(exec);
|
||
|
exec->context().imp()->seenLabels()->popIteration();
|
||
|
if (c.isValueCompletion())
|
||
|
retval = c.value();
|
||
|
|
||
|
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
|
||
|
if ((c.complType() == Break) && ls.contains(c.target()))
|
||
|
break;
|
||
|
if (c.complType() != Normal) {
|
||
|
return c;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
propIt++;
|
||
|
}
|
||
|
|
||
|
// bail out on error
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
return Completion(Normal, retval);
|
||
|
}
|
||
|
|
||
|
void ForInNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ContinueNode ---------------------------------
|
||
|
|
||
|
// ECMA 12.7
|
||
|
Completion ContinueNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Value dummy;
|
||
|
|
||
|
if (ident.isEmpty() && !exec->context().imp()->seenLabels()->inIteration())
|
||
|
return Completion(Throw,
|
||
|
throwError(exec, SyntaxError, "continue used outside of iteration statement"));
|
||
|
else if (!ident.isEmpty() && !exec->context().imp()->seenLabels()->contains(ident))
|
||
|
return Completion(Throw,
|
||
|
throwError(exec, SyntaxError, "Label %s not found in containing block. Can't continue.", ident));
|
||
|
else
|
||
|
return Completion(Continue, dummy, ident);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- BreakNode ------------------------------------
|
||
|
|
||
|
// ECMA 12.8
|
||
|
Completion BreakNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Value dummy;
|
||
|
|
||
|
if (ident.isEmpty() && !exec->context().imp()->seenLabels()->inIteration() &&
|
||
|
!exec->context().imp()->seenLabels()->inSwitch())
|
||
|
return Completion(Throw,
|
||
|
throwError(exec, SyntaxError, "break used outside of iteration or switch statement"));
|
||
|
else if (!ident.isEmpty() && !exec->context().imp()->seenLabels()->contains(ident))
|
||
|
return Completion(Throw,
|
||
|
throwError(exec, SyntaxError, "Label %s not found in containing block. Can't break.", ident));
|
||
|
else
|
||
|
return Completion(Break, dummy, ident);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ReturnNode -----------------------------------
|
||
|
|
||
|
void ReturnNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( value )
|
||
|
value->ref();
|
||
|
}
|
||
|
|
||
|
bool ReturnNode::deref()
|
||
|
{
|
||
|
if ( value && value->deref() )
|
||
|
delete value;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.9
|
||
|
Completion ReturnNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
CodeType codeType = exec->context().imp()->codeType();
|
||
|
if (codeType != FunctionCode) {
|
||
|
return Completion(Throw, throwError(exec, SyntaxError, "Invalid return statement."));
|
||
|
}
|
||
|
|
||
|
if (!value)
|
||
|
return Completion(ReturnValue, Undefined());
|
||
|
|
||
|
Value v = value->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
return Completion(ReturnValue, v);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- WithNode -------------------------------------
|
||
|
|
||
|
void WithNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement )
|
||
|
statement->ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool WithNode::deref()
|
||
|
{
|
||
|
if ( statement && statement->deref() )
|
||
|
delete statement;
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.10
|
||
|
Completion WithNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
Object o = v.toObject(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
exec->context().imp()->pushScope(o);
|
||
|
Completion res = statement->execute(exec);
|
||
|
exec->context().imp()->popScope();
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void WithNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- CaseClauseNode -------------------------------
|
||
|
|
||
|
void CaseClauseNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
if ( list )
|
||
|
list->ref();
|
||
|
}
|
||
|
|
||
|
bool CaseClauseNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
if ( list && list->deref() )
|
||
|
delete list;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.11
|
||
|
Value CaseClauseNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTIONVALUE
|
||
|
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
// ECMA 12.11
|
||
|
Completion CaseClauseNode::evalStatements(ExecState *exec) const
|
||
|
{
|
||
|
if (list)
|
||
|
return list->execute(exec);
|
||
|
else
|
||
|
return Completion(Normal, Undefined());
|
||
|
}
|
||
|
|
||
|
void CaseClauseNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
if (list)
|
||
|
list->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ClauseListNode -------------------------------
|
||
|
|
||
|
void ClauseListNode::ref()
|
||
|
{
|
||
|
for (ClauseListNode *n = this; n; n = n->nx) {
|
||
|
n->Node::ref();
|
||
|
if (n->cl)
|
||
|
n->cl->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ClauseListNode::deref()
|
||
|
{
|
||
|
ClauseListNode *next;
|
||
|
for (ClauseListNode *n = this; n; n = next) {
|
||
|
next = n->nx;
|
||
|
if (n->cl && n->cl->deref())
|
||
|
delete n->cl;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
Value ClauseListNode::evaluate(ExecState * /*exec*/) const
|
||
|
{
|
||
|
/* should never be called */
|
||
|
assert(false);
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.11
|
||
|
void ClauseListNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
for (ClauseListNode *n = this; n; n = n->nx)
|
||
|
if (n->cl)
|
||
|
n->cl->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- CaseBlockNode --------------------------------
|
||
|
|
||
|
CaseBlockNode::CaseBlockNode(ClauseListNode *l1, CaseClauseNode *d,
|
||
|
ClauseListNode *l2)
|
||
|
{
|
||
|
def = d;
|
||
|
if (l1) {
|
||
|
list1 = l1->nx;
|
||
|
l1->nx = 0;
|
||
|
} else {
|
||
|
list1 = 0;
|
||
|
}
|
||
|
if (l2) {
|
||
|
list2 = l2->nx;
|
||
|
l2->nx = 0;
|
||
|
} else {
|
||
|
list2 = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CaseBlockNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( def )
|
||
|
def->ref();
|
||
|
if ( list1 )
|
||
|
list1->ref();
|
||
|
if ( list2 )
|
||
|
list2->ref();
|
||
|
}
|
||
|
|
||
|
bool CaseBlockNode::deref()
|
||
|
{
|
||
|
if ( def && def->deref() )
|
||
|
delete def;
|
||
|
if ( list1 && list1->deref() )
|
||
|
delete list1;
|
||
|
if ( list2 && list2->deref() )
|
||
|
delete list2;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
Value CaseBlockNode::evaluate(ExecState * /*exec*/) const
|
||
|
{
|
||
|
/* should never be called */
|
||
|
assert(false);
|
||
|
return Value();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.11
|
||
|
Completion CaseBlockNode::evalBlock(ExecState *exec, const Value& input) const
|
||
|
{
|
||
|
Value v;
|
||
|
Completion res;
|
||
|
ClauseListNode *a = list1, *b = list2;
|
||
|
CaseClauseNode *clause;
|
||
|
|
||
|
while (a) {
|
||
|
clause = a->clause();
|
||
|
a = a->next();
|
||
|
v = clause->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
if (strictEqual(exec, input, v)) {
|
||
|
res = clause->evalStatements(exec);
|
||
|
if (res.complType() != Normal)
|
||
|
return res;
|
||
|
while (a) {
|
||
|
res = a->clause()->evalStatements(exec);
|
||
|
if (res.complType() != Normal)
|
||
|
return res;
|
||
|
a = a->next();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
while (b) {
|
||
|
clause = b->clause();
|
||
|
b = b->next();
|
||
|
v = clause->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
if (strictEqual(exec, input, v)) {
|
||
|
res = clause->evalStatements(exec);
|
||
|
if (res.complType() != Normal)
|
||
|
return res;
|
||
|
goto step18;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// default clause
|
||
|
if (def) {
|
||
|
res = def->evalStatements(exec);
|
||
|
if (res.complType() != Normal)
|
||
|
return res;
|
||
|
}
|
||
|
b = list2;
|
||
|
step18:
|
||
|
while (b) {
|
||
|
clause = b->clause();
|
||
|
res = clause->evalStatements(exec);
|
||
|
if (res.complType() != Normal)
|
||
|
return res;
|
||
|
b = b->next();
|
||
|
}
|
||
|
|
||
|
// bail out on error
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
return Completion(Normal);
|
||
|
}
|
||
|
|
||
|
void CaseBlockNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
if (list1)
|
||
|
list1->processVarDecls(exec);
|
||
|
if (def)
|
||
|
def->processVarDecls(exec);
|
||
|
if (list2)
|
||
|
list2->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- SwitchNode -----------------------------------
|
||
|
|
||
|
void SwitchNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
if ( block )
|
||
|
block->ref();
|
||
|
}
|
||
|
|
||
|
bool SwitchNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
if ( block && block->deref() )
|
||
|
delete block;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.11
|
||
|
Completion SwitchNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
exec->context().imp()->seenLabels()->pushSwitch();
|
||
|
Completion res = block->evalBlock(exec,v);
|
||
|
exec->context().imp()->seenLabels()->popSwitch();
|
||
|
|
||
|
if ((res.complType() == Break) && ls.contains(res.target()))
|
||
|
return Completion(Normal, res.value());
|
||
|
else
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void SwitchNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
block->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- LabelNode ------------------------------------
|
||
|
|
||
|
void LabelNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( statement )
|
||
|
statement->ref();
|
||
|
}
|
||
|
|
||
|
bool LabelNode::deref()
|
||
|
{
|
||
|
if ( statement && statement->deref() )
|
||
|
delete statement;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.12
|
||
|
Completion LabelNode::execute(ExecState *exec)
|
||
|
{
|
||
|
Completion e;
|
||
|
|
||
|
if (!exec->context().imp()->seenLabels()->push(label)) {
|
||
|
return Completion( Throw,
|
||
|
throwError(exec, SyntaxError, "Duplicated label %s found.", label));
|
||
|
};
|
||
|
e = statement->execute(exec);
|
||
|
exec->context().imp()->seenLabels()->pop();
|
||
|
|
||
|
if ((e.complType() == Break) && (e.target() == label))
|
||
|
return Completion(Normal, e.value());
|
||
|
else
|
||
|
return e;
|
||
|
}
|
||
|
|
||
|
void LabelNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
statement->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ThrowNode ------------------------------------
|
||
|
|
||
|
void ThrowNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( expr )
|
||
|
expr->ref();
|
||
|
}
|
||
|
|
||
|
bool ThrowNode::deref()
|
||
|
{
|
||
|
if ( expr && expr->deref() )
|
||
|
delete expr;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.13
|
||
|
Completion ThrowNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Value v = expr->evaluate(exec);
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
// bail out on error
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
Debugger *dbg = exec->interpreter()->imp()->debugger();
|
||
|
if (dbg)
|
||
|
dbg->exception(exec,v,exec->context().imp()->inTryCatch());
|
||
|
|
||
|
return Completion(Throw, v);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- CatchNode ------------------------------------
|
||
|
|
||
|
void CatchNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( block )
|
||
|
block->ref();
|
||
|
}
|
||
|
|
||
|
bool CatchNode::deref()
|
||
|
{
|
||
|
if ( block && block->deref() )
|
||
|
delete block;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
Completion CatchNode::execute(ExecState * /*exec*/)
|
||
|
{
|
||
|
// should never be reached. execute(exec, arg) is used instead
|
||
|
assert(0L);
|
||
|
return Completion();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.14
|
||
|
Completion CatchNode::execute(ExecState *exec, const Value &arg)
|
||
|
{
|
||
|
/* TODO: correct ? Not part of the spec */
|
||
|
|
||
|
exec->clearException();
|
||
|
|
||
|
Object obj(new ObjectImp());
|
||
|
obj.put(exec, ident, arg, DontDelete);
|
||
|
exec->context().imp()->pushScope(obj);
|
||
|
Completion c = block->execute(exec);
|
||
|
exec->context().imp()->popScope();
|
||
|
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
void CatchNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
block->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- FinallyNode ----------------------------------
|
||
|
|
||
|
void FinallyNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( block )
|
||
|
block->ref();
|
||
|
}
|
||
|
|
||
|
bool FinallyNode::deref()
|
||
|
{
|
||
|
if ( block && block->deref() )
|
||
|
delete block;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.14
|
||
|
Completion FinallyNode::execute(ExecState *exec)
|
||
|
{
|
||
|
return block->execute(exec);
|
||
|
}
|
||
|
|
||
|
void FinallyNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
block->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- TryNode --------------------------------------
|
||
|
|
||
|
void TryNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( block )
|
||
|
block->ref();
|
||
|
if ( _final )
|
||
|
_final->ref();
|
||
|
if ( _catch )
|
||
|
_catch->ref();
|
||
|
}
|
||
|
|
||
|
bool TryNode::deref()
|
||
|
{
|
||
|
if ( block && block->deref() )
|
||
|
delete block;
|
||
|
if ( _final && _final->deref() )
|
||
|
delete _final;
|
||
|
if ( _catch && _catch->deref() )
|
||
|
delete _catch;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 12.14
|
||
|
Completion TryNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_BREAKPOINT;
|
||
|
|
||
|
Completion c, c2;
|
||
|
|
||
|
if (_catch)
|
||
|
exec->context().imp()->pushTryCatch();
|
||
|
c = block->execute(exec);
|
||
|
if (_catch)
|
||
|
exec->context().imp()->popTryCatch();
|
||
|
|
||
|
if (!_final) {
|
||
|
if (c.complType() != Throw)
|
||
|
return c;
|
||
|
return _catch->execute(exec,c.value());
|
||
|
}
|
||
|
|
||
|
if (!_catch) {
|
||
|
Value exception = exec->_exception;
|
||
|
exec->_exception = Value();
|
||
|
|
||
|
c2 = _final->execute(exec);
|
||
|
|
||
|
if (!exec->hadException() && c2.complType() != Throw)
|
||
|
exec->_exception = exception;
|
||
|
|
||
|
return (c2.complType() == Normal) ? c : c2;
|
||
|
}
|
||
|
|
||
|
if (c.complType() == Throw)
|
||
|
c = _catch->execute(exec,c.value());
|
||
|
|
||
|
c2 = _final->execute(exec);
|
||
|
return (c2.complType() == Normal) ? c : c2;
|
||
|
}
|
||
|
|
||
|
void TryNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
block->processVarDecls(exec);
|
||
|
if (_final)
|
||
|
_final->processVarDecls(exec);
|
||
|
if (_catch)
|
||
|
_catch->processVarDecls(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- ParameterNode --------------------------------
|
||
|
|
||
|
void ParameterNode::ref()
|
||
|
{
|
||
|
for (ParameterNode *n = this; n; n = n->next)
|
||
|
n->Node::ref();
|
||
|
}
|
||
|
|
||
|
bool ParameterNode::deref()
|
||
|
{
|
||
|
ParameterNode *next;
|
||
|
for (ParameterNode *n = this; n; n = next) {
|
||
|
next = n->next;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 13
|
||
|
Value ParameterNode::evaluate(ExecState * /*exec*/) const
|
||
|
{
|
||
|
return Undefined();
|
||
|
}
|
||
|
|
||
|
// ----------------------------- FunctionBodyNode -----------------------------
|
||
|
|
||
|
|
||
|
FunctionBodyNode::FunctionBodyNode(SourceElementsNode *s)
|
||
|
: BlockNode(s)
|
||
|
{
|
||
|
//fprintf(stderr,"FunctionBodyNode::FunctionBodyNode %p\n",this);
|
||
|
}
|
||
|
|
||
|
void FunctionBodyNode::processFuncDecl(ExecState *exec)
|
||
|
{
|
||
|
if (source)
|
||
|
source->processFuncDecl(exec);
|
||
|
}
|
||
|
|
||
|
// ----------------------------- FuncDeclNode ---------------------------------
|
||
|
|
||
|
void FuncDeclNode::ref()
|
||
|
{
|
||
|
StatementNode::ref();
|
||
|
if ( param )
|
||
|
param->ref();
|
||
|
if ( body )
|
||
|
body->ref();
|
||
|
}
|
||
|
|
||
|
bool FuncDeclNode::deref()
|
||
|
{
|
||
|
if ( param && param->deref() )
|
||
|
delete param;
|
||
|
if ( body && body->deref() )
|
||
|
delete body;
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 13
|
||
|
void FuncDeclNode::processFuncDecl(ExecState *exec)
|
||
|
{
|
||
|
ContextImp *ctx = exec->context().imp();
|
||
|
// TODO: let this be an object with [[Class]] property "Function"
|
||
|
FunctionImp *fimp = new DeclaredFunctionImp(exec, ident, body, exec->context().imp()->scopeChain());
|
||
|
Object func(fimp); // protect from GC
|
||
|
|
||
|
// Value proto = exec->lexicalInterpreter()->builtinObject().construct(exec,List::empty());
|
||
|
List empty;
|
||
|
Object proto = exec->lexicalInterpreter()->builtinObject().construct(exec,empty);
|
||
|
proto.put(exec, constructorPropertyName, func, ReadOnly|DontDelete|DontEnum);
|
||
|
func.put(exec, prototypePropertyName, proto, Internal|DontDelete);
|
||
|
|
||
|
int plen = 0;
|
||
|
for(const ParameterNode *p = param; p != 0L; p = p->nextParam(), plen++)
|
||
|
fimp->addParameter(p->ident());
|
||
|
|
||
|
func.put(exec, lengthPropertyName, Number(plen), ReadOnly|DontDelete|DontEnum);
|
||
|
|
||
|
#ifdef KJS_VERBOSE
|
||
|
fprintf(stderr,"KJS: new function %s in %p\n", ident.ustring().cstring().c_str(), ctx->variableObject().imp());
|
||
|
#endif
|
||
|
if (exec->_context->codeType() == EvalCode) {
|
||
|
// ECMA 10.2.2
|
||
|
ctx->variableObject().put(exec, ident, func, Internal);
|
||
|
} else {
|
||
|
ctx->variableObject().put(exec, ident, func, DontDelete | Internal);
|
||
|
}
|
||
|
|
||
|
if (body) {
|
||
|
// hack the scope so that the function gets put as a property of func, and it's scope
|
||
|
// contains the func as well as our current scope
|
||
|
Object oldVar = ctx->variableObject();
|
||
|
ctx->setVariableObject(func);
|
||
|
ctx->pushScope(func);
|
||
|
body->processFuncDecl(exec);
|
||
|
ctx->popScope();
|
||
|
ctx->setVariableObject(oldVar);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ----------------------------- FuncExprNode ---------------------------------
|
||
|
|
||
|
void FuncExprNode::ref()
|
||
|
{
|
||
|
Node::ref();
|
||
|
if ( param )
|
||
|
param->ref();
|
||
|
if ( body )
|
||
|
body->ref();
|
||
|
}
|
||
|
|
||
|
bool FuncExprNode::deref()
|
||
|
{
|
||
|
if ( param && param->deref() )
|
||
|
delete param;
|
||
|
if ( body && body->deref() )
|
||
|
delete body;
|
||
|
return Node::deref();
|
||
|
}
|
||
|
|
||
|
|
||
|
// ECMA 13
|
||
|
Value FuncExprNode::evaluate(ExecState *exec) const
|
||
|
{
|
||
|
ContextImp *context = exec->context().imp();
|
||
|
bool named = !ident.isNull();
|
||
|
Object functionScopeObject;
|
||
|
|
||
|
if (named) {
|
||
|
// named FunctionExpressions can recursively call themselves,
|
||
|
// but they won't register with the current scope chain and should
|
||
|
// be contained as single property in an anonymous object.
|
||
|
functionScopeObject = Object(new ObjectImp());
|
||
|
context->pushScope(functionScopeObject);
|
||
|
}
|
||
|
|
||
|
FunctionImp *fimp = new DeclaredFunctionImp(exec, Identifier::null(), body, exec->context().imp()->scopeChain());
|
||
|
Value ret(fimp);
|
||
|
List empty;
|
||
|
Value proto = exec->lexicalInterpreter()->builtinObject().construct(exec,empty);
|
||
|
fimp->put(exec, prototypePropertyName, proto, Internal|DontDelete);
|
||
|
|
||
|
for(const ParameterNode *p = param; p != 0L; p = p->nextParam())
|
||
|
fimp->addParameter(p->ident());
|
||
|
|
||
|
if (named) {
|
||
|
functionScopeObject.put(exec, ident, Value(fimp), ReadOnly|DontDelete);
|
||
|
context->popScope();
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// ----------------------------- SourceElementsNode ---------------------------
|
||
|
|
||
|
SourceElementsNode::SourceElementsNode(StatementNode *s1)
|
||
|
{
|
||
|
element = s1;
|
||
|
elements = this;
|
||
|
setLoc(s1->firstLine(), s1->lastLine(), s1->code());
|
||
|
}
|
||
|
|
||
|
SourceElementsNode::SourceElementsNode(SourceElementsNode *s1, StatementNode *s2)
|
||
|
{
|
||
|
elements = s1->elements;
|
||
|
s1->elements = this;
|
||
|
element = s2;
|
||
|
setLoc(s1->firstLine(), s2->lastLine(), s1->code());
|
||
|
}
|
||
|
|
||
|
void SourceElementsNode::ref()
|
||
|
{
|
||
|
for (SourceElementsNode *n = this; n; n = n->elements) {
|
||
|
n->Node::ref();
|
||
|
if (n->element)
|
||
|
n->element->ref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool SourceElementsNode::deref()
|
||
|
{
|
||
|
SourceElementsNode *next;
|
||
|
for (SourceElementsNode *n = this; n; n = next) {
|
||
|
next = n->elements;
|
||
|
if (n->element && n->element->deref())
|
||
|
delete n->element;
|
||
|
if (n != this && n->Node::deref())
|
||
|
delete n;
|
||
|
}
|
||
|
return StatementNode::deref();
|
||
|
}
|
||
|
|
||
|
// ECMA 14
|
||
|
Completion SourceElementsNode::execute(ExecState *exec)
|
||
|
{
|
||
|
KJS_CHECKEXCEPTION
|
||
|
|
||
|
Completion c1 = element->execute(exec);
|
||
|
KJS_CHECKEXCEPTION;
|
||
|
if (c1.complType() != Normal)
|
||
|
return c1;
|
||
|
|
||
|
for (SourceElementsNode *n = elements; n; n = n->elements) {
|
||
|
Completion c2 = n->element->execute(exec);
|
||
|
if (c2.complType() != Normal)
|
||
|
return c2;
|
||
|
// The spec says to return c2 here, but it seems that mozilla returns c1 if
|
||
|
// c2 doesn't have a value
|
||
|
if (c2.value().isValid())
|
||
|
c1 = c2;
|
||
|
}
|
||
|
|
||
|
return c1;
|
||
|
}
|
||
|
|
||
|
// ECMA 14
|
||
|
void SourceElementsNode::processFuncDecl(ExecState *exec)
|
||
|
{
|
||
|
for (SourceElementsNode *n = this; n; n = n->elements)
|
||
|
n->element->processFuncDecl(exec);
|
||
|
}
|
||
|
|
||
|
void SourceElementsNode::processVarDecls(ExecState *exec)
|
||
|
{
|
||
|
for (SourceElementsNode *n = this; n; n = n->elements)
|
||
|
n->element->processVarDecls(exec);
|
||
|
}
|