Ignore:
Timestamp:
Sep 5, 2012, 11:17:59 PM (13 years ago)
Author:
[email protected]
Message:

Named functions should not allocate scope objects for their names
https://bugs.webkit.org/show_bug.cgi?id=95659

Reviewed by Oliver Hunt.

Source/JavaScriptCore:

In most cases, we can merge a function expression's name into its symbol
table. This reduces memory footprint per closure from three objects
(function + activation + name scope) to two (function + activation),
speeds up closure allocation, and speeds up recursive calls.

In the case of a named function expression that contains a non-strict
eval, the rules are so bat-poop crazy that I don't know how to model
them without an extra object. Since functions now default to not having
such an object, this case needs to allocate the object on function
entry.

Therefore, this patch makes the slow case a bit slower so the fast case
can be faster and more memory-efficient. (Note that the slow case already
allocates an activation on entry, and until recently also allocated a
scope chain node on entry, so adding one allocation on entry shouldn't
break the bank.)

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::CodeBlock): Caught a missed initializer. No behavior change.

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::BytecodeGenerator): Put the callee in static scope
during compilation so it doesn't need to be in dynamic scope at runtime.

(JSC::BytecodeGenerator::resolveCallee):
(JSC::BytecodeGenerator::addCallee): Helper functions for either statically
resolving the callee or adding a dynamic scope that will resolve to it,
depending on whether you're in the fast path.

We move the callee into a var location if it's captured because activations
prefer to have contiguous ranges of captured variables.

  • bytecompiler/BytecodeGenerator.h:

(JSC::BytecodeGenerator::registerFor):
(BytecodeGenerator):

  • dfg/DFGOperations.cpp:
  • interpreter/Interpreter.cpp:

(JSC::Interpreter::privateExecute):

  • jit/JITStubs.cpp:

(JSC::DEFINE_STUB_FUNCTION):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL): This is the point of the patch: remove
one allocation in the case of a named function expression.

  • parser/Parser.cpp:

(JSC::::Parser):

  • parser/Parser.h:

(JSC::Scope::declareCallee):
(Scope):
(Parser):
(JSC::parse):

  • runtime/Executable.cpp:

(JSC::EvalExecutable::compileInternal):
(JSC::ProgramExecutable::checkSyntax):
(JSC::ProgramExecutable::compileInternal):
(JSC::FunctionExecutable::produceCodeBlockFor):
(JSC::FunctionExecutable::fromGlobalCode): Pipe the callee's name through
the parser so we get accurate information on whether the callee was captured.

(JSC::FunctionExecutable::FunctionExecutable):
(JSC::EvalExecutable::compileInternal):
(JSC::ProgramExecutable::checkSyntax):
(JSC::ProgramExecutable::compileInternal):
(JSC::FunctionExecutable::produceCodeBlockFor):
(JSC::FunctionExecutable::fromGlobalCode):

  • runtime/Executable.h:

(JSC::FunctionExecutable::create):
(FunctionExecutable):
(JSC::FunctionExecutable::finishCreation): I had to refactor function
creation to support the following function constructor quirk: the function
gets a name, but its name is not in lexical scope.

To simplify this, FunctionExecutable now automatically extracts all the
data it needs from the parsed node. The special "fromGlobalCode" path
used by the function constructor creates an anonymous function, and then
quirkily sets the value used by the .name property to be non-null, even
though the parsed name is null.

  • runtime/JSNameScope.h:

(JSC::JSNameScope::create):
(JSC::JSNameScope::JSNameScope): Added support for explicitly specifying
your container scope. The compiler uses this for named function expressions.

LayoutTests:

Added coverage for some extra-tricky cases.

  • fast/js/named-function-expression-expected.txt:
  • fast/js/script-tests/named-function-expression.js:

(shouldBeTrueWithDescription): I rolled my own shouldBeTrue() here to avoid the
built-in shouldBe()'s eval scoping, which can change the variable
resolution rules I'm trying to test.

  • inspector/debugger/debugger-expand-scope-expected.txt: Not sure why this

