X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/81345200c95645a1b0d2635520f96ad55dfde63f..HEAD:/bytecompiler/BytecodeGenerator.cpp diff --git a/bytecompiler/BytecodeGenerator.cpp b/bytecompiler/BytecodeGenerator.cpp index 734546a..8b2f8e6 100644 --- a/bytecompiler/BytecodeGenerator.cpp +++ b/bytecompiler/BytecodeGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich * Copyright (C) 2012 Igalia, S.L. * @@ -31,10 +31,12 @@ #include "config.h" #include "BytecodeGenerator.h" +#include "BuiltinExecutables.h" #include "Interpreter.h" -#include "JSActivation.h" #include "JSFunction.h" +#include "JSLexicalEnvironment.h" #include "JSNameScope.h" +#include "JSTemplateRegistryKey.h" #include "LowLevelInterpreter.h" #include "JSCInlines.h" #include "Options.h" @@ -55,7 +57,7 @@ void Label::setLocation(unsigned location) unsigned size = m_unresolvedJumps.size(); for (unsigned i = 0; i < size; ++i) - m_generator->m_instructions[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; + m_generator.instructions()[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; } ParserError BytecodeGenerator::generate() @@ -63,12 +65,36 @@ ParserError BytecodeGenerator::generate() SamplingRegion samplingRegion("Bytecode Generation"); m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); - for (size_t i = 0; i < m_deconstructedParameters.size(); i++) { - auto& entry = m_deconstructedParameters[i]; + + // If we have declared a variable named "arguments" and we are using arguments then we should + // perform that assignment now. + if (m_needToInitializeArguments) + initializeVariable(variable(propertyNames().arguments), m_argumentsRegister); + + for (size_t i = 0; i < m_destructuringParameters.size(); i++) { + auto& entry = m_destructuringParameters[i]; entry.second->bindValue(*this, entry.first.get()); } - m_scopeNode->emitBytecode(*this); + { + RefPtr temp = newTemporary(); + RefPtr globalScope = scopeRegister(); // FIXME: With lexical scoping, this won't always be the global object: https://bugs.webkit.org/show_bug.cgi?id=142944 + for (auto functionPair : m_functionsToInitialize) { + FunctionBodyNode* functionBody = functionPair.first; + FunctionVariableType functionType = functionPair.second; + emitNewFunction(temp.get(), functionBody); + if (functionType == NormalFunctionVariable) + initializeVariable(variable(functionBody->ident()) , temp.get()); + else if (functionType == GlobalFunctionVariable) + emitPutToScope(globalScope.get(), Variable(functionBody->ident()), temp.get(), ThrowIfNotFound); + else + RELEASE_ASSERT_NOT_REACHED(); + } + } + + bool callingClassConstructor = constructorKind() != ConstructorKind::None && !isConstructor(); + if (!callingClassConstructor) + m_scopeNode->emitBytecode(*this); m_staticPropertyAnalyzer.kill(); @@ -103,11 +129,10 @@ ParserError BytecodeGenerator::generate() continue; ASSERT(range.tryData->targetScopeDepth != UINT_MAX); - UnlinkedHandlerInfo info = { - static_cast(start), static_cast(end), - static_cast(range.tryData->target->bind()), - range.tryData->targetScopeDepth - }; + ASSERT(range.tryData->handlerType != HandlerType::Illegal); + UnlinkedHandlerInfo info(static_cast(start), static_cast(end), + static_cast(range.tryData->target->bind()), range.tryData->targetScopeDepth, + range.tryData->handlerType); m_codeBlock->addExceptionHandler(info); } @@ -115,86 +140,38 @@ ParserError BytecodeGenerator::generate() m_codeBlock->shrinkToFit(); - if (m_codeBlock->symbolTable()) - m_codeBlock->setSymbolTable(m_codeBlock->symbolTable()->cloneCapturedNames(*m_codeBlock->vm())); + if (m_codeBlock->symbolTable() && !m_codeBlock->vm()->typeProfiler()) + m_codeBlock->setSymbolTable(m_codeBlock->symbolTable()->cloneScopePart(*m_codeBlock->vm())); if (m_expressionTooDeep) return ParserError(ParserError::OutOfMemory); return ParserError(ParserError::ErrorNone); } -bool BytecodeGenerator::addVar( - const Identifier& ident, ConstantMode constantMode, WatchMode watchMode, RegisterID*& r0) -{ - ASSERT(static_cast(m_codeBlock->m_numVars) == m_calleeRegisters.size()); - - ConcurrentJITLocker locker(symbolTable().m_lock); - int index = virtualRegisterForLocal(m_calleeRegisters.size()).offset(); - SymbolTableEntry newEntry(index, constantMode == IsConstant ? ReadOnly : 0); - SymbolTable::Map::AddResult result = symbolTable().add(locker, ident.impl(), newEntry); - - if (!result.isNewEntry) { - r0 = ®isterFor(result.iterator->value.getIndex()); - return false; - } - - if (watchMode == IsWatchable) { - while (m_watchableVariables.size() < static_cast(m_codeBlock->m_numVars)) - m_watchableVariables.append(Identifier()); - m_watchableVariables.append(ident); - } - - r0 = addVar(); - - ASSERT(watchMode == NotWatchable || static_cast(m_codeBlock->m_numVars) == m_watchableVariables.size()); - - return true; -} - -void BytecodeGenerator::preserveLastVar() -{ - if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) - m_lastVar = &m_calleeRegisters.last(); -} - BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) - , m_symbolTable(0) , m_scopeNode(programNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) - , m_activationRegister(0) - , m_emptyValueRegister(0) - , m_globalObjectRegister(0) - , m_finallyDepth(0) - , m_localScopeDepth(0) , m_codeType(GlobalCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_firstLazyFunction(0) - , m_lastLazyFunction(0) - , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) - , m_lastOpcodeID(op_end) -#ifndef NDEBUG - , m_lastOpcodePosition(0) -#endif - , m_usesExceptions(false) - , m_expressionTooDeep(false) - , m_isBuiltinFunction(false) { + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; + m_codeBlock->setNumParameters(1); // Allocate space for "this" emitOpcode(op_enter); + allocateAndEmitScope(); + const VarStack& varStack = programNode->varStack(); const FunctionStack& functionStack = programNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) { FunctionBodyNode* function = functionStack[i]; - UnlinkedFunctionExecutable* unlinkedFunction = makeFunction(function); - codeBlock->addFunctionDeclaration(*m_vm, function->ident(), unlinkedFunction); + m_functionsToInitialize.append(std::make_pair(function, GlobalFunctionVariable)); } for (size_t i = 0; i < varStack.size(); ++i) @@ -202,38 +179,25 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedP } -BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) +BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) , m_symbolTable(codeBlock->symbolTable()) - , m_scopeNode(functionBody) + , m_scopeNode(functionNode) , m_codeBlock(vm, codeBlock) - , m_activationRegister(0) - , m_emptyValueRegister(0) - , m_globalObjectRegister(0) - , m_finallyDepth(0) - , m_localScopeDepth(0) , m_codeType(FunctionCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_firstLazyFunction(0) - , m_lastLazyFunction(0) - , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) - , m_lastOpcodeID(op_end) -#ifndef NDEBUG - , m_lastOpcodePosition(0) -#endif - , m_usesExceptions(false) - , m_expressionTooDeep(false) , m_isBuiltinFunction(codeBlock->isBuiltinFunction()) { + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; + if (m_isBuiltinFunction) m_shouldEmitDebugHooks = false; - + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); Vector boundParameterProperties; - FunctionParameters& parameters = *functionBody->parameters(); + FunctionParameters& parameters = *functionNode->parameters(); for (size_t i = 0; i < parameters.size(); i++) { auto pattern = parameters.at(i); if (pattern->isBindingNode()) @@ -241,175 +205,295 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, Unl pattern->collectBoundIdentifiers(boundParameterProperties); continue; } - m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); - emitOpcode(op_enter); - if (m_codeBlock->needsFullScopeChain() || m_shouldEmitDebugHooks) { - m_activationRegister = addVar(); - emitInitLazyRegister(m_activationRegister); - m_codeBlock->setActivationRegister(m_activationRegister->virtualRegister()); - } - - m_symbolTable->setCaptureStart(virtualRegisterForLocal(m_codeBlock->m_numVars).offset()); - - if (functionBody->usesArguments() || codeBlock->usesEval()) { // May reify arguments object. - RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code. - RegisterID* argumentsRegister = addVar(propertyNames().arguments, IsVariable, NotWatchable); // Can be changed by assigning to 'arguments'. + bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || m_codeBlock->needsFullScopeChain(); + bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); + bool needsArguments = functionNode->usesArguments() || codeBlock->usesEval(); + + auto captures = [&] (UniquedStringImpl* uid) -> bool { + if (shouldCaptureAllOfTheThings) + return true; + if (!shouldCaptureSomeOfTheThings) + return false; + if (needsArguments && uid == propertyNames().arguments.impl()) { + // Actually, we only need to capture the arguments object when we "need full activation" + // because of name scopes. But historically we did it this way, so for now we just preserve + // the old behavior. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=143072 + return true; + } + return functionNode->captures(uid); + }; + auto varKind = [&] (UniquedStringImpl* uid) -> VarKind { + return captures(uid) ? VarKind::Scope : VarKind::Stack; + }; - // We can save a little space by hard-coding the knowledge that the two - // 'arguments' values are stored in consecutive registers, and storing - // only the index of the assignable one. - codeBlock->setArgumentsRegister(argumentsRegister->virtualRegister()); - ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->virtualRegister() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); + emitOpcode(op_enter); - emitInitLazyRegister(argumentsRegister); - emitInitLazyRegister(unmodifiedArgumentsRegister); - - if (shouldTearOffArgumentsEagerly()) { - emitOpcode(op_create_arguments); - instructions().append(argumentsRegister->index()); - } + allocateAndEmitScope(); + + m_calleeRegister.setIndex(JSStack::Callee); + + if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode()) + && functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode())) { + // When we do this, we should make our local scope stack know about the function name symbol + // table. Currently this works because bytecode linking creates a phony name scope. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141885 + // Also, we could create the scope once per JSFunction instance that needs it. That wouldn't + // be any more correct, but it would be more performant. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141887 + emitPushFunctionNameScope(m_scopeRegister, functionNode->ident(), &m_calleeRegister, ReadOnly | DontDelete); } - bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); - + if (shouldCaptureSomeOfTheThings) { + m_lexicalEnvironmentRegister = addVar(); + m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister()); + emitOpcode(op_create_lexical_environment); + instructions().append(m_lexicalEnvironmentRegister->index()); + instructions().append(scopeRegister()->index()); + emitOpcode(op_mov); + instructions().append(scopeRegister()->index()); + instructions().append(m_lexicalEnvironmentRegister->index()); + } + + // Make sure the code block knows about all of our parameters, and make sure that parameters + // needing destructuring are noted. + m_parameters.grow(parameters.size() + 1); // reserve space for "this" + m_thisRegister.setIndex(initializeNextParameter()->index()); // this + for (unsigned i = 0; i < parameters.size(); ++i) { + auto pattern = parameters.at(i); + RegisterID* reg = initializeNextParameter(); + if (!pattern->isBindingNode()) + m_destructuringParameters.append(std::make_pair(reg, pattern)); + } + + // Figure out some interesting facts about our arguments. bool capturesAnyArgumentByName = false; - Vector capturedArguments; - if (functionBody->hasCapturedVariables() || shouldCaptureAllTheThings) { - FunctionParameters& parameters = *functionBody->parameters(); - capturedArguments.resize(parameters.size()); + if (functionNode->hasCapturedVariables()) { + FunctionParameters& parameters = *functionNode->parameters(); for (size_t i = 0; i < parameters.size(); ++i) { - capturedArguments[i] = 0; auto pattern = parameters.at(i); if (!pattern->isBindingNode()) continue; const Identifier& ident = static_cast(pattern)->boundProperty(); - if (!functionBody->captures(ident) && !shouldCaptureAllTheThings) - continue; - capturesAnyArgumentByName = true; - capturedArguments[i] = addVar(); + capturesAnyArgumentByName |= captures(ident.impl()); } } - if (capturesAnyArgumentByName && !shouldTearOffArgumentsEagerly()) { - size_t parameterCount = m_symbolTable->parameterCount(); - auto slowArguments = std::make_unique(parameterCount); - for (size_t i = 0; i < parameterCount; ++i) { - if (!capturedArguments[i]) { - ASSERT(slowArguments[i].status == SlowArgument::Normal); - slowArguments[i].index = CallFrame::argumentOffset(i); - continue; - } - slowArguments[i].status = SlowArgument::Captured; - slowArguments[i].index = capturedArguments[i]->index(); - } - m_symbolTable->setSlowArguments(WTF::move(slowArguments)); + if (capturesAnyArgumentByName) + ASSERT(m_lexicalEnvironmentRegister); + + // Need to know what our functions are called. Parameters have some goofy behaviors when it + // comes to functions of the same name. + for (FunctionBodyNode* function : functionNode->functionStack()) + m_functions.add(function->ident().impl()); + + if (needsArguments) { + // Create the arguments object now. We may put the arguments object into the activation if + // it is captured. Either way, we create two arguments object variables: one is our + // private variable that is immutable, and another that is the user-visible variable. The + // immutable one is only used here, or during formal parameter resolutions if we opt for + // DirectArguments. + + m_argumentsRegister = addVar(); + m_argumentsRegister->ref(); } - - RegisterID* calleeRegister = resolveCallee(functionBody); // May push to the scope chain and/or add a captured var. - - const DeclarationStacks::FunctionStack& functionStack = functionBody->functionStack(); - const DeclarationStacks::VarStack& varStack = functionBody->varStack(); - IdentifierSet test; - - // Captured variables and functions go first so that activations don't have - // to step over the non-captured locals to mark them. - if (functionBody->hasCapturedVariables()) { - for (size_t i = 0; i < boundParameterProperties.size(); i++) { - const Identifier& ident = boundParameterProperties[i]; - if (functionBody->captures(ident)) - addVar(ident, IsVariable, IsWatchable); - } - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; - const Identifier& ident = function->ident(); - if (functionBody->captures(ident)) { - m_functions.add(ident.impl()); - emitNewFunction(addVar(ident, IsVariable, IsWatchable), IsCaptured, function); + + if (needsArguments && !codeBlock->isStrictMode()) { + // If we captured any formal parameter by name, then we use ScopedArguments. Otherwise we + // use DirectArguments. With ScopedArguments, we lift all of our arguments into the + // activation. + + if (capturesAnyArgumentByName) { + m_symbolTable->setArgumentsLength(vm, parameters.size()); + + // For each parameter, we have two possibilities: + // Either it's a binding node with no function overlap, in which case it gets a name + // in the symbol table - or it just gets space reserved in the symbol table. Either + // way we lift the value into the scope. + for (unsigned i = 0; i < parameters.size(); ++i) { + ScopeOffset offset = m_symbolTable->takeNextScopeOffset(); + m_symbolTable->setArgumentOffset(vm, i, offset); + if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i))) { + VarOffset varOffset(offset); + SymbolTableEntry entry(varOffset); + // Stores to these variables via the ScopedArguments object will not do + // notifyWrite(), since that would be cumbersome. Also, watching formal + // parameters when "arguments" is in play is unlikely to be super profitable. + // So, we just disable it. + entry.disableWatching(); + m_symbolTable->set(name, entry); + } + emitOpcode(op_put_to_scope); + instructions().append(m_lexicalEnvironmentRegister->index()); + instructions().append(UINT_MAX); + instructions().append(virtualRegisterForArgument(1 + i).offset()); + instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand()); + instructions().append(0); + instructions().append(offset.offset()); } + + // This creates a scoped arguments object and copies the overflow arguments into the + // scope. It's the equivalent of calling ScopedArguments::createByCopying(). + emitOpcode(op_create_scoped_arguments); + instructions().append(m_argumentsRegister->index()); + instructions().append(m_lexicalEnvironmentRegister->index()); + } else { + // We're going to put all parameters into the DirectArguments object. First ensure + // that the symbol table knows that this is happening. + for (unsigned i = 0; i < parameters.size(); ++i) { + if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i))) + m_symbolTable->set(name, SymbolTableEntry(VarOffset(DirectArgumentsOffset(i)))); + } + + emitOpcode(op_create_direct_arguments); + instructions().append(m_argumentsRegister->index()); } - for (size_t i = 0; i < varStack.size(); ++i) { - const Identifier& ident = varStack[i].first; - if (functionBody->captures(ident)) - addVar(ident, (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable, IsWatchable); + } else { + // Create the formal parameters the normal way. Any of them could be captured, or not. If + // captured, lift them into the scope. + for (unsigned i = 0; i < parameters.size(); ++i) { + UniquedStringImpl* name = visibleNameForParameter(parameters.at(i)); + if (!name) + continue; + + if (!captures(name)) { + // This is the easy case - just tell the symbol table about the argument. It will + // be accessed directly. + m_symbolTable->set(name, SymbolTableEntry(VarOffset(virtualRegisterForArgument(1 + i)))); + continue; + } + + ScopeOffset offset = m_symbolTable->takeNextScopeOffset(); + const Identifier& ident = + static_cast(parameters.at(i))->boundProperty(); + m_symbolTable->set(name, SymbolTableEntry(VarOffset(offset))); + + emitOpcode(op_put_to_scope); + instructions().append(m_lexicalEnvironmentRegister->index()); + instructions().append(addConstant(ident)); + instructions().append(virtualRegisterForArgument(1 + i).offset()); + instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand()); + instructions().append(0); + instructions().append(offset.offset()); } } - - m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); - - bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; - m_firstLazyFunction = codeBlock->m_numVars; - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; + + if (needsArguments && codeBlock->isStrictMode()) { + // Allocate an out-of-bands arguments object. + emitOpcode(op_create_out_of_band_arguments); + instructions().append(m_argumentsRegister->index()); + } + + // Now declare all variables. + for (const Identifier& ident : boundParameterProperties) + createVariable(ident, varKind(ident.impl()), IsVariable); + for (FunctionBodyNode* function : functionNode->functionStack()) { const Identifier& ident = function->ident(); - if (!functionBody->captures(ident)) { - m_functions.add(ident.impl()); - RefPtr reg = addVar(ident, IsVariable, NotWatchable); - // Don't lazily create functions that override the name 'arguments' - // as this would complicate lazy instantiation of actual arguments. - if (!canLazilyCreateFunctions || ident == propertyNames().arguments) - emitNewFunction(reg.get(), NotCaptured, function); - else { - emitInitLazyRegister(reg.get()); - m_lazyFunctions.set(reg->virtualRegister().toLocal(), function); - } - } + createVariable(ident, varKind(ident.impl()), IsVariable); + m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable)); } - m_lastLazyFunction = canLazilyCreateFunctions ? codeBlock->m_numVars : m_firstLazyFunction; - for (size_t i = 0; i < boundParameterProperties.size(); i++) { - const Identifier& ident = boundParameterProperties[i]; - if (!functionBody->captures(ident)) - addVar(ident, IsVariable, IsWatchable); + for (auto& entry : functionNode->varStack()) { + ConstantMode constantMode = modeForIsConstant(entry.second & DeclarationStacks::IsConstant); + // Variables named "arguments" are never const. + if (entry.first == propertyNames().arguments) + constantMode = IsVariable; + createVariable(entry.first, varKind(entry.first.impl()), constantMode, IgnoreExisting); } - for (size_t i = 0; i < varStack.size(); ++i) { - const Identifier& ident = varStack[i].first; - if (!functionBody->captures(ident)) - addVar(ident, (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable, NotWatchable); + + // There are some variables that need to be preinitialized to something other than Undefined: + // + // - "arguments": unless it's used as a function or parameter, this should refer to the + // arguments object. + // + // - callee: unless it's used as a var, function, or parameter, this should refer to the + // callee (i.e. our function). + // + // - functions: these always override everything else. + // + // The most logical way to do all of this is to initialize none of the variables until now, + // and then initialize them in BytecodeGenerator::generate() in such an order that the rules + // for how these things override each other end up holding. We would initialize the callee + // first, then "arguments", then all arguments, then the functions. + // + // But some arguments are already initialized by default, since if they aren't captured and we + // don't have "arguments" then we just point the symbol table at the stack slot of those + // arguments. We end up initializing the rest of the arguments that have an uncomplicated + // binding (i.e. don't involve destructuring) above when figuring out how to lay them out, + // because that's just the simplest thing. This means that when we initialize them, we have to + // watch out for the things that override arguments (namely, functions). + // + // We also initialize callee here as well, just because it's so weird. We know whether we want + // to do this because we can just check if it's in the symbol table. + if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode()) + && !functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode()) + && m_symbolTable->get(functionNode->ident().impl()).isNull()) { + if (captures(functionNode->ident().impl())) { + ScopeOffset offset; + { + ConcurrentJITLocker locker(m_symbolTable->m_lock); + offset = m_symbolTable->takeNextScopeOffset(locker); + m_symbolTable->add( + locker, functionNode->ident().impl(), + SymbolTableEntry(VarOffset(offset), ReadOnly)); + } + + emitOpcode(op_put_to_scope); + instructions().append(m_lexicalEnvironmentRegister->index()); + instructions().append(addConstant(functionNode->ident())); + instructions().append(m_calleeRegister.index()); + instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand()); + instructions().append(0); + instructions().append(offset.offset()); + } else { + m_symbolTable->add( + functionNode->ident().impl(), + SymbolTableEntry(VarOffset(m_calleeRegister.virtualRegister()), ReadOnly)); + } } - - if (shouldCaptureAllTheThings) - m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); - - if (m_symbolTable->captureCount()) - emitOpcode(op_touch_entry); - m_parameters.grow(parameters.size() + 1); // reserve space for "this" - - // Add "this" as a parameter - int nextParameterIndex = CallFrame::thisArgumentOffset(); - m_thisRegister.setIndex(nextParameterIndex++); - m_codeBlock->addParameter(); - for (size_t i = 0; i < parameters.size(); ++i, ++nextParameterIndex) { - int index = nextParameterIndex; - auto pattern = parameters.at(i); - if (!pattern->isBindingNode()) { - m_codeBlock->addParameter(); - RegisterID& parameter = registerFor(index); - parameter.setIndex(index); - m_deconstructedParameters.append(std::make_pair(¶meter, pattern)); - continue; + // This is our final act of weirdness. "arguments" is overridden by everything except the + // callee. We add it to the symbol table if it's not already there and it's not an argument. + if (needsArguments) { + // If "arguments" is overridden by a function or destructuring parameter name, then it's + // OK for us to call createVariable() because it won't change anything. It's also OK for + // us to them tell BytecodeGenerator::generate() to write to it because it will do so + // before it initializes functions and destructuring parameters. But if "arguments" is + // overridden by a "simple" function parameter, then we have to bail: createVariable() + // would assert and BytecodeGenerator::generate() would write the "arguments" after the + // argument value had already been properly initialized. + + bool haveParameterNamedArguments = false; + for (unsigned i = 0; i < parameters.size(); ++i) { + UniquedStringImpl* name = visibleNameForParameter(parameters.at(i)); + if (name == propertyNames().arguments.impl()) { + haveParameterNamedArguments = true; + break; + } } - auto simpleParameter = static_cast(pattern); - if (capturedArguments.size() && capturedArguments[i]) { - ASSERT((functionBody->hasCapturedVariables() && functionBody->captures(simpleParameter->boundProperty())) || shouldCaptureAllTheThings); - index = capturedArguments[i]->index(); - RegisterID original(nextParameterIndex); - emitMove(capturedArguments[i], &original); + + if (!haveParameterNamedArguments) { + createVariable( + propertyNames().arguments, varKind(propertyNames().arguments.impl()), IsVariable); + m_needToInitializeArguments = true; } - addParameter(simpleParameter->boundProperty(), index); } - preserveLastVar(); - - // We declare the callee's name last because it should lose to a var, function, and/or parameter declaration. - addCallee(functionBody, calleeRegister); - + if (isConstructor()) { - emitCreateThis(&m_thisRegister); - } else if (functionBody->usesThis() || codeBlock->usesEval()) { + if (constructorKind() == ConstructorKind::Derived) { + m_newTargetRegister = addVar(); + emitMove(m_newTargetRegister, &m_thisRegister); + emitMoveEmptyValue(&m_thisRegister); + } else + emitCreateThis(&m_thisRegister); + } else if (constructorKind() != ConstructorKind::None) { + emitThrowTypeError("Cannot call a class constructor"); + } else if (functionNode->usesThis() || codeBlock->usesEval()) { m_codeBlock->addPropertyAccessInstruction(instructions().size()); emitOpcode(op_to_this); instructions().append(kill(&m_thisRegister)); instructions().append(0); + instructions().append(0); } } @@ -420,31 +504,19 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod , m_scopeNode(evalNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) - , m_activationRegister(0) - , m_emptyValueRegister(0) - , m_globalObjectRegister(0) - , m_finallyDepth(0) - , m_localScopeDepth(0) , m_codeType(EvalCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_firstLazyFunction(0) - , m_lastLazyFunction(0) - , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) - , m_lastOpcodeID(op_end) -#ifndef NDEBUG - , m_lastOpcodePosition(0) -#endif - , m_usesExceptions(false) - , m_expressionTooDeep(false) - , m_isBuiltinFunction(false) { + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); m_codeBlock->setNumParameters(1); emitOpcode(op_enter); + allocateAndEmitScope(); + const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) m_codeBlock->addFunctionDecl(makeFunction(functionStack[i])); @@ -454,101 +526,33 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod Vector variables; variables.reserveCapacity(numVariables); for (size_t i = 0; i < numVariables; ++i) { - ASSERT(varStack[i].first.impl()->isAtomic()); + ASSERT(varStack[i].first.impl()->isAtomic() || varStack[i].first.impl()->isSymbol()); variables.append(varStack[i].first); } codeBlock->adoptVariables(variables); - preserveLastVar(); } BytecodeGenerator::~BytecodeGenerator() { } -RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) -{ - emitOpcode(op_init_lazy_reg); - instructions().append(reg->index()); - ASSERT(!hasWatchableVariable(reg->index())); - return reg; -} - -RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) -{ - if (!functionNameIsInScope(functionBodyNode->ident(), functionBodyNode->functionMode())) - return 0; - - if (functionNameScopeIsDynamic(m_codeBlock->usesEval(), m_codeBlock->isStrictMode())) - return 0; - - m_calleeRegister.setIndex(JSStack::Callee); - if (functionBodyNode->captures(functionBodyNode->ident())) - return emitMove(addVar(), IsCaptured, &m_calleeRegister); - - return &m_calleeRegister; -} - -void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister) -{ - if (!calleeRegister) - return; - - symbolTable().add(functionBodyNode->ident().impl(), SymbolTableEntry(calleeRegister->index(), ReadOnly)); -} - -void BytecodeGenerator::addParameter(const Identifier& ident, int parameterIndex) +RegisterID* BytecodeGenerator::initializeNextParameter() { - // Parameters overwrite var declarations, but not function declarations. - StringImpl* rep = ident.impl(); - if (!m_functions.contains(rep)) { - symbolTable().set(rep, parameterIndex); - RegisterID& parameter = registerFor(parameterIndex); - parameter.setIndex(parameterIndex); - } - - // To maintain the calling convention, we have to allocate unique space for - // each parameter, even if the parameter doesn't make it into the symbol table. + VirtualRegister reg = virtualRegisterForArgument(m_codeBlock->numParameters()); + RegisterID& parameter = registerFor(reg); + parameter.setIndex(reg.offset()); m_codeBlock->addParameter(); + return ¶meter; } -bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) -{ - if (ident != propertyNames().arguments) - return false; - - if (!shouldOptimizeLocals()) - return false; - - SymbolTableEntry entry = symbolTable().get(ident.impl()); - if (entry.isNull()) - return false; - - if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) - return true; - - return false; -} - -RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() -{ - ASSERT(willResolveToArguments(propertyNames().arguments)); - - SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.impl()); - ASSERT(!entry.isNull()); - return ®isterFor(entry.getIndex()); -} - -RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) +UniquedStringImpl* BytecodeGenerator::visibleNameForParameter(DestructuringPatternNode* pattern) { - if (!reg->virtualRegister().isLocal()) - return reg; - - int localVariableNumber = reg->virtualRegister().toLocal(); - - if (m_lastLazyFunction <= localVariableNumber || localVariableNumber < m_firstLazyFunction) - return reg; - emitLazyNewFunction(reg, m_lazyFunctions.get(localVariableNumber)); - return reg; + if (pattern->isBindingNode()) { + const Identifier& ident = static_cast(pattern)->boundProperty(); + if (!m_functions.contains(ident.impl())) + return ident.impl(); + } + return nullptr; } RegisterID* BytecodeGenerator::newRegister() @@ -590,7 +594,7 @@ PassRefPtr