/* * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * Copyright (C) 2007 Eric Seidel * * 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 "ExecState.h" #include "JSGlobalObject.h" #include "Parser.h" #include "PropertyNameArray.h" #include "array_object.h" #include "debugger.h" #include "function_object.h" #include "lexer.h" #include "operations.h" #include "regexp_object.h" #include #include #include #include #include namespace KJS { class FunctionBodyNodeWithDebuggerHooks : public FunctionBodyNode { public: FunctionBodyNodeWithDebuggerHooks(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL; virtual JSValue* execute(ExecState*) KJS_FAST_CALL; }; #define KJS_CHECKEXCEPTION \ if (exec->hadException()) \ return rethrowException(exec); #define KJS_CHECKEXCEPTIONVALUE \ if (exec->hadException()) { \ handleException(exec); \ return jsUndefined(); \ } #define KJS_CHECKEXCEPTIONNUMBER \ if (exec->hadException()) { \ handleException(exec); \ return 0; \ } #define KJS_CHECKEXCEPTIONBOOLEAN \ if (exec->hadException()) { \ handleException(exec); \ return false; \ } #define KJS_CHECKEXCEPTIONVOID \ if (exec->hadException()) { \ handleException(exec); \ return; \ } #if !ASSERT_DISABLED static inline bool canSkipLookup(ExecState* exec, const Identifier& ident) { // Static lookup in EvalCode is impossible because variables aren't DontDelete. // Static lookup in GlobalCode may be possible, but we haven't implemented support for it yet. if (exec->codeType() != FunctionCode) return false; // Static lookup is impossible when something dynamic has been added to the front of the scope chain. if (exec->variableObject() != exec->scopeChain().top()) return false; // Static lookup is impossible if the symbol isn't statically declared. if (!exec->variableObject()->symbolTable().contains(ident.ustring().rep())) return false; return true; } #endif static inline bool isConstant(const LocalStorage& localStorage, size_t index) { ASSERT(index < localStorage.size()); return localStorage[index].attributes & ReadOnly; } // ------------------------------ Node ----------------------------------------- #ifndef NDEBUG #ifndef LOG_CHANNEL_PREFIX #define LOG_CHANNEL_PREFIX Log #endif static WTFLogChannel LogKJSNodeLeaks = { 0x00000000, "", WTFLogChannelOn }; struct ParserRefCountedCounter { static unsigned count; ParserRefCountedCounter() { if (count) #ifdef ANDROID // FIXME HACK : LOG not defined appropriately here ASSERT("LEAK: KJS::Node"); #else LOG(KJSNodeLeaks, "LEAK: %u KJS::Node\n", count); #endif } }; unsigned ParserRefCountedCounter::count = 0; static ParserRefCountedCounter parserRefCountedCounter; #endif static HashSet* newTrackedObjects; static HashCountedSet* trackedObjectExtraRefCounts; ParserRefCounted::ParserRefCounted() { #ifndef NDEBUG ++ParserRefCountedCounter::count; #endif if (!newTrackedObjects) newTrackedObjects = new HashSet; newTrackedObjects->add(this); ASSERT(newTrackedObjects->contains(this)); } ParserRefCounted::~ParserRefCounted() { #ifndef NDEBUG --ParserRefCountedCounter::count; #endif } void ParserRefCounted::ref() { // bumping from 0 to 1 is just removing from the new nodes set if (newTrackedObjects) { HashSet::iterator it = newTrackedObjects->find(this); if (it != newTrackedObjects->end()) { newTrackedObjects->remove(it); ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this)); return; } } ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); if (!trackedObjectExtraRefCounts) trackedObjectExtraRefCounts = new HashCountedSet; trackedObjectExtraRefCounts->add(this); } void ParserRefCounted::deref() { ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); if (!trackedObjectExtraRefCounts) { delete this; return; } HashCountedSet::iterator it = trackedObjectExtraRefCounts->find(this); if (it == trackedObjectExtraRefCounts->end()) delete this; else trackedObjectExtraRefCounts->remove(it); } unsigned ParserRefCounted::refcount() { if (newTrackedObjects && newTrackedObjects->contains(this)) { ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this)); return 0; } ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); if (!trackedObjectExtraRefCounts) return 1; return 1 + trackedObjectExtraRefCounts->count(this); } void ParserRefCounted::deleteNewObjects() { if (!newTrackedObjects) return; #ifndef NDEBUG HashSet::iterator end = newTrackedObjects->end(); for (HashSet::iterator it = newTrackedObjects->begin(); it != end; ++it) ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(*it)); #endif deleteAllValues(*newTrackedObjects); delete newTrackedObjects; newTrackedObjects = 0; } Node::Node() : m_expectedReturnType(ObjectType) { m_line = lexer().lineNo(); } Node::Node(JSType expectedReturn) : m_expectedReturnType(expectedReturn) { m_line = lexer().lineNo(); } double ExpressionNode::evaluateToNumber(ExecState* exec) { JSValue* value = evaluate(exec); KJS_CHECKEXCEPTIONNUMBER return value->toNumber(exec); } bool ExpressionNode::evaluateToBoolean(ExecState* exec) { JSValue* value = evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return value->toBoolean(exec); } int32_t ExpressionNode::evaluateToInt32(ExecState* exec) { JSValue* value = evaluate(exec); KJS_CHECKEXCEPTIONNUMBER return value->toInt32(exec); } uint32_t ExpressionNode::evaluateToUInt32(ExecState* exec) { JSValue* value = evaluate(exec); KJS_CHECKEXCEPTIONNUMBER return value->toUInt32(exec); } 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); UString newString = string.substr(0, position); newString.append(substring); newString.append(string.substr(position + 2)); string = newString; } static inline int currentSourceId(ExecState* exec) KJS_FAST_CALL; static inline int currentSourceId(ExecState* exec) { return exec->scopeNode()->sourceId(); } static inline const UString& currentSourceURL(ExecState* exec) KJS_FAST_CALL; static inline const UString& currentSourceURL(ExecState* exec) { return exec->scopeNode()->sourceURL(); } JSValue* Node::setErrorCompletion(ExecState* exec, ErrorType e, const char* msg) { return exec->setThrowCompletion(Error::create(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec))); } JSValue* Node::setErrorCompletion(ExecState* exec, ErrorType e, const char* msg, const Identifier& ident) { UString message = msg; substitute(message, ident.ustring()); return exec->setThrowCompletion(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(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->dynamicGlobalObject()->debugger(); if (dbg && !dbg->hasHandledException(exec, exceptionValue)) { bool cont = dbg->exception(exec, currentSourceId(exec), m_line, exceptionValue); if (!cont) dbg->imp()->abort(); } } NEVER_INLINE JSValue* Node::rethrowException(ExecState* exec) { JSValue* exception = exec->exception(); exec->clearException(); handleException(exec, exception); return exec->setThrowCompletion(exception); } // ------------------------------ StatementNode -------------------------------- StatementNode::StatementNode() : m_lastLine(-1) { m_line = -1; } void StatementNode::setLoc(int firstLine, int lastLine) { m_line = firstLine; m_lastLine = lastLine; } // ------------------------------ SourceElements -------------------------------- void SourceElements::append(PassRefPtr statement) { if (statement->isEmptyStatement()) return; if (Debugger::debuggersPresent) m_statements.append(new BreakpointCheckStatement(statement)); else m_statements.append(statement); } // ------------------------------ BreakpointCheckStatement -------------------------------- BreakpointCheckStatement::BreakpointCheckStatement(PassRefPtr statement) : m_statement(statement) { ASSERT(m_statement); } JSValue* BreakpointCheckStatement::execute(ExecState* exec) { if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) if (!debugger->atStatement(exec, currentSourceId(exec), m_statement->firstLine(), m_statement->lastLine())) return exec->setNormalCompletion(); return m_statement->execute(exec); } void BreakpointCheckStatement::streamTo(SourceStream& stream) const { m_statement->streamTo(stream); } void BreakpointCheckStatement::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_statement.get()); } // ------------------------------ NullNode ------------------------------------- JSValue* NullNode::evaluate(ExecState* ) { return jsNull(); } // ------------------------------ FalseNode ---------------------------------- JSValue* FalseNode::evaluate(ExecState*) { return jsBoolean(false); } // ------------------------------ TrueNode ---------------------------------- JSValue* TrueNode::evaluate(ExecState*) { return jsBoolean(true); } // ------------------------------ NumberNode ----------------------------------- JSValue* NumberNode::evaluate(ExecState*) { // Number nodes are only created when the number can't fit in a JSImmediate, so no need to check again. return jsNumberCell(m_double); } double NumberNode::evaluateToNumber(ExecState*) { return m_double; } bool NumberNode::evaluateToBoolean(ExecState*) { return m_double < 0.0 || m_double > 0.0; // false for NaN as well as 0 } int32_t NumberNode::evaluateToInt32(ExecState*) { return JSValue::toInt32(m_double); } uint32_t NumberNode::evaluateToUInt32(ExecState*) { return JSValue::toUInt32(m_double); } // ------------------------------ ImmediateNumberNode ----------------------------------- JSValue* ImmediateNumberNode::evaluate(ExecState*) { return m_value; } int32_t ImmediateNumberNode::evaluateToInt32(ExecState*) { return JSImmediate::getTruncatedInt32(m_value); } uint32_t ImmediateNumberNode::evaluateToUInt32(ExecState*) { uint32_t i; if (JSImmediate::getTruncatedUInt32(m_value, i)) return i; bool ok; return JSValue::toUInt32SlowCase(m_double, ok); } // ------------------------------ StringNode ----------------------------------- JSValue* StringNode::evaluate(ExecState*) { return jsOwnedString(m_value); } double StringNode::evaluateToNumber(ExecState*) { return m_value.toDouble(); } bool StringNode::evaluateToBoolean(ExecState*) { return !m_value.isEmpty(); } // ------------------------------ RegExpNode ----------------------------------- JSValue* RegExpNode::evaluate(ExecState* exec) { return exec->lexicalGlobalObject()->regExpConstructor()->createRegExpImp(exec, m_regExp); } // ------------------------------ ThisNode ------------------------------------- // ECMA 11.1.1 JSValue* ThisNode::evaluate(ExecState* exec) { return exec->thisValue(); } // ------------------------------ ResolveNode ---------------------------------- // ECMA 11.1.2 & 10.1.4 JSValue* ResolveNode::inlineEvaluate(ExecState* exec) { // Check for missed optimization opportunity. ASSERT(!canSkipLookup(exec, m_ident)); const ScopeChain& chain = exec->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, m_ident, slot)) return slot.getValue(exec, o, m_ident); ++iter; } while (iter != end); return throwUndefinedVariableError(exec, m_ident); } JSValue* ResolveNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double ResolveNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool ResolveNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t ResolveNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t ResolveNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } void ResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) new (this) LocalVarAccessNode(index); } JSValue* LocalVarAccessNode::inlineEvaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); return exec->localStorage()[m_index].value; } JSValue* LocalVarAccessNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double LocalVarAccessNode::evaluateToNumber(ExecState* exec) { return inlineEvaluate(exec)->toNumber(exec); } bool LocalVarAccessNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluate(exec)->toBoolean(exec); } int32_t LocalVarAccessNode::evaluateToInt32(ExecState* exec) { return inlineEvaluate(exec)->toInt32(exec); } uint32_t LocalVarAccessNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluate(exec)->toUInt32(exec); } // ------------------------------ ElementNode ---------------------------------- void ElementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_next) nodeStack.append(m_next.get()); ASSERT(m_node); nodeStack.append(m_node.get()); } // ECMA 11.1.4 JSValue* ElementNode::evaluate(ExecState* exec) { JSObject* array = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList()); int length = 0; for (ElementNode* n = this; n; n = n->m_next.get()) { JSValue* val = n->m_node->evaluate(exec); KJS_CHECKEXCEPTIONVALUE length += n->m_elision; array->put(exec, length++, val); } return array; } // ------------------------------ ArrayNode ------------------------------------ void ArrayNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_element) nodeStack.append(m_element.get()); } // ECMA 11.1.4 JSValue* ArrayNode::evaluate(ExecState* exec) { JSObject* array; int length; if (m_element) { array = static_cast(m_element->evaluate(exec)); KJS_CHECKEXCEPTIONVALUE length = m_optional ? array->get(exec, exec->propertyNames().length)->toInt32(exec) : 0; } else { JSValue* newArr = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList()); array = static_cast(newArr); length = 0; } if (m_optional) array->put(exec, exec->propertyNames().length, jsNumber(m_elision + length)); return array; } // ------------------------------ ObjectLiteralNode ---------------------------- void ObjectLiteralNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_list) nodeStack.append(m_list.get()); } // ECMA 11.1.5 JSValue* ObjectLiteralNode::evaluate(ExecState* exec) { if (m_list) return m_list->evaluate(exec); return exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); } // ------------------------------ PropertyListNode ----------------------------- void PropertyListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_next) nodeStack.append(m_next.get()); nodeStack.append(m_node.get()); } // ECMA 11.1.5 JSValue* PropertyListNode::evaluate(ExecState* exec) { JSObject* obj = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); for (PropertyListNode* p = this; p; p = p->m_next.get()) { JSValue* v = p->m_node->m_assign->evaluate(exec); KJS_CHECKEXCEPTIONVALUE switch (p->m_node->m_type) { case PropertyNode::Getter: ASSERT(v->isObject()); obj->defineGetter(exec, p->m_node->name(), static_cast(v)); break; case PropertyNode::Setter: ASSERT(v->isObject()); obj->defineSetter(exec, p->m_node->name(), static_cast(v)); break; case PropertyNode::Constant: obj->put(exec, p->m_node->name(), v); break; } } return obj; } // ------------------------------ PropertyNode ----------------------------- void PropertyNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_assign.get()); } // ECMA 11.1.5 JSValue* PropertyNode::evaluate(ExecState*) { ASSERT(false); return jsNull(); } // ------------------------------ BracketAccessorNode -------------------------------- void BracketAccessorNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } // ECMA 11.2.1a JSValue* BracketAccessorNode::inlineEvaluate(ExecState* exec) { JSValue* v1 = m_base->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_subscript->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))); } JSValue* BracketAccessorNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double BracketAccessorNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool BracketAccessorNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t BracketAccessorNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t BracketAccessorNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } // ------------------------------ DotAccessorNode -------------------------------- void DotAccessorNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_base.get()); } // ECMA 11.2.1b JSValue* DotAccessorNode::inlineEvaluate(ExecState* exec) { JSValue* v = m_base->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return v->toObject(exec)->get(exec, m_ident); } JSValue* DotAccessorNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double DotAccessorNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool DotAccessorNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t DotAccessorNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t DotAccessorNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } // ------------------------------ ArgumentListNode ----------------------------- void ArgumentListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_next) nodeStack.append(m_next.get()); ASSERT(m_expr); nodeStack.append(m_expr.get()); } // ECMA 11.2.4 void ArgumentListNode::evaluateList(ExecState* exec, List& list) { for (ArgumentListNode* n = this; n; n = n->m_next.get()) { JSValue* v = n->m_expr->evaluate(exec); KJS_CHECKEXCEPTIONVOID list.append(v); } } // ------------------------------ ArgumentsNode -------------------------------- void ArgumentsNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_listNode) nodeStack.append(m_listNode.get()); } // ------------------------------ NewExprNode ---------------------------------- void NewExprNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_args) nodeStack.append(m_args.get()); nodeStack.append(m_expr.get()); } // ECMA 11.2.2 JSValue* NewExprNode::inlineEvaluate(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTIONVALUE List argList; if (m_args) { m_args->evaluateList(exec, argList); 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, m_expr.get()); JSObject* constr = static_cast(v); if (!constr->implementsConstruct()) return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, m_expr.get()); return constr->construct(exec, argList); } JSValue* NewExprNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double NewExprNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool NewExprNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t NewExprNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t NewExprNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } void FunctionCallValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_args.get()); nodeStack.append(m_expr.get()); } // ECMA 11.2.3 JSValue* FunctionCallValueNode::evaluate(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTIONVALUE if (!v->isObject()) { return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_expr.get()); } JSObject* func = static_cast(v); if (!func->implementsCall()) { return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_expr.get()); } List argList; m_args->evaluateList(exec, argList); KJS_CHECKEXCEPTIONVALUE JSObject* thisObj = exec->dynamicGlobalObject(); return func->call(exec, thisObj, argList); } void FunctionCallResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_args.get()); size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) new (this) LocalVarFunctionCallNode(index); } // ECMA 11.2.3 JSValue* FunctionCallResolveNode::inlineEvaluate(ExecState* exec) { // Check for missed optimization opportunity. ASSERT(!canSkipLookup(exec, m_ident)); const ScopeChain& chain = exec->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); KJS_CHECKEXCEPTIONVALUE if (!v->isObject()) return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident); JSObject* func = static_cast(v); if (!func->implementsCall()) return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident); List argList; m_args->evaluateList(exec, argList); 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 internal 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->isActivationObject()) thisObj = exec->dynamicGlobalObject(); return func->call(exec, thisObj, argList); } ++iter; } while (iter != end); return throwUndefinedVariableError(exec, m_ident); } JSValue* FunctionCallResolveNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double FunctionCallResolveNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool FunctionCallResolveNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t FunctionCallResolveNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t FunctionCallResolveNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } JSValue* LocalVarFunctionCallNode::inlineEvaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue* v = exec->localStorage()[m_index].value; if (!v->isObject()) return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident); JSObject* func = static_cast(v); if (!func->implementsCall()) return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident); List argList; m_args->evaluateList(exec, argList); KJS_CHECKEXCEPTIONVALUE return func->call(exec, exec->dynamicGlobalObject(), argList); } JSValue* LocalVarFunctionCallNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double LocalVarFunctionCallNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool LocalVarFunctionCallNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t LocalVarFunctionCallNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t LocalVarFunctionCallNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } void FunctionCallBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_args.get()); nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } // ECMA 11.2.3 JSValue* FunctionCallBracketNode::evaluate(ExecState* exec) { JSValue* baseVal = m_base->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* subscriptVal = m_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, m_base.get(), m_subscript.get()); JSObject* func = static_cast(funcVal); if (!func->implementsCall()) return throwError(exec, TypeError, "Object %s (result of expression %s[%s]) does not allow calls.", funcVal, m_base.get(), m_subscript.get()); List argList; m_args->evaluateList(exec, argList); KJS_CHECKEXCEPTIONVALUE JSObject* thisObj = baseObj; ASSERT(thisObj); ASSERT(thisObj->isObject()); ASSERT(!thisObj->isActivationObject()); 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."; } void FunctionCallDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_args.get()); nodeStack.append(m_base.get()); } // ECMA 11.2.3 JSValue* FunctionCallDotNode::inlineEvaluate(ExecState* exec) { JSValue* baseVal = m_base->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSObject* baseObj = baseVal->toObject(exec); PropertySlot slot; JSValue* funcVal = baseObj->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, baseObj, m_ident) : jsUndefined(); KJS_CHECKEXCEPTIONVALUE if (!funcVal->isObject()) return throwError(exec, TypeError, dotExprNotAnObjectString(), funcVal, m_base.get(), m_ident); JSObject* func = static_cast(funcVal); if (!func->implementsCall()) return throwError(exec, TypeError, dotExprDoesNotAllowCallsString(), funcVal, m_base.get(), m_ident); List argList; m_args->evaluateList(exec, argList); KJS_CHECKEXCEPTIONVALUE JSObject* thisObj = baseObj; ASSERT(thisObj); ASSERT(thisObj->isObject()); ASSERT(!thisObj->isActivationObject()); return func->call(exec, thisObj, argList); } JSValue* FunctionCallDotNode::evaluate(ExecState* exec) { return inlineEvaluate(exec); } double FunctionCallDotNode::evaluateToNumber(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toNumber(exec); } bool FunctionCallDotNode::evaluateToBoolean(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return v->toBoolean(exec); } int32_t FunctionCallDotNode::evaluateToInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toInt32(exec); } uint32_t FunctionCallDotNode::evaluateToUInt32(ExecState* exec) { JSValue* v = inlineEvaluate(exec); KJS_CHECKEXCEPTIONNUMBER return v->toUInt32(exec); } // ECMA 11.3 // ------------------------------ PostfixResolveNode ---------------------------------- // Increment void PostIncResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) { if (isConstant(localStorage, index)) new (this) PostIncConstNode(index); else new (this) PostIncLocalVarNode(index); } } JSValue* PostIncResolveNode::evaluate(ExecState* exec) { // Check for missed optimization opportunity. ASSERT(!canSkipLookup(exec, m_ident)); const ScopeChain& chain = exec->scopeChain(); ScopeChainIterator iter = chain.begin(); ScopeChainIterator end = chain.end(); // we must always have something in the scope chain ASSERT(iter != end); PropertySlot slot; do { if ((*iter)->getPropertySlot(exec, m_ident, slot)) { // If m_ident is 'arguments', the base->getPropertySlot() may cause // base (which must be an ActivationImp in such this case) to be torn // off from the activation stack, in which case we need to get it again // from the ScopeChainIterator. JSObject* base = *iter; JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec); base->put(exec, m_ident, jsNumber(v->toNumber(exec) + 1)); return v; } ++iter; } while (iter != end); return throwUndefinedVariableError(exec, m_ident); } void PostIncResolveNode::optimizeForUnnecessaryResult() { new (this) PreIncResolveNode(PlacementNewAdopt); } JSValue* PostIncLocalVarNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue** slot = &exec->localStorage()[m_index].value; JSValue* v = (*slot)->toJSNumber(exec); *slot = jsNumber(v->toNumber(exec) + 1); return v; } void PostIncLocalVarNode::optimizeForUnnecessaryResult() { new (this) PreIncLocalVarNode(m_index); } // Decrement void PostDecResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) { if (isConstant(localStorage, index)) new (this) PostDecConstNode(index); else new (this) PostDecLocalVarNode(index); } } JSValue* PostDecResolveNode::evaluate(ExecState* exec) { // Check for missed optimization opportunity. ASSERT(!canSkipLookup(exec, m_ident)); const ScopeChain& chain = exec->scopeChain(); ScopeChainIterator iter = chain.begin(); ScopeChainIterator end = chain.end(); // we must always have something in the scope chain ASSERT(iter != end); PropertySlot slot; do { if ((*iter)->getPropertySlot(exec, m_ident, slot)) { // See the comment in PostIncResolveNode::evaluate(). JSObject* base = *iter; JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec); base->put(exec, m_ident, jsNumber(v->toNumber(exec) - 1)); return v; } ++iter; } while (iter != end); return throwUndefinedVariableError(exec, m_ident); } void PostDecResolveNode::optimizeForUnnecessaryResult() { new (this) PreDecResolveNode(PlacementNewAdopt); } JSValue* PostDecLocalVarNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue** slot = &exec->localStorage()[m_index].value; JSValue* v = (*slot)->toJSNumber(exec); *slot = jsNumber(v->toNumber(exec) - 1); return v; } double PostDecLocalVarNode::inlineEvaluateToNumber(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue** slot = &exec->localStorage()[m_index].value; double n = (*slot)->toNumber(exec); *slot = jsNumber(n - 1); return n; } double PostDecLocalVarNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } bool PostDecLocalVarNode::evaluateToBoolean(ExecState* exec) { double result = inlineEvaluateToNumber(exec); return result > 0.0 || 0.0 > result; // NaN produces false as well } int32_t PostDecLocalVarNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t PostDecLocalVarNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } void PostDecLocalVarNode::optimizeForUnnecessaryResult() { new (this) PreDecLocalVarNode(m_index); } // ------------------------------ PostfixBracketNode ---------------------------------- void PostfixBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } JSValue* PostIncBracketNode::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 JSValue* v2 = v->toJSNumber(exec); base->put(exec, propertyIndex, jsNumber(v2->toNumber(exec) + 1)); return v2; } Identifier propertyName(subscript->toString(exec)); PropertySlot slot; JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = v->toJSNumber(exec); base->put(exec, propertyName, jsNumber(v2->toNumber(exec) + 1)); return v2; } JSValue* PostDecBracketNode::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 JSValue* v2 = v->toJSNumber(exec); base->put(exec, propertyIndex, jsNumber(v2->toNumber(exec) - 1)); return v2; } Identifier propertyName(subscript->toString(exec)); PropertySlot slot; JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = v->toJSNumber(exec); base->put(exec, propertyName, jsNumber(v2->toNumber(exec) - 1)); return v2; } // ------------------------------ PostfixDotNode ---------------------------------- void PostfixDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_base.get()); } JSValue* PostIncDotNode::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 JSValue* v2 = v->toJSNumber(exec); base->put(exec, m_ident, jsNumber(v2->toNumber(exec) + 1)); return v2; } JSValue* PostDecDotNode::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 JSValue* v2 = v->toJSNumber(exec); base->put(exec, m_ident, jsNumber(v2->toNumber(exec) - 1)); return v2; } // ------------------------------ PostfixErrorNode ----------------------------------- JSValue* PostfixErrorNode::evaluate(ExecState* exec) { throwError(exec, ReferenceError, "Postfix %s operator applied to value that is not a reference.", m_operator == OpPlusPlus ? "++" : "--"); handleException(exec); return jsUndefined(); } // ------------------------------ DeleteResolveNode ----------------------------------- void DeleteResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) new (this) LocalVarDeleteNode(); } // ECMA 11.4.1 JSValue* DeleteResolveNode::evaluate(ExecState* exec) { // Check for missed optimization opportunity. ASSERT(!canSkipLookup(exec, m_ident)); const ScopeChain& chain = exec->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); } JSValue* LocalVarDeleteNode::evaluate(ExecState*) { return jsBoolean(false); } // ------------------------------ DeleteBracketNode ----------------------------------- void DeleteBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } 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 ----------------------------------- void DeleteDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_base.get()); } 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 ----------------------------------- void DeleteValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } 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 ------------------------------------- void VoidNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } // ECMA 11.4.2 JSValue* VoidNode::evaluate(ExecState* exec) { m_expr->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return jsUndefined(); } // ECMA 11.4.3 // ------------------------------ TypeOfValueNode ----------------------------------- void TypeOfValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } 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(v)->masqueradeAsUndefined()) return jsString("undefined"); else if (static_cast(v)->implementsCall()) return jsString("function"); } return jsString("object"); } } void TypeOfResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) new (this) LocalVarTypeOfNode(index); } JSValue* LocalVarTypeOfNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); return typeStringForValue(exec->localStorage()[m_index].value); } JSValue* TypeOfResolveNode::evaluate(ExecState* exec) { const ScopeChain& chain = exec->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 ---------------------------------- void PreIncResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) { if (isConstant(localStorage, index)) new (this) PreIncConstNode(index); else new (this) PreIncLocalVarNode(index); } } JSValue* PreIncLocalVarNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue** slot = &exec->localStorage()[m_index].value; double n = (*slot)->toNumber(exec); JSValue* n2 = jsNumber(n + 1); *slot = n2; return n2; } JSValue* PreIncResolveNode::evaluate(ExecState* exec) { const ScopeChain& chain = exec->scopeChain(); ScopeChainIterator iter = chain.begin(); ScopeChainIterator end = chain.end(); // we must always have something in the scope chain ASSERT(iter != end); PropertySlot slot; do { if ((*iter)->getPropertySlot(exec, m_ident, slot)) { // See the comment in PostIncResolveNode::evaluate(). JSObject* base = *iter; JSValue* v = slot.getValue(exec, base, m_ident); double n = v->toNumber(exec); JSValue* n2 = jsNumber(n + 1); base->put(exec, m_ident, n2); return n2; } ++iter; } while (iter != end); return throwUndefinedVariableError(exec, m_ident); } void PreDecResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&) { size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) { if (isConstant(localStorage, index)) new (this) PreDecConstNode(index); else new (this) PreDecLocalVarNode(index); } } JSValue* PreDecLocalVarNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue** slot = &exec->localStorage()[m_index].value; double n = (*slot)->toNumber(exec); JSValue* n2 = jsNumber(n - 1); *slot = n2; return n2; } JSValue* PreDecResolveNode::evaluate(ExecState* exec) { const ScopeChain& chain = exec->scopeChain(); ScopeChainIterator iter = chain.begin(); ScopeChainIterator end = chain.end(); // we must always have something in the scope chain ASSERT(iter != end); PropertySlot slot; do { if ((*iter)->getPropertySlot(exec, m_ident, slot)) { // See the comment in PostIncResolveNode::evaluate(). JSObject* base = *iter; JSValue* v = slot.getValue(exec, base, m_ident); double n = v->toNumber(exec); JSValue* n2 = jsNumber(n - 1); base->put(exec, m_ident, n2); return n2; } ++iter; } while (iter != end); return throwUndefinedVariableError(exec, m_ident); } // ------------------------------ PreIncConstNode ---------------------------------- JSValue* PreIncConstNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); return jsNumber(exec->localStorage()[m_index].value->toNumber(exec) + 1); } // ------------------------------ PreDecConstNode ---------------------------------- JSValue* PreDecConstNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); return jsNumber(exec->localStorage()[m_index].value->toNumber(exec) - 1); } // ------------------------------ PostIncConstNode ---------------------------------- JSValue* PostIncConstNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); return jsNumber(exec->localStorage()[m_index].value->toNumber(exec)); } // ------------------------------ PostDecConstNode ---------------------------------- JSValue* PostDecConstNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); return jsNumber(exec->localStorage()[m_index].value->toNumber(exec)); } // ------------------------------ PrefixBracketNode ---------------------------------- void PrefixBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } JSValue* PreIncBracketNode::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 JSValue* n2 = jsNumber(v->toNumber(exec) + 1); 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 JSValue* n2 = jsNumber(v->toNumber(exec) + 1); base->put(exec, propertyName, n2); return n2; } JSValue* PreDecBracketNode::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 JSValue* n2 = jsNumber(v->toNumber(exec) - 1); 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 JSValue* n2 = jsNumber(v->toNumber(exec) - 1); base->put(exec, propertyName, n2); return n2; } // ------------------------------ PrefixDotNode ---------------------------------- void PrefixDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_base.get()); } JSValue* PreIncDotNode::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); JSValue* n2 = jsNumber(n + 1); base->put(exec, m_ident, n2); return n2; } JSValue* PreDecDotNode::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); JSValue* n2 = jsNumber(n - 1); 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_operator == OpPlusPlus ? "++" : "--"); handleException(exec); return jsUndefined(); } // ------------------------------ UnaryPlusNode -------------------------------- void UnaryPlusNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } // ECMA 11.4.6 JSValue* UnaryPlusNode::evaluate(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return v->toJSNumber(exec); } bool UnaryPlusNode::evaluateToBoolean(ExecState* exec) { return m_expr->evaluateToBoolean(exec); } double UnaryPlusNode::evaluateToNumber(ExecState* exec) { return m_expr->evaluateToNumber(exec); } int32_t UnaryPlusNode::evaluateToInt32(ExecState* exec) { return m_expr->evaluateToInt32(exec); } uint32_t UnaryPlusNode::evaluateToUInt32(ExecState* exec) { return m_expr->evaluateToInt32(exec); } // ------------------------------ NegateNode ----------------------------------- void NegateNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } // ECMA 11.4.7 JSValue* NegateNode::evaluate(ExecState* exec) { // No need to check exception, caller will do so right after evaluate() return jsNumber(-m_expr->evaluateToNumber(exec)); } double NegateNode::evaluateToNumber(ExecState* exec) { // No need to check exception, caller will do so right after evaluateToNumber() return -m_expr->evaluateToNumber(exec); } // ------------------------------ BitwiseNotNode ------------------------------- void BitwiseNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } // ECMA 11.4.8 int32_t BitwiseNotNode::inlineEvaluateToInt32(ExecState* exec) { return ~m_expr->evaluateToInt32(exec); } JSValue* BitwiseNotNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToInt32(exec)); } double BitwiseNotNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToInt32(exec); } bool BitwiseNotNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToInt32(exec); } int32_t BitwiseNotNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } uint32_t BitwiseNotNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } // ------------------------------ LogicalNotNode ------------------------------- void LogicalNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } // ECMA 11.4.9 JSValue* LogicalNotNode::evaluate(ExecState* exec) { return jsBoolean(!m_expr->evaluateToBoolean(exec)); } bool LogicalNotNode::evaluateToBoolean(ExecState* exec) { return !m_expr->evaluateToBoolean(exec); } // ------------------------------ Multiplicative Nodes ----------------------------------- void MultNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.5.1 double MultNode::inlineEvaluateToNumber(ExecState* exec) { double n1 = m_term1->evaluateToNumber(exec); KJS_CHECKEXCEPTIONNUMBER double n2 = m_term2->evaluateToNumber(exec); return n1 * n2; } JSValue* MultNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToNumber(exec)); } double MultNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } bool MultNode::evaluateToBoolean(ExecState* exec) { double result = inlineEvaluateToNumber(exec); return result > 0.0 || 0.0 > result; // NaN produces false as well } int32_t MultNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t MultNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } void DivNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.5.2 double DivNode::inlineEvaluateToNumber(ExecState* exec) { double n1 = m_term1->evaluateToNumber(exec); KJS_CHECKEXCEPTIONNUMBER double n2 = m_term2->evaluateToNumber(exec); return n1 / n2; } JSValue* DivNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToNumber(exec)); } double DivNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } int32_t DivNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t DivNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } void ModNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.5.3 double ModNode::inlineEvaluateToNumber(ExecState* exec) { double n1 = m_term1->evaluateToNumber(exec); KJS_CHECKEXCEPTIONNUMBER double n2 = m_term2->evaluateToNumber(exec); return fmod(n1, n2); } JSValue* ModNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToNumber(exec)); } double ModNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } bool ModNode::evaluateToBoolean(ExecState* exec) { double result = inlineEvaluateToNumber(exec); return result > 0.0 || 0.0 > result; // NaN produces false as well } int32_t ModNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t ModNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } // ------------------------------ Additive Nodes -------------------------------------- static JSValue* throwOutOfMemoryError(ExecState* exec) { JSObject* error = Error::create(exec, GeneralError, "Out of memory"); exec->setException(error); return error; } static double throwOutOfMemoryErrorToNumber(ExecState* exec) { JSObject* error = Error::create(exec, GeneralError, "Out of memory"); exec->setException(error); return 0.0; } // ECMA 11.6 static JSValue* addSlowCase(ExecState* exec, JSValue* v1, JSValue* v2) { // exception for the Date exception in defaultValue() JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType); JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType); if (p1->isString() || p2->isString()) { UString value = p1->toString(exec) + p2->toString(exec); if (value.isNull()) return throwOutOfMemoryError(exec); return jsString(value); } return jsNumber(p1->toNumber(exec) + p2->toNumber(exec)); } static double addSlowCaseToNumber(ExecState* exec, JSValue* v1, JSValue* v2) { // exception for the Date exception in defaultValue() JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType); JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType); if (p1->isString() || p2->isString()) { UString value = p1->toString(exec) + p2->toString(exec); if (value.isNull()) return throwOutOfMemoryErrorToNumber(exec); return value.toDouble(); } return p1->toNumber(exec) + p2->toNumber(exec); } // Fast-path choices here are based on frequency data from SunSpider: // Add case: // --------------------------- // 5627160 Add case: 1 1 // 247427 Add case: 5 5 // 20901 Add case: 5 6 // 13978 Add case: 5 1 // 4000 Add case: 1 5 // 1 Add case: 3 5 static inline JSValue* add(ExecState* exec, JSValue* v1, JSValue* v2) { JSType t1 = v1->type(); JSType t2 = v2->type(); const unsigned bothTypes = (t1 << 3) | t2; if (bothTypes == ((NumberType << 3) | NumberType)) return jsNumber(v1->toNumber(exec) + v2->toNumber(exec)); if (bothTypes == ((StringType << 3) | StringType)) { UString value = static_cast(v1)->value() + static_cast(v2)->value(); if (value.isNull()) return throwOutOfMemoryError(exec); return jsString(value); } // All other cases are pretty uncommon return addSlowCase(exec, v1, v2); } static inline double addToNumber(ExecState* exec, JSValue* v1, JSValue* v2) { JSType t1 = v1->type(); JSType t2 = v2->type(); const unsigned bothTypes = (t1 << 3) | t2; if (bothTypes == ((NumberType << 3) | NumberType)) return v1->toNumber(exec) + v2->toNumber(exec); if (bothTypes == ((StringType << 3) | StringType)) { UString value = static_cast(v1)->value() + static_cast(v2)->value(); if (value.isNull()) return throwOutOfMemoryErrorToNumber(exec); return value.toDouble(); } // All other cases are pretty uncommon return addSlowCaseToNumber(exec, v1, v2); } void AddNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.6.1 JSValue* AddNode::evaluate(ExecState* exec) { JSValue* v1 = m_term1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_term2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return add(exec, v1, v2); } double AddNode::inlineEvaluateToNumber(ExecState* exec) { JSValue* v1 = m_term1->evaluate(exec); KJS_CHECKEXCEPTIONNUMBER JSValue* v2 = m_term2->evaluate(exec); KJS_CHECKEXCEPTIONNUMBER return addToNumber(exec, v1, v2); } double AddNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } int32_t AddNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t AddNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } double AddNumbersNode::inlineEvaluateToNumber(ExecState* exec) { double n1 = m_term1->evaluateToNumber(exec); KJS_CHECKEXCEPTIONNUMBER double n2 = m_term2->evaluateToNumber(exec); return n1 + n2; } JSValue* AddNumbersNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToNumber(exec)); } double AddNumbersNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } int32_t AddNumbersNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t AddNumbersNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } JSValue* AddStringsNode::evaluate(ExecState* exec) { JSValue* v1 = m_term1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_term2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return jsString(static_cast(v1)->value() + static_cast(v2)->value()); } JSValue* AddStringLeftNode::evaluate(ExecState* exec) { JSValue* v1 = m_term1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_term2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType); return jsString(static_cast(v1)->value() + p2->toString(exec)); } JSValue* AddStringRightNode::evaluate(ExecState* exec) { JSValue* v1 = m_term1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_term2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType); return jsString(p1->toString(exec) + static_cast(v2)->value()); } void SubNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.6.2 double SubNode::inlineEvaluateToNumber(ExecState* exec) { double n1 = m_term1->evaluateToNumber(exec); KJS_CHECKEXCEPTIONNUMBER double n2 = m_term2->evaluateToNumber(exec); return n1 - n2; } JSValue* SubNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToNumber(exec)); } double SubNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToNumber(exec); } int32_t SubNode::evaluateToInt32(ExecState* exec) { return JSValue::toInt32(inlineEvaluateToNumber(exec)); } uint32_t SubNode::evaluateToUInt32(ExecState* exec) { return JSValue::toUInt32(inlineEvaluateToNumber(exec)); } // ------------------------------ Shift Nodes ------------------------------------ void LeftShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.7.1 int32_t LeftShiftNode::inlineEvaluateToInt32(ExecState* exec) { int i1 = m_term1->evaluateToInt32(exec); KJS_CHECKEXCEPTIONNUMBER unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f; return (i1 << i2); } JSValue* LeftShiftNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToInt32(exec)); } double LeftShiftNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToInt32(exec); } int32_t LeftShiftNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } uint32_t LeftShiftNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } void RightShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.7.2 int32_t RightShiftNode::inlineEvaluateToInt32(ExecState* exec) { int i1 = m_term1->evaluateToInt32(exec); KJS_CHECKEXCEPTIONNUMBER unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f; return (i1 >> i2); } JSValue* RightShiftNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToInt32(exec)); } double RightShiftNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToInt32(exec); } int32_t RightShiftNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } uint32_t RightShiftNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } void UnsignedRightShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_term1.get()); nodeStack.append(m_term2.get()); } // ECMA 11.7.3 uint32_t UnsignedRightShiftNode::inlineEvaluateToUInt32(ExecState* exec) { unsigned int i1 = m_term1->evaluateToUInt32(exec); KJS_CHECKEXCEPTIONNUMBER unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f; return (i1 >> i2); } JSValue* UnsignedRightShiftNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToUInt32(exec)); } double UnsignedRightShiftNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToUInt32(exec); } int32_t UnsignedRightShiftNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToUInt32(exec); } uint32_t UnsignedRightShiftNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToUInt32(exec); } // ------------------------------ Relational Nodes ------------------------------- static inline bool lessThan(ExecState* exec, JSValue* v1, JSValue* v2) { double n1; double n2; JSValue* p1; JSValue* p2; bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1); bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2); if (wasNotString1 | wasNotString2) return n1 < n2; return static_cast(p1)->value() < static_cast(p2)->value(); } static inline bool lessThanEq(ExecState* exec, JSValue* v1, JSValue* v2) { double n1; double n2; JSValue* p1; JSValue* p2; bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1); bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2); if (wasNotString1 | wasNotString2) return n1 <= n2; return !(static_cast(p2)->value() < static_cast(p1)->value()); } void LessNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.8.1 bool LessNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return lessThan(exec, v1, v2); } JSValue* LessNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool LessNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } bool LessNumbersNode::inlineEvaluateToBoolean(ExecState* exec) { double n1 = m_expr1->evaluateToNumber(exec); KJS_CHECKEXCEPTIONVALUE double n2 = m_expr2->evaluateToNumber(exec); return n1 < n2; } JSValue* LessNumbersNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool LessNumbersNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } bool LessStringsNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_expr2->evaluate(exec); return static_cast(v1)->value() < static_cast(v2)->value(); } JSValue* LessStringsNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool LessStringsNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void GreaterNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.8.2 bool GreaterNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return lessThan(exec, v2, v1); } JSValue* GreaterNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool GreaterNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void LessEqNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.8.3 bool LessEqNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return lessThanEq(exec, v1, v2); } JSValue* LessEqNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool LessEqNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void GreaterEqNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.8.4 bool GreaterEqNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return lessThanEq(exec, v2, v1); } JSValue* GreaterEqNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool GreaterEqNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void InstanceOfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.8.6 JSValue* InstanceOfNode::evaluate(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE if (!v2->isObject()) return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, m_expr2.get()); JSObject* o2 = static_cast(v2); // 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 objects have the property, but not all implement it, so in this // case we return false (consistent with Mozilla). if (!o2->implementsHasInstance()) return jsBoolean(false); return jsBoolean(o2->hasInstance(exec, v1)); } bool InstanceOfNode::evaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN if (!v2->isObject()) { throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'instanceof' operator.", v2, m_expr2.get()); return false; } JSObject* o2 = static_cast(v2); // 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 objects have the property, but not all implement it, so in this // case we return false (consistent with Mozilla). if (!o2->implementsHasInstance()) return false; return o2->hasInstance(exec, v1); } void InNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.8.7 JSValue* InNode::evaluate(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE if (!v2->isObject()) return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'in' operator.", v2, m_expr2.get()); return jsBoolean(static_cast(v2)->hasProperty(exec, Identifier(v1->toString(exec)))); } bool InNode::evaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN if (!v2->isObject()) { throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'in' operator.", v2, m_expr2.get()); return false; } return static_cast(v2)->hasProperty(exec, Identifier(v1->toString(exec))); } // ------------------------------ Equality Nodes ------------------------------------ void EqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.9.1 bool EqualNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return equal(exec, v1, v2); } JSValue* EqualNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool EqualNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void NotEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.9.2 bool NotEqualNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return !equal(exec,v1, v2); } JSValue* NotEqualNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool NotEqualNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void StrictEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.9.4 bool StrictEqualNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return strictEqual(exec,v1, v2); } JSValue* StrictEqualNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool StrictEqualNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } void NotStrictEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.9.5 bool NotStrictEqualNode::inlineEvaluateToBoolean(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONBOOLEAN return !strictEqual(exec,v1, v2); } JSValue* NotStrictEqualNode::evaluate(ExecState* exec) { return jsBoolean(inlineEvaluateToBoolean(exec)); } bool NotStrictEqualNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToBoolean(exec); } // ------------------------------ Bit Operation Nodes ---------------------------------- void BitAndNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.10 JSValue* BitAndNode::evaluate(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return jsNumberFromAnd(exec, v1, v2); } int32_t BitAndNode::inlineEvaluateToInt32(ExecState* exec) { int32_t i1 = m_expr1->evaluateToInt32(exec); KJS_CHECKEXCEPTIONNUMBER int32_t i2 = m_expr2->evaluateToInt32(exec); return (i1 & i2); } double BitAndNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToInt32(exec); } bool BitAndNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToInt32(exec); } int32_t BitAndNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } uint32_t BitAndNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } void BitXOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } int32_t BitXOrNode::inlineEvaluateToInt32(ExecState* exec) { int i1 = m_expr1->evaluateToInt32(exec); KJS_CHECKEXCEPTIONNUMBER int i2 = m_expr2->evaluateToInt32(exec); return (i1 ^ i2); } JSValue* BitXOrNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToInt32(exec)); } double BitXOrNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToInt32(exec); } bool BitXOrNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToInt32(exec); } int32_t BitXOrNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } uint32_t BitXOrNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } void BitOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } int32_t BitOrNode::inlineEvaluateToInt32(ExecState* exec) { int i1 = m_expr1->evaluateToInt32(exec); KJS_CHECKEXCEPTIONNUMBER int i2 = m_expr2->evaluateToInt32(exec); return (i1 | i2); } JSValue* BitOrNode::evaluate(ExecState* exec) { return jsNumber(inlineEvaluateToInt32(exec)); } double BitOrNode::evaluateToNumber(ExecState* exec) { return inlineEvaluateToInt32(exec); } bool BitOrNode::evaluateToBoolean(ExecState* exec) { return inlineEvaluateToInt32(exec); } int32_t BitOrNode::evaluateToInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } uint32_t BitOrNode::evaluateToUInt32(ExecState* exec) { return inlineEvaluateToInt32(exec); } // ------------------------------ Binary Logical Nodes ---------------------------- void LogicalAndNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.11 JSValue* LogicalAndNode::evaluate(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE bool b1 = v1->toBoolean(exec); KJS_CHECKEXCEPTIONVALUE if (!b1) return v1; JSValue* v2 = m_expr2->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return v2; } bool LogicalAndNode::evaluateToBoolean(ExecState* exec) { bool b = m_expr1->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONBOOLEAN return b && m_expr2->evaluateToBoolean(exec); } void LogicalOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } JSValue* LogicalOrNode::evaluate(ExecState* exec) { JSValue* v1 = m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE if (v1->toBoolean(exec)) return v1; return m_expr2->evaluate(exec); } bool LogicalOrNode::evaluateToBoolean(ExecState* exec) { bool b = m_expr1->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONBOOLEAN return b || m_expr2->evaluateToBoolean(exec); } // ------------------------------ ConditionalNode ------------------------------ void ConditionalNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); nodeStack.append(m_logical.get()); } // ECMA 11.12 JSValue* ConditionalNode::evaluate(ExecState* exec) { bool b = m_logical->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONVALUE return b ? m_expr1->evaluate(exec) : m_expr2->evaluate(exec); } bool ConditionalNode::evaluateToBoolean(ExecState* exec) { bool b = m_logical->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONBOOLEAN return b ? m_expr1->evaluateToBoolean(exec) : m_expr2->evaluateToBoolean(exec); } double ConditionalNode::evaluateToNumber(ExecState* exec) { bool b = m_logical->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONNUMBER return b ? m_expr1->evaluateToNumber(exec) : m_expr2->evaluateToNumber(exec); } int32_t ConditionalNode::evaluateToInt32(ExecState* exec) { bool b = m_logical->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONNUMBER return b ? m_expr1->evaluateToInt32(exec) : m_expr2->evaluateToInt32(exec); } uint32_t ConditionalNode::evaluateToUInt32(ExecState* exec) { bool b = m_logical->evaluateToBoolean(exec); KJS_CHECKEXCEPTIONNUMBER return b ? m_expr1->evaluateToUInt32(exec) : m_expr2->evaluateToUInt32(exec); } // ECMA 11.13 static ALWAYS_INLINE JSValue* valueForReadModifyAssignment(ExecState* exec, JSValue* current, ExpressionNode* right, Operator oper) KJS_FAST_CALL; static ALWAYS_INLINE JSValue* valueForReadModifyAssignment(ExecState* exec, JSValue* current, ExpressionNode* right, Operator oper) { JSValue* v; int i1; int i2; unsigned int ui; switch (oper) { case OpMultEq: v = jsNumber(current->toNumber(exec) * right->evaluateToNumber(exec)); break; case OpDivEq: v = jsNumber(current->toNumber(exec) / right->evaluateToNumber(exec)); break; case OpPlusEq: v = add(exec, current, right->evaluate(exec)); break; case OpMinusEq: v = jsNumber(current->toNumber(exec) - right->evaluateToNumber(exec)); break; case OpLShift: i1 = current->toInt32(exec); i2 = right->evaluateToInt32(exec); v = jsNumber(i1 << i2); break; case OpRShift: i1 = current->toInt32(exec); i2 = right->evaluateToInt32(exec); v = jsNumber(i1 >> i2); break; case OpURShift: ui = current->toUInt32(exec); i2 = right->evaluateToInt32(exec); v = jsNumber(ui >> i2); break; case OpAndEq: i1 = current->toInt32(exec); i2 = right->evaluateToInt32(exec); v = jsNumber(i1 & i2); break; case OpXOrEq: i1 = current->toInt32(exec); i2 = right->evaluateToInt32(exec); v = jsNumber(i1 ^ i2); break; case OpOrEq: i1 = current->toInt32(exec); i2 = right->evaluateToInt32(exec); v = jsNumber(i1 | i2); break; case OpModEq: { double d1 = current->toNumber(exec); double d2 = right->evaluateToNumber(exec); v = jsNumber(fmod(d1, d2)); } break; default: ASSERT_NOT_REACHED(); v = jsUndefined(); } return v; } // ------------------------------ ReadModifyResolveNode ----------------------------------- void ReadModifyResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) { nodeStack.append(m_right.get()); size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) { if (isConstant(localStorage, index)) new (this) ReadModifyConstNode(index); else new (this) ReadModifyLocalVarNode(index); } } // ------------------------------ AssignResolveNode ----------------------------------- void AssignResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) { nodeStack.append(m_right.get()); size_t index = symbolTable.get(m_ident.ustring().rep()); if (index != missingSymbolMarker()) { if (isConstant(localStorage, index)) new (this) AssignConstNode; else new (this) AssignLocalVarNode(index); } } // ------------------------------ ReadModifyLocalVarNode ----------------------------------- JSValue* ReadModifyLocalVarNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); ASSERT(m_operator != OpEqual); JSValue* v = valueForReadModifyAssignment(exec, exec->localStorage()[m_index].value, m_right.get(), m_operator); KJS_CHECKEXCEPTIONVALUE // We can't store a pointer into localStorage() and use it throughout the function // body, because valueForReadModifyAssignment() might cause an ActivationImp tear-off, // changing the value of localStorage(). exec->localStorage()[m_index].value = v; return v; } // ------------------------------ AssignLocalVarNode ----------------------------------- JSValue* AssignLocalVarNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue* v = m_right->evaluate(exec); KJS_CHECKEXCEPTIONVALUE exec->localStorage()[m_index].value = v; return v; } // ------------------------------ ReadModifyConstNode ----------------------------------- JSValue* ReadModifyConstNode::evaluate(ExecState* exec) { ASSERT(exec->variableObject() == exec->scopeChain().top()); JSValue* left = exec->localStorage()[m_index].value; ASSERT(m_operator != OpEqual); JSValue* result = valueForReadModifyAssignment(exec, left, m_right.get(), m_operator); KJS_CHECKEXCEPTIONVALUE return result; } // ------------------------------ AssignConstNode ----------------------------------- JSValue* AssignConstNode::evaluate(ExecState* exec) { return m_right->evaluate(exec); } JSValue* ReadModifyResolveNode::evaluate(ExecState* exec) { const ScopeChain& chain = exec->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)) { // See the comment in PostIncResolveNode::evaluate(). base = *iter; goto found; } ++iter; } while (iter != end); ASSERT(m_operator != OpEqual); return throwUndefinedVariableError(exec, m_ident); found: JSValue* v; ASSERT(m_operator != OpEqual); JSValue* v1 = slot.getValue(exec, base, m_ident); KJS_CHECKEXCEPTIONVALUE v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); KJS_CHECKEXCEPTIONVALUE // Since valueForReadModifyAssignment() might cause an ActivationImp tear-off, // we need to get the base from the ScopeChainIterator again. (*iter)->put(exec, m_ident, v); return v; } JSValue* AssignResolveNode::evaluate(ExecState* exec) { const ScopeChain& chain = exec->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)) { // See the comment in PostIncResolveNode::evaluate(). base = *iter; goto found; } ++iter; } while (iter != end); found: JSValue* v = m_right->evaluate(exec); KJS_CHECKEXCEPTIONVALUE base->put(exec, m_ident, v); return v; } // ------------------------------ ReadModifyDotNode ----------------------------------- void AssignDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_right.get()); nodeStack.append(m_base.get()); } JSValue* AssignDotNode::evaluate(ExecState* exec) { JSValue* baseValue = m_base->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSObject* base = baseValue->toObject(exec); JSValue* v = m_right->evaluate(exec); KJS_CHECKEXCEPTIONVALUE base->put(exec, m_ident, v); return v; } void ReadModifyDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_right.get()); nodeStack.append(m_base.get()); } JSValue* ReadModifyDotNode::evaluate(ExecState* exec) { JSValue* baseValue = m_base->evaluate(exec); KJS_CHECKEXCEPTIONVALUE JSObject* base = baseValue->toObject(exec); JSValue* v; ASSERT(m_operator != OpEqual); PropertySlot slot; JSValue* v1 = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined(); KJS_CHECKEXCEPTIONVALUE v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); 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 ----------------------------------- void AssignBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_right.get()); nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } 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 = m_right->evaluate(exec); KJS_CHECKEXCEPTIONVALUE base->put(exec, propertyIndex, v); return v; } Identifier propertyName(subscript->toString(exec)); JSValue* v = m_right->evaluate(exec); KJS_CHECKEXCEPTIONVALUE base->put(exec, propertyName, v); return v; } void ReadModifyBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_right.get()); nodeStack.append(m_subscript.get()); nodeStack.append(m_base.get()); } JSValue* ReadModifyBracketNode::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; ASSERT(m_operator != OpEqual); PropertySlot slot; JSValue* v1 = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined(); KJS_CHECKEXCEPTIONVALUE v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); KJS_CHECKEXCEPTIONVALUE base->put(exec, propertyIndex, v); return v; } Identifier propertyName(subscript->toString(exec)); JSValue* v; ASSERT(m_operator != OpEqual); PropertySlot slot; JSValue* v1 = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined(); KJS_CHECKEXCEPTIONVALUE v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator); KJS_CHECKEXCEPTIONVALUE base->put(exec, propertyName, v); return v; } // ------------------------------ CommaNode ------------------------------------ void CommaNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 11.14 JSValue* CommaNode::evaluate(ExecState* exec) { m_expr1->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return m_expr2->evaluate(exec); } // ------------------------------ ConstDeclNode ---------------------------------- ConstDeclNode::ConstDeclNode(const Identifier& ident, ExpressionNode* init) : m_ident(ident) , m_init(init) { } void ConstDeclNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_next) nodeStack.append(m_next.get()); if (m_init) nodeStack.append(m_init.get()); } void ConstDeclNode::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, m_ident, slot)) break; ++iter; } while (iter != end); unsigned flags = 0; base->getPropertyAttributes(m_ident, flags); flags |= ReadOnly; base->put(exec, m_ident, val, flags); } // ECMA 12.2 inline void ConstDeclNode::evaluateSingle(ExecState* exec) { ASSERT(exec->variableObject()->hasOwnProperty(exec, m_ident) || exec->codeType() == EvalCode); // Guaranteed by processDeclarations. const ScopeChain& chain = exec->scopeChain(); JSObject* variableObject = exec->variableObject(); ASSERT(!chain.isEmpty()); bool inGlobalScope = ++chain.begin() == chain.end(); if (m_init) { if (inGlobalScope) { JSValue* val = m_init->evaluate(exec); int flags = Internal; if (exec->codeType() != EvalCode) flags |= DontDelete; flags |= ReadOnly; variableObject->put(exec, m_ident, val, flags); } else { JSValue* val = m_init->evaluate(exec); KJS_CHECKEXCEPTIONVOID // 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(m_ident, flags); flags |= ReadOnly; variableObject->put(exec, m_ident, val, flags); } } } JSValue* ConstDeclNode::evaluate(ExecState* exec) { evaluateSingle(exec); if (ConstDeclNode* n = m_next.get()) { do { n->evaluateSingle(exec); KJS_CHECKEXCEPTIONVALUE n = n->m_next.get(); } while (n); } return jsUndefined(); } // ------------------------------ ConstStatementNode ----------------------------- void ConstStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { ASSERT(m_next); nodeStack.append(m_next.get()); } // ECMA 12.2 JSValue* ConstStatementNode::execute(ExecState* exec) { m_next->evaluate(exec); KJS_CHECKEXCEPTION return exec->setNormalCompletion(); } // ------------------------------ Helper functions for handling Vectors of StatementNode ------------------------------- static inline void statementListPushFIFO(StatementVector& statements, DeclarationStacks::NodeStack& stack) { StatementVector::iterator it = statements.end(); StatementVector::iterator begin = statements.begin(); while (it != begin) { --it; stack.append((*it).get()); } } static inline Node* statementListInitializeVariableAccessStack(StatementVector& statements, DeclarationStacks::NodeStack& stack) { if (statements.isEmpty()) return 0; StatementVector::iterator it = statements.end(); StatementVector::iterator begin = statements.begin(); StatementVector::iterator beginPlusOne = begin + 1; while (it != beginPlusOne) { --it; stack.append((*it).get()); } return (*begin).get(); } static inline JSValue* statementListExecute(StatementVector& statements, ExecState* exec) { JSValue* value = 0; size_t size = statements.size(); for (size_t i = 0; i != size; ++i) { JSValue* statementValue = statements[i]->execute(exec); if (statementValue) value = statementValue; if (exec->completionType() != Normal) return value; } return exec->setNormalCompletion(value); } // ------------------------------ BlockNode ------------------------------------ BlockNode::BlockNode(SourceElements* children) { if (children) children->releaseContentsIntoVector(m_children); } void BlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { statementListPushFIFO(m_children, nodeStack); } // ECMA 12.1 JSValue* BlockNode::execute(ExecState* exec) { return statementListExecute(m_children, exec); } // ------------------------------ EmptyStatementNode --------------------------- // ECMA 12.3 JSValue* EmptyStatementNode::execute(ExecState* exec) { return exec->setNormalCompletion(); } // ------------------------------ ExprStatementNode ---------------------------- void ExprStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { ASSERT(m_expr); nodeStack.append(m_expr.get()); } // ECMA 12.4 JSValue* ExprStatementNode::execute(ExecState* exec) { JSValue* value = m_expr->evaluate(exec); KJS_CHECKEXCEPTION return exec->setNormalCompletion(value); } // ------------------------------ VarStatementNode ---------------------------- void VarStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { ASSERT(m_expr); nodeStack.append(m_expr.get()); } JSValue* VarStatementNode::execute(ExecState* exec) { m_expr->evaluate(exec); KJS_CHECKEXCEPTION return exec->setNormalCompletion(); } // ------------------------------ IfNode --------------------------------------- void IfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_ifBlock.get()); nodeStack.append(m_condition.get()); } // ECMA 12.5 JSValue* IfNode::execute(ExecState* exec) { bool b = m_condition->evaluateToBoolean(exec); KJS_CHECKEXCEPTION if (b) return m_ifBlock->execute(exec); return exec->setNormalCompletion(); } void IfElseNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack) { nodeStack.append(m_elseBlock.get()); IfNode::optimizeVariableAccess(symbolTable, localStorage, nodeStack); } // ECMA 12.5 JSValue* IfElseNode::execute(ExecState* exec) { bool b = m_condition->evaluateToBoolean(exec); KJS_CHECKEXCEPTION if (b) return m_ifBlock->execute(exec); return m_elseBlock->execute(exec); } // ------------------------------ DoWhileNode ---------------------------------- void DoWhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_statement.get()); nodeStack.append(m_expr.get()); } // ECMA 12.6.1 JSValue* DoWhileNode::execute(ExecState* exec) { JSValue* value = 0; while (1) { exec->pushIteration(); JSValue* statementValue = m_statement->execute(exec); exec->popIteration(); if (exec->dynamicGlobalObject()->timedOut()) return exec->setInterruptedCompletion(); if (statementValue) value = statementValue; if (exec->completionType() != Normal) { if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) goto continueDoWhileLoop; if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) break; return statementValue; } continueDoWhileLoop: bool b = m_expr->evaluateToBoolean(exec); KJS_CHECKEXCEPTION if (!b) break; } return exec->setNormalCompletion(value); } // ------------------------------ WhileNode ------------------------------------ void WhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_statement.get()); nodeStack.append(m_expr.get()); } // ECMA 12.6.2 JSValue* WhileNode::execute(ExecState* exec) { JSValue* value = 0; while (1) { bool b = m_expr->evaluateToBoolean(exec); KJS_CHECKEXCEPTION if (!b) break; exec->pushIteration(); JSValue* statementValue = m_statement->execute(exec); exec->popIteration(); if (exec->dynamicGlobalObject()->timedOut()) return exec->setInterruptedCompletion(); if (statementValue) value = statementValue; if (exec->completionType() != Normal) { if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) continue; if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) break; return statementValue; } } return exec->setNormalCompletion(value); } // ------------------------------ ForNode -------------------------------------- void ForNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_statement.get()); nodeStack.append(m_expr3.get()); nodeStack.append(m_expr2.get()); nodeStack.append(m_expr1.get()); } // ECMA 12.6.3 JSValue* ForNode::execute(ExecState* exec) { JSValue* value = 0; m_expr1->evaluate(exec); KJS_CHECKEXCEPTION while (1) { bool b = m_expr2->evaluateToBoolean(exec); KJS_CHECKEXCEPTION if (!b) break; exec->pushIteration(); JSValue* statementValue = m_statement->execute(exec); exec->popIteration(); if (statementValue) value = statementValue; if (exec->dynamicGlobalObject()->timedOut()) return exec->setInterruptedCompletion(); if (exec->completionType() != Normal) { if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) goto continueForLoop; if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) break; return statementValue; } continueForLoop: m_expr3->evaluate(exec); KJS_CHECKEXCEPTION } return exec->setNormalCompletion(value); } // ------------------------------ ForInNode ------------------------------------ ForInNode::ForInNode(ExpressionNode* l, ExpressionNode* expr, StatementNode* statement) : m_init(0L) , m_lexpr(l) , m_expr(expr) , m_statement(statement) , m_identIsVarDecl(false) { } ForInNode::ForInNode(const Identifier& ident, ExpressionNode* in, ExpressionNode* expr, StatementNode* statement) : m_ident(ident) , m_lexpr(new ResolveNode(ident)) , m_expr(expr) , m_statement(statement) , m_identIsVarDecl(true) { if (in) m_init = new AssignResolveNode(ident, in); // for( var foo = bar in baz ) } void ForInNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_statement.get()); nodeStack.append(m_expr.get()); nodeStack.append(m_lexpr.get()); if (m_init) nodeStack.append(m_init.get()); } // ECMA 12.6.4 JSValue* ForInNode::execute(ExecState* exec) { JSValue* value = 0; if (m_init) { m_init->evaluate(exec); KJS_CHECKEXCEPTION } JSValue* e = m_expr->evaluate(exec); KJS_CHECKEXCEPTION // For Null and Undefined, we want to make sure not to go through // the loop at all, because toObject will throw an exception. if (e->isUndefinedOrNull()) return exec->setNormalCompletion(); JSObject* v = e->toObject(exec); PropertyNameArray propertyNames; v->getPropertyNames(exec, propertyNames); PropertyNameArray::const_iterator end = propertyNames.end(); for (PropertyNameArray::const_iterator it = propertyNames.begin(); it != end; ++it) { const Identifier& name = *it; if (!v->hasProperty(exec, name)) continue; JSValue* str = jsOwnedString(name.ustring()); if (m_lexpr->isResolveNode()) { const Identifier& ident = static_cast(m_lexpr.get())->identifier(); const ScopeChain& chain = exec->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 (m_lexpr->isDotAccessorNode()) { const Identifier& ident = static_cast(m_lexpr.get())->identifier(); JSValue* v = static_cast(m_lexpr.get())->base()->evaluate(exec); KJS_CHECKEXCEPTION JSObject* o = v->toObject(exec); o->put(exec, ident, str); } else { ASSERT(m_lexpr->isBracketAccessorNode()); JSValue* v = static_cast(m_lexpr.get())->base()->evaluate(exec); KJS_CHECKEXCEPTION JSValue* v2 = static_cast(m_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->pushIteration(); JSValue* statementValue = m_statement->execute(exec); exec->popIteration(); if (statementValue) value = statementValue; if (exec->completionType() != Normal) { if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget())) continue; if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) break; return statementValue; } } return exec->setNormalCompletion(value); } // ------------------------------ ContinueNode --------------------------------- // ECMA 12.7 JSValue* ContinueNode::execute(ExecState* exec) { if (m_ident.isEmpty() && !exec->inIteration()) return setErrorCompletion(exec, SyntaxError, "Invalid continue statement."); if (!m_ident.isEmpty() && !exec->seenLabels().contains(m_ident)) return setErrorCompletion(exec, SyntaxError, "Label %s not found.", m_ident); return exec->setContinueCompletion(&m_ident); } // ------------------------------ BreakNode ------------------------------------ // ECMA 12.8 JSValue* BreakNode::execute(ExecState* exec) { if (m_ident.isEmpty() && !exec->inIteration() && !exec->inSwitch()) return setErrorCompletion(exec, SyntaxError, "Invalid break statement."); if (!m_ident.isEmpty() && !exec->seenLabels().contains(m_ident)) return setErrorCompletion(exec, SyntaxError, "Label %s not found."); return exec->setBreakCompletion(&m_ident); } // ------------------------------ ReturnNode ----------------------------------- void ReturnNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_value) nodeStack.append(m_value.get()); } // ECMA 12.9 JSValue* ReturnNode::execute(ExecState* exec) { CodeType codeType = exec->codeType(); if (codeType != FunctionCode) return setErrorCompletion(exec, SyntaxError, "Invalid return statement."); if (!m_value) return exec->setReturnValueCompletion(jsUndefined()); JSValue* v = m_value->evaluate(exec); KJS_CHECKEXCEPTION return exec->setReturnValueCompletion(v); } // ------------------------------ WithNode ------------------------------------- void WithNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { // Can't optimize within statement because "with" introduces a dynamic scope. nodeStack.append(m_expr.get()); } // ECMA 12.10 JSValue* WithNode::execute(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTION JSObject* o = v->toObject(exec); KJS_CHECKEXCEPTION exec->dynamicGlobalObject()->tearOffActivation(exec); exec->pushScope(o); JSValue* value = m_statement->execute(exec); exec->popScope(); return value; } // ------------------------------ CaseClauseNode ------------------------------- void CaseClauseNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_expr) nodeStack.append(m_expr.get()); statementListPushFIFO(m_children, nodeStack); } // ECMA 12.11 JSValue* CaseClauseNode::evaluate(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTIONVALUE return v; } // ECMA 12.11 JSValue* CaseClauseNode::executeStatements(ExecState* exec) { return statementListExecute(m_children, exec); } // ------------------------------ ClauseListNode ------------------------------- void ClauseListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_next) nodeStack.append(m_next.get()); nodeStack.append(m_clause.get()); } // ------------------------------ CaseBlockNode -------------------------------- CaseBlockNode::CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2) : m_list1(list1) , m_defaultClause(defaultClause) , m_list2(list2) { } void CaseBlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { if (m_list2) nodeStack.append(m_list2.get()); if (m_defaultClause) nodeStack.append(m_defaultClause.get()); if (m_list1) nodeStack.append(m_list1.get()); } // ECMA 12.11 JSValue* CaseBlockNode::executeBlock(ExecState* exec, JSValue* input) { ClauseListNode* a = m_list1.get(); while (a) { CaseClauseNode* clause = a->getClause(); a = a->getNext(); JSValue* v = clause->evaluate(exec); KJS_CHECKEXCEPTION if (strictEqual(exec, input, v)) { JSValue* res = clause->executeStatements(exec); if (exec->completionType() != Normal) return res; for (; a; a = a->getNext()) { JSValue* res = a->getClause()->executeStatements(exec); if (exec->completionType() != Normal) return res; } break; } } ClauseListNode* b = m_list2.get(); while (b) { CaseClauseNode* clause = b->getClause(); b = b->getNext(); JSValue* v = clause->evaluate(exec); KJS_CHECKEXCEPTION if (strictEqual(exec, input, v)) { JSValue* res = clause->executeStatements(exec); if (exec->completionType() != Normal) return res; goto step18; } } // default clause if (m_defaultClause) { JSValue* res = m_defaultClause->executeStatements(exec); if (exec->completionType() != Normal) return res; } b = m_list2.get(); step18: while (b) { CaseClauseNode* clause = b->getClause(); JSValue* res = clause->executeStatements(exec); if (exec->completionType() != Normal) return res; b = b->getNext(); } // bail out on error KJS_CHECKEXCEPTION return exec->setNormalCompletion(); } // ------------------------------ SwitchNode ----------------------------------- void SwitchNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_block.get()); nodeStack.append(m_expr.get()); } // ECMA 12.11 JSValue* SwitchNode::execute(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTION exec->pushSwitch(); JSValue* result = m_block->executeBlock(exec, v); exec->popSwitch(); if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget())) exec->setCompletionType(Normal); return result; } // ------------------------------ LabelNode ------------------------------------ void LabelNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_statement.get()); } // ECMA 12.12 JSValue* LabelNode::execute(ExecState* exec) { if (!exec->seenLabels().push(m_label)) return setErrorCompletion(exec, SyntaxError, "Duplicated label %s found.", m_label); JSValue* result = m_statement->execute(exec); exec->seenLabels().pop(); if (exec->completionType() == Break && exec->breakOrContinueTarget() == m_label) exec->setCompletionType(Normal); return result; } // ------------------------------ ThrowNode ------------------------------------ void ThrowNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { nodeStack.append(m_expr.get()); } // ECMA 12.13 JSValue* ThrowNode::execute(ExecState* exec) { JSValue* v = m_expr->evaluate(exec); KJS_CHECKEXCEPTION handleException(exec, v); return exec->setThrowCompletion(v); } // ------------------------------ TryNode -------------------------------------- void TryNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack) { // Can't optimize within catchBlock because "catch" introduces a dynamic scope. if (m_finallyBlock) nodeStack.append(m_finallyBlock.get()); nodeStack.append(m_tryBlock.get()); } // ECMA 12.14 JSValue* TryNode::execute(ExecState* exec) { JSValue* result = m_tryBlock->execute(exec); if (m_catchBlock && exec->completionType() == Throw) { JSObject* obj = new JSObject; obj->put(exec, m_exceptionIdent, result, DontDelete); exec->dynamicGlobalObject()->tearOffActivation(exec); exec->pushScope(obj); result = m_catchBlock->execute(exec); exec->popScope(); } if (m_finallyBlock) { ComplType savedCompletionType = exec->completionType(); JSValue* finallyResult = m_finallyBlock->execute(exec); if (exec->completionType() != Normal) result = finallyResult; else exec->setCompletionType(savedCompletionType); } return result; } // ------------------------------ FunctionBodyNode ----------------------------- ScopeNode::ScopeNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) : BlockNode(children) , m_sourceURL(parser().sourceURL()) , m_sourceId(parser().sourceId()) { if (varStack) m_varStack = *varStack; if (funcStack) m_functionStack = *funcStack; } // ------------------------------ ProgramNode ----------------------------- ProgramNode::ProgramNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) : ScopeNode(children, varStack, funcStack) { } ProgramNode* ProgramNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) { return new ProgramNode(children, varStack, funcStack); } // ------------------------------ EvalNode ----------------------------- EvalNode::EvalNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) : ScopeNode(children, varStack, funcStack) { } EvalNode* EvalNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) { return new EvalNode(children, varStack, funcStack); } // ------------------------------ FunctionBodyNode ----------------------------- FunctionBodyNode::FunctionBodyNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) : ScopeNode(children, varStack, funcStack) , m_initialized(false) { } FunctionBodyNode* FunctionBodyNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack) { if (Debugger::debuggersPresent) return new FunctionBodyNodeWithDebuggerHooks(children, varStack, funcStack); return new FunctionBodyNode(children, varStack, funcStack); } void FunctionBodyNode::initializeSymbolTable(ExecState* exec) { SymbolTable& symbolTable = exec->variableObject()->symbolTable(); ASSERT(symbolTable.isEmpty()); size_t localStorageIndex = 0; // Order must match the order in processDeclarations. for (size_t i = 0, size = m_parameters.size(); i < size; ++i, ++localStorageIndex) { UString::Rep* rep = m_parameters[i].ustring().rep(); symbolTable.set(rep, localStorageIndex); } for (size_t i = 0, size = m_functionStack.size(); i < size; ++i, ++localStorageIndex) { UString::Rep* rep = m_functionStack[i]->m_ident.ustring().rep(); symbolTable.set(rep, localStorageIndex); } for (size_t i = 0, size = m_varStack.size(); i < size; ++i, ++localStorageIndex) { Identifier& ident = m_varStack[i].first; if (ident == exec->propertyNames().arguments) continue; symbolTable.add(ident.ustring().rep(), localStorageIndex); } } void ProgramNode::initializeSymbolTable(ExecState* exec) { // If a previous script defined a symbol with the same name as one of our // symbols, to avoid breaking previously optimized nodes, we need to reuse // the symbol's existing storage index. So, we can't be as efficient as // FunctionBodyNode::initializeSymbolTable, which knows that no bindings // have yet been made. JSVariableObject* variableObject = exec->variableObject(); SymbolTable& symbolTable = variableObject->symbolTable(); size_t localStorageIndex = symbolTable.size(); size_t size; // Order must match the order in processDeclarations. size = m_functionStack.size(); m_functionIndexes.resize(size); for (size_t i = 0; i < size; ++i) { UString::Rep* rep = m_functionStack[i]->m_ident.ustring().rep(); pair result = symbolTable.add(rep, localStorageIndex); m_functionIndexes[i] = result.first->second; if (result.second) ++localStorageIndex; } size = m_varStack.size(); m_varIndexes.resize(size); for (size_t i = 0; i < size; ++i) { const Identifier& ident = m_varStack[i].first; if (variableObject->hasProperty(exec, ident)) { m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration. continue; } UString::Rep* rep = ident.ustring().rep(); pair result = symbolTable.add(rep, localStorageIndex); if (!result.second) { m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration. continue; } m_varIndexes[i] = result.first->second; ++localStorageIndex; } } void ScopeNode::optimizeVariableAccess(ExecState* exec) { NodeStack nodeStack; Node* node = statementListInitializeVariableAccessStack(m_children, nodeStack); if (!node) return; const SymbolTable& symbolTable = exec->variableObject()->symbolTable(); const LocalStorage& localStorage = exec->variableObject()->localStorage(); while (true) { node->optimizeVariableAccess(symbolTable, localStorage, nodeStack); size_t size = nodeStack.size(); if (!size) break; --size; node = nodeStack[size]; nodeStack.shrink(size); } } void FunctionBodyNode::processDeclarations(ExecState* exec) { if (!m_initialized) initializeSymbolTable(exec); if (!m_functionStack.isEmpty()) exec->dynamicGlobalObject()->tearOffActivation(exec); LocalStorage& localStorage = exec->variableObject()->localStorage(); // We can't just resize localStorage here because that would temporarily // leave uninitialized entries, which would crash GC during the mark phase. size_t totalSize = m_varStack.size() + m_parameters.size() + m_functionStack.size(); if (totalSize > localStorage.capacity()) // Doing this check inline avoids function call overhead. localStorage.reserveCapacity(totalSize); int minAttributes = Internal | DontDelete; // In order for our localStorage indexes to be correct, we must match the // order of addition in initializeSymbolTable(). const List& args = *exec->arguments(); for (size_t i = 0, size = m_parameters.size(); i < size; ++i) localStorage.uncheckedAppend(LocalStorageEntry(args[i], DontDelete)); for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) { FuncDeclNode* node = m_functionStack[i]; localStorage.uncheckedAppend(LocalStorageEntry(node->makeFunction(exec), minAttributes)); } for (size_t i = 0, size = m_varStack.size(); i < size; ++i) { int attributes = minAttributes; if (m_varStack[i].second & DeclarationStacks::IsConstant) attributes |= ReadOnly; localStorage.uncheckedAppend(LocalStorageEntry(jsUndefined(), attributes)); } if (!m_initialized) { optimizeVariableAccess(exec); m_initialized = true; } } static void gccIsCrazy() KJS_FAST_CALL; static void gccIsCrazy() { } void ProgramNode::processDeclarations(ExecState* exec) { // If you remove this call, some SunSpider tests, including // bitops-nsieve-bits.js, will regress substantially on Mac, due to a ~40% // increase in L2 cache misses. FIXME: WTF? gccIsCrazy(); initializeSymbolTable(exec); LocalStorage& localStorage = exec->variableObject()->localStorage(); // We can't just resize localStorage here because that would temporarily // leave uninitialized entries, which would crash GC during the mark phase. localStorage.reserveCapacity(localStorage.size() + m_varStack.size() + m_functionStack.size()); int minAttributes = Internal | DontDelete; // In order for our localStorage indexes to be correct, we must match the // order of addition in initializeSymbolTable(). for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) { FuncDeclNode* node = m_functionStack[i]; LocalStorageEntry entry = LocalStorageEntry(node->makeFunction(exec), minAttributes); size_t index = m_functionIndexes[i]; if (index == localStorage.size()) localStorage.uncheckedAppend(entry); else { ASSERT(index < localStorage.size()); localStorage[index] = entry; } } for (size_t i = 0, size = m_varStack.size(); i < size; ++i) { size_t index = m_varIndexes[i]; if (index == missingSymbolMarker()) continue; int attributes = minAttributes; if (m_varStack[i].second & DeclarationStacks::IsConstant) attributes |= ReadOnly; LocalStorageEntry entry = LocalStorageEntry(jsUndefined(), attributes); ASSERT(index == localStorage.size()); localStorage.uncheckedAppend(entry); } optimizeVariableAccess(exec); } void EvalNode::processDeclarations(ExecState* exec) { // We could optimize access to pre-existing symbols here, but SunSpider // reports that to be a net loss. size_t i; size_t size; JSVariableObject* variableObject = exec->variableObject(); int minAttributes = Internal; for (i = 0, size = m_varStack.size(); i < size; ++i) { Identifier& ident = m_varStack[i].first; if (variableObject->hasProperty(exec, ident)) continue; int attributes = minAttributes; if (m_varStack[i].second & DeclarationStacks::IsConstant) attributes |= ReadOnly; variableObject->put(exec, ident, jsUndefined(), attributes); } for (i = 0, size = m_functionStack.size(); i < size; ++i) { FuncDeclNode* node = m_functionStack[i]; variableObject->put(exec, node->m_ident, node->makeFunction(exec), minAttributes); } } UString FunctionBodyNode::paramString() const { UString s(""); size_t count = m_parameters.size(); for (size_t pos = 0; pos < count; ++pos) { if (!s.isEmpty()) s += ", "; s += m_parameters[pos].ustring(); } return s; } JSValue* ProgramNode::execute(ExecState* exec) { processDeclarations(exec); return ScopeNode::execute(exec); } JSValue* EvalNode::execute(ExecState* exec) { processDeclarations(exec); return ScopeNode::execute(exec); } JSValue* FunctionBodyNode::execute(ExecState* exec) { processDeclarations(exec); return ScopeNode::execute(exec); } // ------------------------------ FunctionBodyNodeWithDebuggerHooks --------------------------------- FunctionBodyNodeWithDebuggerHooks::FunctionBodyNodeWithDebuggerHooks(SourceElements* children, DeclarationStacks::VarStack* varStack, DeclarationStacks::FunctionStack* funcStack) : FunctionBodyNode(children, varStack, funcStack) { } JSValue* FunctionBodyNodeWithDebuggerHooks::execute(ExecState* exec) { if (Debugger* dbg = exec->dynamicGlobalObject()->debugger()) { if (!dbg->callEvent(exec, sourceId(), lineNo(), exec->function(), *exec->arguments())) { dbg->imp()->abort(); return exec->setInterruptedCompletion(); } } JSValue* result = FunctionBodyNode::execute(exec); if (Debugger* dbg = exec->dynamicGlobalObject()->debugger()) { if (exec->completionType() == Throw) exec->setException(result); if (!dbg->returnEvent(exec, sourceId(), lineNo(), exec->function())) { dbg->imp()->abort(); return exec->setInterruptedCompletion(); } } return result; } // ------------------------------ FuncDeclNode --------------------------------- void FuncDeclNode::addParams() { for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam()) m_body->parameters().append(p->ident()); } FunctionImp* FuncDeclNode::makeFunction(ExecState* exec) { FunctionImp* func = new FunctionImp(exec, m_ident, m_body.get(), exec->scopeChain()); JSObject* proto = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); proto->putDirect(exec->propertyNames().constructor, func, ReadOnly | DontDelete | DontEnum); func->putDirect(exec->propertyNames().prototype, proto, Internal | DontDelete); func->putDirect(exec->propertyNames().length, jsNumber(m_body->parameters().size()), ReadOnly | DontDelete | DontEnum); return func; } JSValue* FuncDeclNode::execute(ExecState* exec) { return exec->setNormalCompletion(); } // ------------------------------ FuncExprNode --------------------------------- // ECMA 13 void FuncExprNode::addParams() { for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam()) m_body->parameters().append(p->ident()); } JSValue* FuncExprNode::evaluate(ExecState* exec) { exec->dynamicGlobalObject()->tearOffActivation(exec); bool named = !m_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; exec->pushScope(functionScopeObject); } FunctionImp* func = new FunctionImp(exec, m_ident, m_body.get(), exec->scopeChain()); JSObject* proto = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList()); proto->putDirect(exec->propertyNames().constructor, func, ReadOnly | DontDelete | DontEnum); func->putDirect(exec->propertyNames().prototype, proto, Internal | DontDelete); if (named) { functionScopeObject->putDirect(m_ident, func, Internal | ReadOnly | (exec->codeType() == EvalCode ? 0 : DontDelete)); exec->popScope(); } return func; } } // namespace KJS