| /* |
| * 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 <eric@webkit.org> |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 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 <math.h> |
| #include <wtf/Assertions.h> |
| #include <wtf/HashCountedSet.h> |
| #include <wtf/HashSet.h> |
| #include <wtf/MathExtras.h> |
| |
| 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) |
| LOG(KJSNodeLeaks, "LEAK: %u KJS::Node\n", count); |
| } |
| }; |
| unsigned ParserRefCountedCounter::count = 0; |
| static ParserRefCountedCounter parserRefCountedCounter; |
| #endif |
| |
| static HashSet<ParserRefCounted*>* newTrackedObjects; |
| static HashCountedSet<ParserRefCounted*>* trackedObjectExtraRefCounts; |
| |
| ParserRefCounted::ParserRefCounted() |
| { |
| #ifndef NDEBUG |
| ++ParserRefCountedCounter::count; |
| #endif |
| if (!newTrackedObjects) |
| newTrackedObjects = new HashSet<ParserRefCounted*>; |
| 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<ParserRefCounted*>::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<ParserRefCounted*>; |
| trackedObjectExtraRefCounts->add(this); |
| } |
| |
| void ParserRefCounted::deref() |
| { |
| ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this)); |
| |
| if (!trackedObjectExtraRefCounts) { |
| delete this; |
| return; |
| } |
| |
| HashCountedSet<ParserRefCounted*>::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<ParserRefCounted*>::iterator end = newTrackedObjects->end(); |
| for (HashSet<ParserRefCounted*>::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<JSObject*>(exceptionValue); |
| if (!exception->hasProperty(exec, "line") && !exception->hasProperty(exec, "sourceURL")) { |
| exception->put(exec, "line", jsNumber(m_line)); |
| exception->put(exec, "sourceURL", jsString(currentSourceURL(exec))); |
| } |
| } |
| Debugger* dbg = exec->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<StatementNode> statement) |
| { |
| if (statement->isEmptyStatement()) |
| return; |
| |
| if (Debugger::debuggersPresent) |
| m_statements.append(new BreakpointCheckStatement(statement)); |
| else |
| m_statements.append(statement); |
| } |
| |
| // ------------------------------ BreakpointCheckStatement -------------------------------- |
| |
| BreakpointCheckStatement::BreakpointCheckStatement(PassRefPtr<StatementNode> 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<JSObject*>(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<JSObject*>(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<JSObject* >(v)); |
| break; |
| case PropertyNode::Setter: |
| ASSERT(v->isObject()); |
| obj->defineSetter(exec, p->m_node->name(), static_cast<JSObject* >(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<JSObject*>(v); |
| if (!constr->implementsConstruct()) |
| return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, 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<JSObject*>(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<JSObject*>(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<JSObject*>(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<JSObject*>(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<JSObject*>(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<JSObject*>(v)->masqueradeAsUndefined()) |
| return jsString("undefined"); |
| else if (static_cast<JSObject*>(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: |
| // <times> Add case: <t1> <t2> |
| // --------------------------- |
| // 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<StringImp*>(v1)->value() + static_cast<StringImp*>(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<StringImp*>(v1)->value() + static_cast<StringImp*>(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<StringImp*>(v1)->value() + static_cast<StringImp*>(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<StringImp*>(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<StringImp*>(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<const StringImp*>(p1)->value() < static_cast<const StringImp*>(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<const StringImp*>(p2)->value() < static_cast<const StringImp*>(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<StringImp*>(v1)->value() < static_cast<StringImp*>(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<JSObject*>(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<JSObject*>(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<JSObject*>(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<JSObject*>(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<ResolveNode*>(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<DotAccessorNode*>(m_lexpr.get())->identifier(); |
| JSValue* v = static_cast<DotAccessorNode*>(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<BracketAccessorNode*>(m_lexpr.get())->base()->evaluate(exec); |
| KJS_CHECKEXCEPTION |
| JSValue* v2 = static_cast<BracketAccessorNode*>(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<SymbolTable::iterator, bool> 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<SymbolTable::iterator, bool> 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: <rdar://problem/5657439> 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 |