result used to miss the function name scope, but the new result is a
progression, so I've updated the expected results.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/Executable.cpp

    r127505 r127698  
    134134const ClassInfo FunctionExecutable::s_info = { "FunctionExecutable", &ScriptExecutable::s_info, 0, 0, CREATE_METHOD_TABLE(FunctionExecutable) };
    135135
    136 FunctionExecutable::FunctionExecutable(JSGlobalData& globalData, const Identifier& name, const Identifier& inferredName, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext)
    137     : ScriptExecutable(globalData.functionExecutableStructure.get(), globalData, source, inStrictContext)
     136FunctionExecutable::FunctionExecutable(JSGlobalData& globalData, FunctionBodyNode* node)
     137    : ScriptExecutable(globalData.functionExecutableStructure.get(), globalData, node->source(), node->isStrictMode())
    138138    , m_numCapturedVariables(0)
    139     , m_forceUsesArguments(forceUsesArguments)
    140     , m_parameters(parameters)
    141     , m_name(name)
    142     , m_inferredName(inferredName.isNull() ? globalData.propertyNames->emptyIdentifier : inferredName)
    143 {
    144 }
    145 
    146 FunctionExecutable::FunctionExecutable(ExecState* exec, const Identifier& name, const Identifier& inferredName, const SourceCode& source, bool forceUsesArguments, FunctionParameters* parameters, bool inStrictContext)
    147     : ScriptExecutable(exec->globalData().functionExecutableStructure.get(), exec, source, inStrictContext)
    148     , m_numCapturedVariables(0)
    149     , m_forceUsesArguments(forceUsesArguments)
    150     , m_parameters(parameters)
    151     , m_name(name)
    152     , m_inferredName(inferredName.isNull() ? exec->globalData().propertyNames->emptyIdentifier : inferredName)
    153 {
     139    , m_forceUsesArguments(node->usesArguments())
     140    , m_parameters(node->parameters())
     141    , m_name(node->ident())
     142    , m_inferredName(node->inferredName().isNull() ? globalData.propertyNames->emptyIdentifier : node->inferredName())
     143{
     144    m_firstLine = node->lineNo();
     145    m_lastLine = node->lastLine();
    154146}
    155147
     
    211203        if (!lexicalGlobalObject->evalEnabled())
    212204            return throwError(exec, createEvalError(exec, ASCIILiteral("Eval is disabled")));
    213         RefPtr<EvalNode> evalNode = parse<EvalNode>(globalData, lexicalGlobalObject, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, EvalNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception);
     205        RefPtr<EvalNode> evalNode = parse<EvalNode>(globalData, lexicalGlobalObject, m_source, 0, Identifier(), isStrictMode() ? JSParseStrict : JSParseNormal, EvalNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception);
    214206        if (!evalNode) {
    215207            ASSERT(exception);
     
    294286    JSGlobalData* globalData = &exec->globalData();
    295287    JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
    296     RefPtr<ProgramNode> programNode = parse<ProgramNode>(globalData, lexicalGlobalObject, m_source, 0, JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception);
     288    RefPtr<ProgramNode> programNode = parse<ProgramNode>(globalData, lexicalGlobalObject, m_source, 0, Identifier(), JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception);
    297289    if (programNode)
    298290        return 0;
     
    336328        m_programCodeBlock = newCodeBlock.release();
    337329    } else {
    338         RefPtr<ProgramNode> programNode = parse<ProgramNode>(globalData, lexicalGlobalObject, m_source, 0, isStrictMode() ? JSParseStrict : JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception);
     330        RefPtr<ProgramNode> programNode = parse<ProgramNode>(globalData, lexicalGlobalObject, m_source, 0, Identifier(), isStrictMode() ? JSParseStrict : JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, lexicalGlobalObject->debugger(), exec, &exception);
    339331        if (!programNode) {
    340332            ASSERT(exception);
     
    479471    JSGlobalData* globalData = scope->globalData();
    480472    JSGlobalObject* globalObject = scope->globalObject();
    481     RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(globalData, globalObject, m_source, m_parameters.get(), isStrictMode() ? JSParseStrict : JSParseNormal, FunctionBodyNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, 0, 0, &exception);
     473    RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(
     474        globalData,
     475        globalObject,
     476        m_source,
     477        m_parameters.get(),
     478        name(),
     479        isStrictMode() ? JSParseStrict : JSParseNormal,
     480        FunctionBodyNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode,
     481        0,
     482        0,
     483        &exception
     484    );
    482485
    483486    if (!body) {
     
    648651}
    649652
    650 FunctionExecutable* FunctionExecutable::fromGlobalCode(const Identifier& functionName, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception)
     653FunctionExecutable* FunctionExecutable::fromGlobalCode(const Identifier& name, ExecState* exec, Debugger* debugger, const SourceCode& source, JSObject** exception)
    651654{
    652655    JSGlobalObject* lexicalGlobalObject = exec->lexicalGlobalObject();
    653     RefPtr<ProgramNode> program = parse<ProgramNode>(&exec->globalData(), lexicalGlobalObject, source, 0, JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, debugger, exec, exception);
     656    RefPtr<ProgramNode> program = parse<ProgramNode>(&exec->globalData(), lexicalGlobalObject, source, 0, Identifier(), JSParseNormal, ProgramNode::isFunctionNode ? JSParseFunctionCode : JSParseProgramCode, debugger, exec, exception);
    654657    if (!program) {
    655658        ASSERT(*exception);
     
    657660    }
    658661
    659     // Uses of this function that would not result in a single function expression are invalid.
     662    // This function assumes an input string that would result in a single anonymous function expression.
    660663    StatementNode* exprStatement = program->singleStatement();
    661664    ASSERT(exprStatement);
     
    666669    FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body();
    667670    ASSERT(body);
    668 
    669     return FunctionExecutable::create(exec->globalData(), functionName, functionName, body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine());
     671    ASSERT(body->ident().isNull());
     672
     673    FunctionExecutable* functionExecutable = FunctionExecutable::create(exec->globalData(), body);
     674    functionExecutable->m_nameValue.set(exec->globalData(), functionExecutable, jsString(&exec->globalData(), name.ustring()));
     675    return functionExecutable;
    670676}
    671677
Note: See TracChangeset for help on using the changeset viewer.