diff options
Diffstat (limited to 'Source/JavaScriptCore/parser/JSParser.cpp')
-rw-r--r-- | Source/JavaScriptCore/parser/JSParser.cpp | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/Source/JavaScriptCore/parser/JSParser.cpp b/Source/JavaScriptCore/parser/JSParser.cpp index 37b7f90..993dd66 100644 --- a/Source/JavaScriptCore/parser/JSParser.cpp +++ b/Source/JavaScriptCore/parser/JSParser.cpp @@ -33,6 +33,8 @@ using namespace JSC; #include "JSGlobalData.h" #include "NodeInfo.h" #include "ASTBuilder.h" +#include "SourceProvider.h" +#include "SourceProviderCacheItem.h" #include <wtf/HashFunctions.h> #include <wtf/WTFThreadData.h> #include <utility> @@ -94,7 +96,7 @@ private: StringImpl* m_ident; bool m_isLoop; }; - + void next(Lexer::LexType lexType = Lexer::IdentifyReservedWords) { m_lastLine = m_token.m_info.line; @@ -417,6 +419,37 @@ private: bool strictMode() const { return m_strictMode; } bool isValidStrictMode() const { return m_isValidStrictMode; } bool shadowsArguments() const { return m_shadowsArguments; } + + void copyCapturedVariablesToVector(const IdentifierSet& capturedVariables, Vector<RefPtr<StringImpl> >& vector) + { + IdentifierSet::iterator end = capturedVariables.end(); + for (IdentifierSet::iterator it = capturedVariables.begin(); it != end; ++it) { + if (m_declaredVariables.contains(*it)) + continue; + vector.append(*it); + } + vector.shrinkToFit(); + } + + void saveFunctionInfo(SourceProviderCacheItem* info) + { + ASSERT(m_isFunction); + info->usesEval = m_usesEval; + copyCapturedVariablesToVector(m_writtenVariables, info->writtenVariables); + copyCapturedVariablesToVector(m_usedVariables, info->usedVariables); + } + + void restoreFunctionInfo(const SourceProviderCacheItem* info) + { + ASSERT(m_isFunction); + m_usesEval = info->usesEval; + unsigned size = info->usedVariables.size(); + for (unsigned i = 0; i < size; ++i) + m_usedVariables.add(info->usedVariables[i]); + size = info->writtenVariables.size(); + for (unsigned i = 0; i < size; ++i) + m_writtenVariables.add(info->writtenVariables[i]); + } private: JSGlobalData* m_globalData; @@ -543,6 +576,13 @@ private: } ScopeStack m_scopeStack; + + const SourceProviderCacheItem* findCachedFunctionInfo(int openBracePos) + { + return m_functionCache ? m_functionCache->get(openBracePos) : 0; + } + + SourceProviderCache* m_functionCache; }; const char* jsParse(JSGlobalData* globalData, FunctionParameters* parameters, JSParserStrictness strictness, JSParserMode parserMode, const SourceCode* source) @@ -566,6 +606,7 @@ JSParser::JSParser(Lexer* lexer, JSGlobalData* globalData, FunctionParameters* p , m_statementDepth(0) , m_nonTrivialExpressionCount(0) , m_lastIdentifier(0) + , m_functionCache(m_lexer->sourceProvider()->cache()) { ScopeRef scope = pushScope(); if (isFunction) @@ -582,6 +623,7 @@ JSParser::JSParser(Lexer* lexer, JSGlobalData* globalData, FunctionParameters* p const char* JSParser::parseProgram() { + unsigned oldFunctionCacheSize = m_functionCache ? m_functionCache->byteSize() : 0; ASTBuilder context(m_globalData, m_lexer); if (m_lexer->isReparsing()) m_statementDepth--; @@ -596,6 +638,10 @@ const char* JSParser::parseProgram() features |= StrictModeFeature; if (scope->shadowsArguments()) features |= ShadowsArgumentsFeature; + + unsigned functionCacheSize = m_functionCache ? m_functionCache->byteSize() : 0; + if (functionCacheSize != oldFunctionCacheSize) + m_lexer->sourceProvider()->notifyCacheSizeChanged(functionCacheSize - oldFunctionCacheSize); m_globalData->parser->didFinishParsing(sourceElements, context.varDeclarations(), context.funcDeclarations(), features, m_lastLine, context.numConstants(), capturedVariables); @@ -1223,6 +1269,7 @@ template <JSParser::FunctionRequirements requirements, bool nameIsInContainingSc functionScope->setIsFunction(); if (match(IDENT)) { name = m_token.m_data.ident; + failIfTrue(*name == m_globalData->propertyNames->underscoreProto); next(); if (!nameIsInContainingScope) failIfFalseIfStrict(functionScope->declareVariable(name)); @@ -1238,6 +1285,23 @@ template <JSParser::FunctionRequirements requirements, bool nameIsInContainingSc openBracePos = m_token.m_data.intValue; bodyStartLine = tokenLine(); + + if (const SourceProviderCacheItem* cachedInfo = TreeBuilder::CanUseFunctionCache ? findCachedFunctionInfo(openBracePos) : 0) { + // If we know about this function already, we can use the cached info and skip the parser to the end of the function. + body = context.createFunctionBody(strictMode()); + + functionScope->restoreFunctionInfo(cachedInfo); + failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); + + closeBracePos = cachedInfo->closeBracePos; + m_token = cachedInfo->closeBraceToken(); + m_lexer->setOffset(m_token.m_info.endOffset); + m_lexer->setLineNumber(m_token.m_info.line); + + next(); + return true; + } + next(); body = parseFunctionBody(context); @@ -1246,9 +1310,26 @@ template <JSParser::FunctionRequirements requirements, bool nameIsInContainingSc failIfTrue(m_globalData->propertyNames->arguments == *name); failIfTrue(m_globalData->propertyNames->eval == *name); } + closeBracePos = m_token.m_data.intValue; + + // Cache the tokenizer state and the function scope the first time the function is parsed. + // Any future reparsing can then skip the function. + static const int minimumFunctionLengthToCache = 64; + OwnPtr<SourceProviderCacheItem> newInfo; + int functionLength = closeBracePos - openBracePos; + if (TreeBuilder::CanUseFunctionCache && m_functionCache && functionLength > minimumFunctionLengthToCache) { + newInfo = adoptPtr(new SourceProviderCacheItem(m_token.m_info.line, closeBracePos)); + functionScope->saveFunctionInfo(newInfo.get()); + } + failIfFalse(popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo)); matchOrFail(CLOSEBRACE); - closeBracePos = m_token.m_data.intValue; + + if (newInfo) { + unsigned approximateByteSize = newInfo->approximateByteSize(); + m_functionCache->add(openBracePos, newInfo.release(), approximateByteSize); + } + next(); return true; } |