/* * 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, 2009, 2012 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * Copyright (C) 2007 Eric Seidel * Copyright (C) 2012 Igalia, S.L. * * 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 "NodeConstructors.h" #include "BytecodeGenerator.h" #include "CallFrame.h" #include "Debugger.h" #include "JIT.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "JSNameScope.h" #include "LabelScope.h" #include "Lexer.h" #include "Operations.h" #include "Parser.h" #include "PropertyNameArray.h" #include "RegExpCache.h" #include "RegExpObject.h" #include "SamplingTool.h" #include #include #include using namespace WTF; namespace JSC { /* Details of the emitBytecode function. Return value: The register holding the production's value. dst: An optional parameter specifying the most efficient destination at which to store the production's value. The callee must honor dst. The dst argument provides for a crude form of copy propagation. For example, x = 1 becomes load r[x], 1 instead of load r0, 1 mov r[x], r0 because the assignment node, "x =", passes r[x] as dst to the number node, "1". */ // ------------------------------ ThrowableExpressionData -------------------------------- RegisterID* ThrowableExpressionData::emitThrowReferenceError(BytecodeGenerator& generator, const String& message) { generator.emitExpressionInfo(divot(), startOffset(), endOffset()); generator.emitThrowReferenceError(message); return generator.newTemporary(); } // ------------------------------ NullNode ------------------------------------- RegisterID* NullNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return 0; return generator.emitLoad(dst, jsNull()); } // ------------------------------ BooleanNode ---------------------------------- RegisterID* BooleanNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return 0; return generator.emitLoad(dst, m_value); } // ------------------------------ NumberNode ----------------------------------- RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return 0; return generator.emitLoad(dst, m_value); } // ------------------------------ StringNode ----------------------------------- RegisterID* StringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return 0; return generator.emitLoad(dst, m_value); } // ------------------------------ RegExpNode ----------------------------------- RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return 0; return generator.emitNewRegExp(generator.finalDestination(dst), RegExp::create(*generator.globalData(), m_pattern.string(), regExpFlags(m_flags.string()))); } // ------------------------------ ThisNode ------------------------------------- RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (dst == generator.ignoredResult()) return 0; return generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); } // ------------------------------ ResolveNode ---------------------------------- bool ResolveNode::isPure(BytecodeGenerator& generator) const { return generator.resolve(m_ident).isStatic(); } RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { ResolveResult resolveResult = generator.resolve(m_ident); if (RegisterID* local = resolveResult.local()) { if (dst == generator.ignoredResult()) return 0; return generator.moveToDestinationIfNeeded(dst, local); } generator.emitExpressionInfo(m_startOffset + m_ident.length(), m_ident.length(), 0); return generator.emitResolve(generator.finalDestination(dst), resolveResult, m_ident); } // ------------------------------ ArrayNode ------------------------------------ RegisterID* ArrayNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { // FIXME: Should we put all of this code into emitNewArray? unsigned length = 0; ElementNode* firstPutElement; for (firstPutElement = m_element; firstPutElement; firstPutElement = firstPutElement->next()) { if (firstPutElement->elision()) break; ++length; } if (!firstPutElement && !m_elision) return generator.emitNewArray(generator.finalDestination(dst), m_element, length); RefPtr array = generator.emitNewArray(generator.tempDestination(dst), m_element, length); for (ElementNode* n = firstPutElement; n; n = n->next()) { RegisterID* value = generator.emitNode(n->value()); length += n->elision(); generator.emitPutByIndex(array.get(), length++, value); } if (m_elision) { RegisterID* value = generator.emitLoad(0, jsNumber(m_elision + length)); generator.emitPutById(array.get(), generator.propertyNames().length, value); } return generator.moveToDestinationIfNeeded(dst, array.get()); } bool ArrayNode::isSimpleArray() const { if (m_elision || m_optional) return false; for (ElementNode* ptr = m_element; ptr; ptr = ptr->next()) { if (ptr->elision()) return false; } return true; } ArgumentListNode* ArrayNode::toArgumentList(JSGlobalData* globalData, int lineNumber, int columnNumber) const { ASSERT(!m_elision && !m_optional); ElementNode* ptr = m_element; if (!ptr) return 0; JSTokenLocation location; location.line = lineNumber; location.column = columnNumber; ArgumentListNode* head = new (globalData) ArgumentListNode(location, ptr->value()); ArgumentListNode* tail = head; ptr = ptr->next(); for (; ptr; ptr = ptr->next()) { ASSERT(!ptr->elision()); tail = new (globalData) ArgumentListNode(location, tail, ptr->value()); } return head; } // ------------------------------ ObjectLiteralNode ---------------------------- RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (!m_list) { if (dst == generator.ignoredResult()) return 0; return generator.emitNewObject(generator.finalDestination(dst)); } return generator.emitNode(dst, m_list); } // ------------------------------ PropertyListNode ----------------------------- RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr newObj = generator.tempDestination(dst); generator.emitNewObject(newObj.get()); // Fast case: this loop just handles regular value properties. PropertyListNode* p = this; for (; p && p->m_node->m_type == PropertyNode::Constant; p = p->m_next) generator.emitDirectPutById(newObj.get(), p->m_node->name(), generator.emitNode(p->m_node->m_assign)); // Were there any get/set properties? if (p) { typedef std::pair GetterSetterPair; typedef HashMap GetterSetterMap; GetterSetterMap map; // Build a map, pairing get/set values together. for (PropertyListNode* q = p; q; q = q->m_next) { PropertyNode* node = q->m_node; if (node->m_type == PropertyNode::Constant) continue; GetterSetterPair pair(node, static_cast(0)); GetterSetterMap::AddResult result = map.add(node->name().impl(), pair); if (!result.isNewEntry) result.iterator->second.second = node; } // Iterate over the remaining properties in the list. for (; p; p = p->m_next) { PropertyNode* node = p->m_node; RegisterID* value = generator.emitNode(node->m_assign); // Handle regular values. if (node->m_type == PropertyNode::Constant) { generator.emitDirectPutById(newObj.get(), node->name(), value); continue; } // This is a get/set property, find its entry in the map. ASSERT(node->m_type == PropertyNode::Getter || node->m_type == PropertyNode::Setter); GetterSetterMap::iterator it = map.find(node->name().impl()); ASSERT(it != map.end()); GetterSetterPair& pair = it->second; // Was this already generated as a part of its partner? if (pair.second == node) continue; // Generate the paired node now. RefPtr getterReg; RefPtr setterReg; if (node->m_type == PropertyNode::Getter) { getterReg = value; if (pair.second) { ASSERT(pair.second->m_type == PropertyNode::Setter); setterReg = generator.emitNode(pair.second->m_assign); } else { setterReg = generator.newTemporary(); generator.emitLoad(setterReg.get(), jsUndefined()); } } else { ASSERT(node->m_type == PropertyNode::Setter); setterReg = value; if (pair.second) { ASSERT(pair.second->m_type == PropertyNode::Getter); getterReg = generator.emitNode(pair.second->m_assign); } else { getterReg = generator.newTemporary(); generator.emitLoad(getterReg.get(), jsUndefined()); } } generator.emitPutGetterSetter(newObj.get(), node->name(), getterReg.get(), setterReg.get()); } } return generator.moveToDestinationIfNeeded(dst, newObj.get()); } // ------------------------------ BracketAccessorNode -------------------------------- RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (m_base->isResolveNode() && generator.willResolveToArguments(static_cast(m_base)->identifier()) && !generator.symbolTable().slowArguments()) { RegisterID* property = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot(), startOffset(), endOffset()); return generator.emitGetArgumentByVal(generator.finalDestination(dst), generator.uncheckedRegisterForArguments(), property); } RefPtr base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); RegisterID* property = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot(), startOffset(), endOffset()); return generator.emitGetByVal(generator.finalDestination(dst), base.get(), property); } // ------------------------------ DotAccessorNode -------------------------------- RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (m_ident == generator.propertyNames().length) { if (!m_base->isResolveNode()) goto nonArgumentsPath; ResolveNode* resolveNode = static_cast(m_base); if (!generator.willResolveToArguments(resolveNode->identifier())) goto nonArgumentsPath; generator.emitExpressionInfo(divot(), startOffset(), endOffset()); return generator.emitGetArgumentsLength(generator.finalDestination(dst), generator.uncheckedRegisterForArguments()); } nonArgumentsPath: RegisterID* base = generator.emitNode(m_base); generator.emitExpressionInfo(divot(), startOffset(), endOffset()); return generator.emitGetById(generator.finalDestination(dst), base, m_ident); } // ------------------------------ ArgumentListNode ----------------------------- RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { ASSERT(m_expr); return generator.emitNode(dst, m_expr); } // ------------------------------ NewExprNode ---------------------------------- RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr func = generator.emitNode(m_expr); CallArguments callArguments(generator, m_args); return generator.emitConstruct(generator.finalDestinationOrIgnored(dst), func.get(), callArguments, divot(), startOffset(), endOffset()); } inline CallArguments::CallArguments(BytecodeGenerator& generator, ArgumentsNode* argumentsNode) : m_argumentsNode(argumentsNode) { if (generator.shouldEmitProfileHooks()) m_profileHookRegister = generator.newTemporary(); size_t argumentCountIncludingThis = 1; // 'this' register. if (argumentsNode) { for (ArgumentListNode* node = argumentsNode->m_listNode; node; node = node->m_next) ++argumentCountIncludingThis; } m_argv.grow(argumentCountIncludingThis); for (int i = argumentCountIncludingThis - 1; i >= 0; --i) { m_argv[i] = generator.newTemporary(); ASSERT(static_cast(i) == m_argv.size() - 1 || m_argv[i]->index() == m_argv[i + 1]->index() + 1); } } inline void CallArguments::newArgument(BytecodeGenerator& generator) { RefPtr tmp = generator.newTemporary(); ASSERT(m_argv.isEmpty() || tmp->index() == m_argv.last()->index() + 1); // Calling convention assumes that all arguments are contiguous. m_argv.append(tmp.release()); } // ------------------------------ EvalFunctionCallNode ---------------------------------- RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr func = generator.tempDestination(dst); CallArguments callArguments(generator, m_args); generator.emitExpressionInfo(divot() - startOffset() + 4, 4, 0); generator.emitResolveWithThis(callArguments.thisRegister(), func.get(), generator.resolve(generator.propertyNames().eval), generator.propertyNames().eval); return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallValueNode ---------------------------------- RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr func = generator.emitNode(m_expr); CallArguments callArguments(generator, m_args); generator.emitLoad(callArguments.thisRegister(), jsUndefined()); return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallResolveNode ---------------------------------- RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { ResolveResult resolveResult = generator.resolve(m_ident); if (RegisterID* local = resolveResult.local()) { RefPtr func = generator.emitMove(generator.tempDestination(dst), local); CallArguments callArguments(generator, m_args); generator.emitLoad(callArguments.thisRegister(), jsUndefined()); return generator.emitCall(generator.finalDestinationOrIgnored(dst, callArguments.thisRegister()), func.get(), callArguments, divot(), startOffset(), endOffset()); } if (resolveResult.isStatic()) { RefPtr func = generator.newTemporary(); CallArguments callArguments(generator, m_args); generator.emitGetStaticVar(func.get(), resolveResult, m_ident); generator.emitLoad(callArguments.thisRegister(), jsUndefined()); return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); } RefPtr func = generator.newTemporary(); CallArguments callArguments(generator, m_args); int identifierStart = divot() - startOffset(); generator.emitExpressionInfo(identifierStart + m_ident.length(), m_ident.length(), 0); generator.emitResolveWithThis(callArguments.thisRegister(), func.get(), resolveResult, m_ident); return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallBracketNode ---------------------------------- RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr base = generator.emitNode(m_base); RegisterID* property = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); RefPtr function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); CallArguments callArguments(generator, m_args); generator.emitMove(callArguments.thisRegister(), base.get()); return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), callArguments, divot(), startOffset(), endOffset()); } // ------------------------------ FunctionCallDotNode ---------------------------------- RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr function = generator.tempDestination(dst); CallArguments callArguments(generator, m_args); generator.emitNode(callArguments.thisRegister(), m_base); generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); generator.emitMethodCheck(); generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident); return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), callArguments, divot(), startOffset(), endOffset()); } RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr