Simplified name scope creation for function expressions
https://bugs.webkit.org/show_bug.cgi?id=128031
Reviewed by Mark Lam.
Source/JavaScriptCore:
3X speedup on js/regress/script-tests/function-with-eval.js.
We used to emit bytecode to push a name into local scope every
time a function that needed such a name executed. Now, we push the name
into scope once on the function object, and leave it there.
This is faster, and it also reduces the number of variable resolution
modes you have to worry about when thinking about bytecode and the
debugger.
This patch is slightly complicated by the fact that we don't know if
a function needs a name scope until we parse its body. So, there's some
glue code in here to delay filling in a function's scope until we parse
its body for the first time.
* bytecode/UnlinkedCodeBlock.cpp:
(JSC::generateFunctionCodeBlock):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedFunctionExecutable::functionMode): Renamed
functionNameIsInScopeToggle to functionMode.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator): No need to emit convert_this
when debugging. The debugger will perform the conversion as needed.
(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::addCallee): Simplified this code by removing
the "my function needs a name scope, but didn't allocate one" mode.
* interpreter/Interpreter.cpp:
(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
(JSC::Interpreter::prepareForRepeatCall): Pass a scope slot through to
CodeBlock generation, so we can add a function name scope if the parsed
function body requires one.
* jit/JITOperations.cpp:
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::setUpCall): Ditto.
* parser/NodeConstructors.h:
(JSC::FuncExprNode::FuncExprNode):
(JSC::FuncDeclNode::FuncDeclNode):
* parser/Nodes.cpp:
(JSC::FunctionBodyNode::finishParsing):
* parser/Nodes.h:
(JSC::FunctionBodyNode::functionMode): Updated for rename.
* parser/ParserModes.h:
(JSC::functionNameIsInScope):
(JSC::functionNameScopeIsDynamic): Helper functions for reasoning about
how crazy JavaScript language semantics are.
* runtime/ArrayPrototype.cpp:
(JSC::isNumericCompareFunction):
(JSC::attemptFastSort): Updated for interface changes above.
* runtime/Executable.cpp:
(JSC::ScriptExecutable::newCodeBlockFor):
(JSC::ScriptExecutable::prepareForExecutionImpl):
(JSC::FunctionExecutable::FunctionExecutable):
* runtime/Executable.h:
(JSC::ScriptExecutable::prepareForExecution):
(JSC::FunctionExecutable::functionMode):
* runtime/JSFunction.cpp:
(JSC::JSFunction::addNameScopeIfNeeded):
* runtime/JSFunction.h:
* runtime/JSNameScope.h:
(JSC::JSNameScope::create):
(JSC::JSNameScope::JSNameScope): Added machinery for pushing a function
name scope onto a function when we first discover that it's needed.
LayoutTests:
Added a performance regression test.
* js/regress/function-with-eval-expected.txt: Added.
* js/regress/function-with-eval.html: Added.
* js/regress/script-tests/function-with-eval.js: Added.
(foo):
(bar):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@163321 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 488e4b5..592d100 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,84 @@
+2014-01-31 Geoffrey Garen <ggaren@apple.com>
+
+ Simplified name scope creation for function expressions
+ https://bugs.webkit.org/show_bug.cgi?id=128031
+
+ Reviewed by Mark Lam.
+
+ 3X speedup on js/regress/script-tests/function-with-eval.js.
+
+ We used to emit bytecode to push a name into local scope every
+ time a function that needed such a name executed. Now, we push the name
+ into scope once on the function object, and leave it there.
+
+ This is faster, and it also reduces the number of variable resolution
+ modes you have to worry about when thinking about bytecode and the
+ debugger.
+
+ This patch is slightly complicated by the fact that we don't know if
+ a function needs a name scope until we parse its body. So, there's some
+ glue code in here to delay filling in a function's scope until we parse
+ its body for the first time.
+
+ * bytecode/UnlinkedCodeBlock.cpp:
+ (JSC::generateFunctionCodeBlock):
+ (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
+ * bytecode/UnlinkedCodeBlock.h:
+ (JSC::UnlinkedFunctionExecutable::functionMode): Renamed
+ functionNameIsInScopeToggle to functionMode.
+
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator): No need to emit convert_this
+ when debugging. The debugger will perform the conversion as needed.
+
+ (JSC::BytecodeGenerator::resolveCallee):
+ (JSC::BytecodeGenerator::addCallee): Simplified this code by removing
+ the "my function needs a name scope, but didn't allocate one" mode.
+
+ * interpreter/Interpreter.cpp:
+ (JSC::Interpreter::execute):
+ (JSC::Interpreter::executeCall):
+ (JSC::Interpreter::executeConstruct):
+ (JSC::Interpreter::prepareForRepeatCall): Pass a scope slot through to
+ CodeBlock generation, so we can add a function name scope if the parsed
+ function body requires one.
+
+ * jit/JITOperations.cpp:
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::setUpCall): Ditto.
+
+ * parser/NodeConstructors.h:
+ (JSC::FuncExprNode::FuncExprNode):
+ (JSC::FuncDeclNode::FuncDeclNode):
+ * parser/Nodes.cpp:
+ (JSC::FunctionBodyNode::finishParsing):
+ * parser/Nodes.h:
+ (JSC::FunctionBodyNode::functionMode): Updated for rename.
+
+ * parser/ParserModes.h:
+ (JSC::functionNameIsInScope):
+ (JSC::functionNameScopeIsDynamic): Helper functions for reasoning about
+ how crazy JavaScript language semantics are.
+
+ * runtime/ArrayPrototype.cpp:
+ (JSC::isNumericCompareFunction):
+ (JSC::attemptFastSort): Updated for interface changes above.
+
+ * runtime/Executable.cpp:
+ (JSC::ScriptExecutable::newCodeBlockFor):
+ (JSC::ScriptExecutable::prepareForExecutionImpl):
+ (JSC::FunctionExecutable::FunctionExecutable):
+ * runtime/Executable.h:
+ (JSC::ScriptExecutable::prepareForExecution):
+ (JSC::FunctionExecutable::functionMode):
+ * runtime/JSFunction.cpp:
+ (JSC::JSFunction::addNameScopeIfNeeded):
+ * runtime/JSFunction.h:
+ * runtime/JSNameScope.h:
+ (JSC::JSNameScope::create):
+ (JSC::JSNameScope::JSNameScope): Added machinery for pushing a function
+ name scope onto a function when we first discover that it's needed.
+
2014-01-25 Darin Adler <darin@apple.com>
Stop using Unicode.h
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
index 1dfb5ac..bd95746 100644
--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
@@ -60,7 +60,7 @@
if (executable->forceUsesArguments())
body->setUsesArguments();
- body->finishParsing(executable->parameters(), executable->name(), executable->functionNameIsInScopeToggle());
+ body->finishParsing(executable->parameters(), executable->name(), executable->functionMode());
executable->recordParse(body->features(), body->hasCapturedVariables());
UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&vm, FunctionCode, ExecutableInfo(body->needsActivation(), body->usesEval(), body->isStrictMode(), kind == CodeForConstruct));
@@ -100,7 +100,7 @@
, m_startOffset(node->source().startOffset() - source.startOffset())
, m_sourceLength(node->source().length())
, m_features(node->features())
- , m_functionNameIsInScopeToggle(node->functionNameIsInScopeToggle())
+ , m_functionMode(node->functionMode())
{
}
diff --git a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
index 58015b4..7c091e1 100644
--- a/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
+++ b/Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
@@ -99,7 +99,7 @@
}
size_t parameterCount() const;
bool isInStrictContext() const { return m_isInStrictContext; }
- FunctionNameIsInScopeToggle functionNameIsInScopeToggle() const { return m_functionNameIsInScopeToggle; }
+ FunctionMode functionMode() const { return m_functionMode; }
unsigned firstLineOffset() const { return m_firstLineOffset; }
unsigned lineCount() const { return m_lineCount; }
@@ -169,7 +169,7 @@
CodeFeatures m_features;
- FunctionNameIsInScopeToggle m_functionNameIsInScopeToggle;
+ FunctionMode m_functionMode;
protected:
void finishCreation(VM& vm)
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index a532b3b..77e0bf8 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -383,7 +383,7 @@
if (isConstructor()) {
emitCreateThis(&m_thisRegister);
- } else if (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks) {
+ } else if (functionBody->usesThis() || codeBlock->usesEval()) {
m_codeBlock->addPropertyAccessInstruction(instructions().size());
emitOpcode(op_to_this);
instructions().append(kill(&m_thisRegister));
@@ -456,32 +456,24 @@
RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode)
{
- if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope())
+ 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);
- // If non-strict eval is in play, we use a separate object in the scope chain for the callee's name.
- if (m_codeBlock->usesEval() && !m_codeBlock->isStrictMode())
- emitPushFunctionNameScope(functionBodyNode->ident(), &m_calleeRegister, ReadOnly | DontDelete);
-
- if (!functionBodyNode->captures(functionBodyNode->ident()))
- return &m_calleeRegister;
-
- // Move the callee into the captured section of the stack.
- return emitMove(addVar(), IsCaptured, &m_calleeRegister);
+ return &m_calleeRegister;
}
void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister)
{
- if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope())
+ if (!calleeRegister)
return;
- // If non-strict eval is in play, we use a separate object in the scope chain for the callee's name.
- if (m_codeBlock->usesEval() && !m_codeBlock->isStrictMode())
- return;
-
- ASSERT(calleeRegister);
symbolTable().add(functionBodyNode->ident().impl(), SymbolTableEntry(calleeRegister->index(), ReadOnly));
}
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index 3734033..9f8fb26 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -875,7 +875,7 @@
if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope))
return checkedReturn(callFrame->vm().throwException(callFrame, error));
- if (JSObject* error = program->prepareForExecution(callFrame, scope, CodeForCall))
+ if (JSObject* error = program->prepareForExecution(callFrame, nullptr, &scope, CodeForCall))
return checkedReturn(callFrame->vm().throwException(callFrame, error));
ProgramCodeBlock* codeBlock = program->codeBlock();
@@ -932,7 +932,7 @@
if (isJSCall) {
// Compile the callee:
- JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForCall);
+ JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), &scope, CodeForCall);
if (UNLIKELY(!!compileError)) {
return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
}
@@ -1000,7 +1000,7 @@
if (isJSConstruct) {
// Compile the callee:
- JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForConstruct);
+ JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), &scope, CodeForConstruct);
if (UNLIKELY(!!compileError)) {
return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
}
@@ -1053,7 +1053,7 @@
return CallFrameClosure();
// Compile the callee:
- JSObject* error = functionExecutable->prepareForExecution(callFrame, scope, CodeForCall);
+ JSObject* error = functionExecutable->prepareForExecution(callFrame, function, &scope, CodeForCall);
if (error) {
callFrame->vm().throwException(callFrame, error);
return CallFrameClosure();
@@ -1134,7 +1134,7 @@
}
}
- JSObject* compileError = eval->prepareForExecution(callFrame, scope, CodeForCall);
+ JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, &scope, CodeForCall);
if (UNLIKELY(!!compileError))
return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
EvalCodeBlock* codeBlock = eval->codeBlock();
diff --git a/Source/JavaScriptCore/jit/JITOperations.cpp b/Source/JavaScriptCore/jit/JITOperations.cpp
index 24f8ed3..0f2ca90 100644
--- a/Source/JavaScriptCore/jit/JITOperations.cpp
+++ b/Source/JavaScriptCore/jit/JITOperations.cpp
@@ -693,7 +693,8 @@
return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind));
JSFunction* callee = jsCast<JSFunction*>(calleeAsFunctionCell);
- execCallee->setScope(callee->scopeUnchecked());
+ JSScope* scope = callee->scopeUnchecked();
+ execCallee->setScope(scope);
ExecutableBase* executable = callee->executable();
MacroAssemblerCodePtr codePtr;
@@ -703,7 +704,8 @@
codePtr = executable->entrypointFor(*vm, kind, MustCheckArity, registers);
else {
FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
- JSObject* error = functionExecutable->prepareForExecution(execCallee, callee->scope(), kind);
+ JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, &scope, kind);
+ execCallee->setScope(scope);
if (error) {
throwStackOverflowError(exec);
return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress());
@@ -757,11 +759,13 @@
return reinterpret_cast<char*>(handleHostCall(execCallee, calleeAsValue, kind));
JSFunction* function = jsCast<JSFunction*>(calleeAsFunctionCell);
- execCallee->setScope(function->scopeUnchecked());
+ JSScope* scope = function->scopeUnchecked();
+ execCallee->setScope(scope);
ExecutableBase* executable = function->executable();
if (UNLIKELY(!executable->hasJITCodeFor(kind))) {
FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
- JSObject* error = functionExecutable->prepareForExecution(execCallee, function->scope(), kind);
+ JSObject* error = functionExecutable->prepareForExecution(execCallee, function, &scope, kind);
+ execCallee->setScope(scope);
if (error) {
exec->vm().throwException(execCallee, error);
return reinterpret_cast<char*>(vm->getCTIStub(throwExceptionFromCallSlowPathGenerator).code().executableAddress());
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index e5031b5..070a8d6 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -1081,7 +1081,8 @@
codePtr = executable->entrypointFor(vm, kind, MustCheckArity, RegisterPreservationNotRequired);
else {
FunctionExecutable* functionExecutable = static_cast<FunctionExecutable*>(executable);
- JSObject* error = functionExecutable->prepareForExecution(execCallee, callee->scope(), kind);
+ JSObject* error = functionExecutable->prepareForExecution(execCallee, callee, &scope, kind);
+ execCallee->setScope(scope);
if (error)
LLINT_CALL_THROW(execCallee->callerFrame(), error);
codeBlock = functionExecutable->codeBlockFor(kind);
diff --git a/Source/JavaScriptCore/parser/NodeConstructors.h b/Source/JavaScriptCore/parser/NodeConstructors.h
index 9171269..2083aa4 100644
--- a/Source/JavaScriptCore/parser/NodeConstructors.h
+++ b/Source/JavaScriptCore/parser/NodeConstructors.h
@@ -769,14 +769,14 @@
: ExpressionNode(location)
, m_body(body)
{
- m_body->finishParsing(source, parameter, ident, FunctionNameIsInScope);
+ m_body->finishParsing(source, parameter, ident, FunctionExpression);
}
inline FuncDeclNode::FuncDeclNode(const JSTokenLocation& location, const Identifier& ident, FunctionBodyNode* body, const SourceCode& source, ParameterNode* parameter)
: StatementNode(location)
, m_body(body)
{
- m_body->finishParsing(source, parameter, ident, FunctionNameIsNotInScope);
+ m_body->finishParsing(source, parameter, ident, FunctionDeclaration);
}
inline CaseClauseNode::CaseClauseNode(ExpressionNode* expr, SourceElements* statements)
diff --git a/Source/JavaScriptCore/parser/Nodes.cpp b/Source/JavaScriptCore/parser/Nodes.cpp
index fc9ccd9..a96ccdb 100644
--- a/Source/JavaScriptCore/parser/Nodes.cpp
+++ b/Source/JavaScriptCore/parser/Nodes.cpp
@@ -195,18 +195,18 @@
{
}
-void FunctionBodyNode::finishParsing(const SourceCode& source, ParameterNode* firstParameter, const Identifier& ident, FunctionNameIsInScopeToggle functionNameIsInScopeToggle)
+void FunctionBodyNode::finishParsing(const SourceCode& source, ParameterNode* firstParameter, const Identifier& ident, enum FunctionMode functionMode)
{
setSource(source);
- finishParsing(FunctionParameters::create(firstParameter), ident, functionNameIsInScopeToggle);
+ finishParsing(FunctionParameters::create(firstParameter), ident, functionMode);
}
-void FunctionBodyNode::finishParsing(PassRefPtr<FunctionParameters> parameters, const Identifier& ident, FunctionNameIsInScopeToggle functionNameIsInScopeToggle)
+void FunctionBodyNode::finishParsing(PassRefPtr<FunctionParameters> parameters, const Identifier& ident, enum FunctionMode functionMode)
{
ASSERT(!source().isNull());
m_parameters = parameters;
m_ident = ident;
- m_functionNameIsInScopeToggle = functionNameIsInScopeToggle;
+ m_functionMode = functionMode;
}
FunctionBodyNode* FunctionBodyNode::create(VM* vm, const JSTokenLocation& startLocation, const JSTokenLocation& endLocation, unsigned startColumn, unsigned endColumn, bool inStrictContext)
diff --git a/Source/JavaScriptCore/parser/Nodes.h b/Source/JavaScriptCore/parser/Nodes.h
index d779a17..783282b 100644
--- a/Source/JavaScriptCore/parser/Nodes.h
+++ b/Source/JavaScriptCore/parser/Nodes.h
@@ -1532,15 +1532,14 @@
virtual void emitBytecode(BytecodeGenerator&, RegisterID* = 0) override;
- void finishParsing(const SourceCode&, ParameterNode*, const Identifier&, FunctionNameIsInScopeToggle);
- void finishParsing(PassRefPtr<FunctionParameters>, const Identifier&, FunctionNameIsInScopeToggle);
+ void finishParsing(const SourceCode&, ParameterNode*, const Identifier&, FunctionMode);
+ void finishParsing(PassRefPtr<FunctionParameters>, const Identifier&, FunctionMode);
const Identifier& ident() { return m_ident; }
void setInferredName(const Identifier& inferredName) { ASSERT(!inferredName.isNull()); m_inferredName = inferredName; }
const Identifier& inferredName() { return m_inferredName.isEmpty() ? m_ident : m_inferredName; }
- bool functionNameIsInScope() { return m_functionNameIsInScopeToggle == FunctionNameIsInScope; }
- FunctionNameIsInScopeToggle functionNameIsInScopeToggle() { return m_functionNameIsInScopeToggle; }
+ FunctionMode functionMode() { return m_functionMode; }
void setFunctionNameStart(int functionNameStart) { m_functionNameStart = functionNameStart; }
int functionNameStart() const { return m_functionNameStart; }
@@ -1557,7 +1556,7 @@
Identifier m_ident;
Identifier m_inferredName;
- FunctionNameIsInScopeToggle m_functionNameIsInScopeToggle;
+ FunctionMode m_functionMode;
RefPtr<FunctionParameters> m_parameters;
int m_functionNameStart;
unsigned m_startColumn;
diff --git a/Source/JavaScriptCore/parser/ParserModes.h b/Source/JavaScriptCore/parser/ParserModes.h
index 4e9a17c..57c9ffb 100644
--- a/Source/JavaScriptCore/parser/ParserModes.h
+++ b/Source/JavaScriptCore/parser/ParserModes.h
@@ -27,6 +27,8 @@
#ifndef ParserModes_h
#define ParserModes_h
+#include "Identifier.h"
+
namespace JSC {
enum JSParserStrictness { JSParseNormal, JSParseStrict };
@@ -35,7 +37,32 @@
enum ProfilerMode { ProfilerOff, ProfilerOn };
enum DebuggerMode { DebuggerOff, DebuggerOn };
-enum FunctionNameIsInScopeToggle { FunctionNameIsNotInScope, FunctionNameIsInScope };
+enum FunctionMode { FunctionExpression, FunctionDeclaration };
+
+inline bool functionNameIsInScope(const Identifier& name, FunctionMode functionMode)
+{
+ if (name.isNull())
+ return false;
+
+ if (functionMode != FunctionExpression)
+ return false;
+
+ return true;
+}
+
+inline bool functionNameScopeIsDynamic(bool usesEval, bool isStrictMode)
+{
+ // If non-strict eval is in play, a function gets a separate object in the scope chain for its name.
+ // This enables eval to declare and then delete a name that shadows the function's name.
+
+ if (!usesEval)
+ return false;
+
+ if (isStrictMode)
+ return false;
+
+ return true;
+}
typedef unsigned CodeFeatures;
diff --git a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
index 27ac6d4..ab8cbba 100644
--- a/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
+++ b/Source/JavaScriptCore/runtime/ArrayPrototype.cpp
@@ -75,14 +75,15 @@
namespace JSC {
-static inline bool isNumericCompareFunction(ExecState* exec, CallType callType, const CallData& callData)
+static inline bool isNumericCompareFunction(ExecState* exec, JSValue function, CallType callType, const CallData& callData)
{
if (callType != CallTypeJS)
return false;
FunctionExecutable* executable = callData.js.functionExecutable;
+ JSScope* scope = callData.js.scope;
- JSObject* error = executable->prepareForExecution(exec, callData.js.scope, CodeForCall);
+ JSObject* error = executable->prepareForExecution(exec, jsCast<JSFunction*>(function), &scope, CodeForCall);
if (error)
return false;
@@ -614,7 +615,7 @@
|| shouldUseSlowPut(thisObj->structure()->indexingType()))
return false;
- if (isNumericCompareFunction(exec, callType, callData))
+ if (isNumericCompareFunction(exec, function, callType, callData))
asArray(thisObj)->sortNumeric(exec, function, callType, callData);
else if (callType != CallTypeNone)
asArray(thisObj)->sort(exec, function, callType, callData);
diff --git a/Source/JavaScriptCore/runtime/Executable.cpp b/Source/JavaScriptCore/runtime/Executable.cpp
index 6bc464d..0e2c61b 100644
--- a/Source/JavaScriptCore/runtime/Executable.cpp
+++ b/Source/JavaScriptCore/runtime/Executable.cpp
@@ -180,9 +180,9 @@
}
PassRefPtr<CodeBlock> ScriptExecutable::newCodeBlockFor(
- CodeSpecializationKind kind, JSScope* scope, JSObject*& exception)
+ CodeSpecializationKind kind, JSFunction* function, JSScope** scope, JSObject*& exception)
{
- VM* vm = scope->vm();
+ VM* vm = (*scope)->vm();
ASSERT(vm->heap.isDeferred());
ASSERT(startColumn() != UINT_MAX);
@@ -192,8 +192,9 @@
EvalExecutable* executable = jsCast<EvalExecutable*>(this);
RELEASE_ASSERT(kind == CodeForCall);
RELEASE_ASSERT(!executable->m_evalCodeBlock);
+ RELEASE_ASSERT(!function);
return adoptRef(new EvalCodeBlock(
- executable, executable->m_unlinkedEvalCodeBlock.get(), scope,
+ executable, executable->m_unlinkedEvalCodeBlock.get(), *scope,
executable->source().provider()));
}
@@ -201,15 +202,17 @@
ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
RELEASE_ASSERT(kind == CodeForCall);
RELEASE_ASSERT(!executable->m_programCodeBlock);
+ RELEASE_ASSERT(!function);
return adoptRef(new ProgramCodeBlock(
- executable, executable->m_unlinkedProgramCodeBlock.get(), scope,
+ executable, executable->m_unlinkedProgramCodeBlock.get(), *scope,
executable->source().provider(), executable->source().startColumn()));
}
RELEASE_ASSERT(classInfo() == FunctionExecutable::info());
+ RELEASE_ASSERT(function);
FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
RELEASE_ASSERT(!executable->codeBlockFor(kind));
- JSGlobalObject* globalObject = scope->globalObject();
+ JSGlobalObject* globalObject = (*scope)->globalObject();
ParserError error;
DebuggerMode debuggerMode = globalObject->hasDebugger() ? DebuggerOn : DebuggerOff;
ProfilerMode profilerMode = globalObject->hasProfiler() ? ProfilerOn : ProfilerOff;
@@ -223,13 +226,21 @@
error.toErrorObject(globalObject, executable->m_source));
return 0;
}
+
+ // Parsing reveals whether our function uses features that require a separate function name object in the scope chain.
+ // Be sure to add this scope before linking the bytecode because this scope will change the resolution depth of non-local variables.
+ if (!executable->m_didParseForTheFirstTime) {
+ executable->m_didParseForTheFirstTime = true;
+ function->addNameScopeIfNeeded(*vm);
+ *scope = function->scope();
+ }
SourceProvider* provider = executable->source().provider();
unsigned sourceOffset = executable->source().startOffset();
unsigned startColumn = executable->source().startColumn();
return adoptRef(new FunctionCodeBlock(
- executable, unlinkedCodeBlock, scope, provider, sourceOffset, startColumn));
+ executable, unlinkedCodeBlock, *scope, provider, sourceOffset, startColumn));
}
PassRefPtr<CodeBlock> ScriptExecutable::newReplacementCodeBlockFor(
@@ -291,13 +302,13 @@
}
JSObject* ScriptExecutable::prepareForExecutionImpl(
- ExecState* exec, JSScope* scope, CodeSpecializationKind kind)
+ ExecState* exec, JSFunction* function, JSScope** scope, CodeSpecializationKind kind)
{
VM& vm = exec->vm();
DeferGC deferGC(vm.heap);
JSObject* exception = 0;
- RefPtr<CodeBlock> codeBlock = newCodeBlockFor(kind, scope, exception);
+ RefPtr<CodeBlock> codeBlock = newCodeBlockFor(kind, function, scope, exception);
if (!codeBlock) {
RELEASE_ASSERT(exception);
return exception;
@@ -376,6 +387,7 @@
: ScriptExecutable(vm.functionExecutableStructure.get(), vm, source, unlinkedExecutable->isInStrictContext())
, m_unlinkedExecutable(vm, this, unlinkedExecutable)
, m_bodyIncludesBraces(bodyIncludesBraces)
+ , m_didParseForTheFirstTime(false)
{
RELEASE_ASSERT(!source.isNull());
ASSERT(source.length());
diff --git a/Source/JavaScriptCore/runtime/Executable.h b/Source/JavaScriptCore/runtime/Executable.h
index 198976b..57e6ae3 100644
--- a/Source/JavaScriptCore/runtime/Executable.h
+++ b/Source/JavaScriptCore/runtime/Executable.h
@@ -419,18 +419,18 @@
}
void installCode(CodeBlock*);
- PassRefPtr<CodeBlock> newCodeBlockFor(CodeSpecializationKind, JSScope*, JSObject*& exception);
+ PassRefPtr<CodeBlock> newCodeBlockFor(CodeSpecializationKind, JSFunction*, JSScope**, JSObject*& exception);
PassRefPtr<CodeBlock> newReplacementCodeBlockFor(CodeSpecializationKind);
- JSObject* prepareForExecution(ExecState* exec, JSScope* scope, CodeSpecializationKind kind)
+ JSObject* prepareForExecution(ExecState* exec, JSFunction* function, JSScope** scope, CodeSpecializationKind kind)
{
if (hasJITCodeFor(kind))
return 0;
- return prepareForExecutionImpl(exec, scope, kind);
+ return prepareForExecutionImpl(exec, function, scope, kind);
}
private:
- JSObject* prepareForExecutionImpl(ExecState*, JSScope*, CodeSpecializationKind);
+ JSObject* prepareForExecutionImpl(ExecState*, JSFunction*, JSScope**, CodeSpecializationKind);
protected:
void finishCreation(VM& vm)
@@ -629,6 +629,7 @@
return baselineCodeBlockFor(kind);
}
+ FunctionMode functionMode() { return m_unlinkedExecutable->functionMode(); }
const Identifier& name() { return m_unlinkedExecutable->name(); }
const Identifier& inferredName() { return m_unlinkedExecutable->inferredName(); }
JSString* nameValue() const { return m_unlinkedExecutable->nameValue(); }
@@ -673,6 +674,7 @@
RefPtr<FunctionCodeBlock> m_codeBlockForCall;
RefPtr<FunctionCodeBlock> m_codeBlockForConstruct;
bool m_bodyIncludesBraces;
+ bool m_didParseForTheFirstTime;
};
inline bool isHostFunction(JSValue value, NativeFunction nativeFunction)
diff --git a/Source/JavaScriptCore/runtime/JSFunction.cpp b/Source/JavaScriptCore/runtime/JSFunction.cpp
index 2419646..990e7c2 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.cpp
+++ b/Source/JavaScriptCore/runtime/JSFunction.cpp
@@ -36,6 +36,7 @@
#include "JSArray.h"
#include "JSBoundFunction.h"
#include "JSGlobalObject.h"
+#include "JSNameScope.h"
#include "JSNotAnObject.h"
#include "Interpreter.h"
#include "ObjectConstructor.h"
@@ -109,6 +110,16 @@
putDirect(vm, vm.propertyNames->length, jsNumber(length), DontDelete | ReadOnly | DontEnum);
}
+void JSFunction::addNameScopeIfNeeded(VM& vm)
+{
+ FunctionExecutable* executable = jsCast<FunctionExecutable*>(m_executable.get());
+ if (!functionNameIsInScope(executable->name(), executable->functionMode()))
+ return;
+ if (!functionNameScopeIsDynamic(executable->usesEval(), executable->isStrictMode()))
+ return;
+ m_scope.set(vm, this, JSNameScope::create(vm, m_scope->globalObject(), executable->name(), this, ReadOnly | DontDelete, m_scope.get()));
+}
+
ObjectAllocationProfile* JSFunction::createAllocationProfile(ExecState* exec, size_t inlineCapacity)
{
VM& vm = exec->vm();
diff --git a/Source/JavaScriptCore/runtime/JSFunction.h b/Source/JavaScriptCore/runtime/JSFunction.h
index 7cd14b8..aa2471e 100644
--- a/Source/JavaScriptCore/runtime/JSFunction.h
+++ b/Source/JavaScriptCore/runtime/JSFunction.h
@@ -93,6 +93,7 @@
ASSERT(!isHostFunctionNonInline());
m_scope.set(vm, this, scope);
}
+ void addNameScopeIfNeeded(VM&);
ExecutableBase* executable() const { return m_executable.get(); }
diff --git a/Source/JavaScriptCore/runtime/JSNameScope.h b/Source/JavaScriptCore/runtime/JSNameScope.h
index f5b15a2..a6b6a22 100644
--- a/Source/JavaScriptCore/runtime/JSNameScope.h
+++ b/Source/JavaScriptCore/runtime/JSNameScope.h
@@ -39,15 +39,14 @@
static JSNameScope* create(ExecState* exec, const Identifier& identifier, JSValue value, unsigned attributes)
{
VM& vm = exec->vm();
- JSNameScope* scopeObject = new (NotNull, allocateCell<JSNameScope>(vm.heap)) JSNameScope(exec, exec->scope());
+ JSNameScope* scopeObject = new (NotNull, allocateCell<JSNameScope>(vm.heap)) JSNameScope(vm, exec->lexicalGlobalObject(), exec->scope());
scopeObject->finishCreation(vm, identifier, value, attributes);
return scopeObject;
}
- static JSNameScope* create(ExecState* exec, const Identifier& identifier, JSValue value, unsigned attributes, JSScope* next)
+ static JSNameScope* create(VM& vm, JSGlobalObject* globalObject, const Identifier& identifier, JSValue value, unsigned attributes, JSScope* next)
{
- VM& vm = exec->vm();
- JSNameScope* scopeObject = new (NotNull, allocateCell<JSNameScope>(vm.heap)) JSNameScope(exec, next);
+ JSNameScope* scopeObject = new (NotNull, allocateCell<JSNameScope>(vm.heap)) JSNameScope(vm, globalObject, next);
scopeObject->finishCreation(vm, identifier, value, attributes);
return scopeObject;
}
@@ -72,10 +71,10 @@
static const unsigned StructureFlags = OverridesGetOwnPropertySlot | OverridesVisitChildren | Base::StructureFlags;
private:
- JSNameScope(ExecState* exec, JSScope* next)
+ JSNameScope(VM& vm, JSGlobalObject* globalObject, JSScope* next)
: Base(
- exec->vm(),
- exec->lexicalGlobalObject()->nameScopeStructure(),
+ vm,
+ globalObject->nameScopeStructure(),
reinterpret_cast<Register*>(&m_registerStore + 1),
next
)