Title: [149836] trunk
Revision
149836
Author
[email protected]
Date
2013-05-09 13:03:23 -0700 (Thu, 09 May 2013)

Log Message

Code cache stores bogus var references for functions in eval code
https://bugs.webkit.org/show_bug.cgi?id=115747

Reviewed by Mark Hahnenberg.

Source/_javascript_Core:

Non-global eval now uses a per-CodeBlock cache, and only use it
when we're at the top of a function's scope.  This means that we
will no longer cache the parsing of a single string across
multiple functions, and we won't cache when we're nested inside
constructs like |with| and |catch| where previously we would, which
is good because caching in those cases is unsound.

* bytecode/EvalCodeCache.h:
(JSC):
(JSC::EvalCodeCache::getSlow):
(JSC::EvalCodeCache::get):
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::codeCacheForEval):
(UnlinkedCodeBlock):
(RareData):
* debugger/Debugger.cpp:
(JSC::evaluateInGlobalCallFrame):
* debugger/DebuggerCallFrame.cpp:
(JSC::DebuggerCallFrame::evaluate):
* interpreter/Interpreter.cpp:
(JSC::eval):
* runtime/CodeCache.cpp:
(JSC::CodeCache::CodeCache):
(JSC::CodeCache::generateBytecode):
(JSC):
(JSC::CodeCache::getCodeBlock):
* runtime/CodeCache.h:
(JSC::CodeCacheMap::CodeCacheMap):
(CodeCacheMap):
(JSC::CodeCacheMap::canPruneQuickly):
(JSC::CodeCacheMap::prune):
(JSC::CodeCache::create):
(CodeCache):
* runtime/Executable.cpp:
(JSC::EvalExecutable::EvalExecutable):
(JSC::EvalExecutable::compileInternal):
* runtime/Executable.h:
(JSC::EvalExecutable::create):
(EvalExecutable):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::createEvalCodeBlock):
* runtime/JSGlobalObject.h:
(JSGlobalObject):
* runtime/JSGlobalObjectFunctions.cpp:
(JSC::globalFuncEval):
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
(VM):

LayoutTests:

Many tests

* fast/js/eval-cache-scoped-lookup-expected.txt: Added.
* fast/js/eval-cache-scoped-lookup.html: Added.
* fast/js/script-tests/eval-cache-scoped-lookup.js: Added.
(first):
(a.string_appeared_here.second):
(third):
(fifth):
(sixth):
(seventh):
(eighth):
(nineth):
(tenth):
(eleventh):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (149835 => 149836)


--- trunk/LayoutTests/ChangeLog	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/LayoutTests/ChangeLog	2013-05-09 20:03:23 UTC (rev 149836)
@@ -1,3 +1,26 @@
+2013-05-08  Oliver Hunt  <[email protected]>
+
+        Code cache stores bogus var references for functions in eval code
+        https://bugs.webkit.org/show_bug.cgi?id=115747
+
+        Reviewed by Mark Hahnenberg.
+
+        Many tests
+
+        * fast/js/eval-cache-scoped-lookup-expected.txt: Added.
+        * fast/js/eval-cache-scoped-lookup.html: Added.
+        * fast/js/script-tests/eval-cache-scoped-lookup.js: Added.
+        (first):
+        (a.string_appeared_here.second):
+        (third):
+        (fifth):
+        (sixth):
+        (seventh):
+        (eighth):
+        (nineth):
+        (tenth):
+        (eleventh):
+
 2013-05-08  Mark Hahnenberg  <[email protected]>
 
         DFGArrayMode::fromObserved is too liberal when it sees different Array and NonArray shapes

Added: trunk/LayoutTests/fast/js/eval-cache-scoped-lookup-expected.txt (0 => 149836)


--- trunk/LayoutTests/fast/js/eval-cache-scoped-lookup-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/eval-cache-scoped-lookup-expected.txt	2013-05-09 20:03:23 UTC (rev 149836)
@@ -0,0 +1,20 @@
+Test to make sure that scoped variable access through an eval produces the correct results
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS first() is 'first'
+PASS second() is 'second'
+PASS third() is 'third'
+PASS fourth() is 'fourth'
+PASS fifth() is 'fifth'
+PASS sixth() is 'sixth'
+PASS seventh() is 'seventh'
+PASS eighth() is 'eighth'
+PASS nineth() is 'nineth'
+PASS tenth() is 'tenth'
+PASS eleventh() is 'eleventh'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/eval-cache-scoped-lookup.html (0 => 149836)


--- trunk/LayoutTests/fast/js/eval-cache-scoped-lookup.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/eval-cache-scoped-lookup.html	2013-05-09 20:03:23 UTC (rev 149836)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/js/script-tests/eval-cache-scoped-lookup.js (0 => 149836)


--- trunk/LayoutTests/fast/js/script-tests/eval-cache-scoped-lookup.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/eval-cache-scoped-lookup.js	2013-05-09 20:03:23 UTC (rev 149836)
@@ -0,0 +1,109 @@
+description(
+"Test to make sure that scoped variable access through an eval produces the correct results"
+);
+
+
+
+var str = "(function () { return a; })";
+
+function first() {
+    var a = "first"
+    return eval(str)();
+}
+
+var a = "second"
+function second() {
+    var b = "error"
+    return eval(str)();
+}
+
+function third() {
+    var a = "third"
+	return (function () {
+		var b = "error";
+	    return eval(str)();
+	})()
+}
+
+function fourth() {
+    eval('var a = "fourth"');
+    return eval(str)();
+}
+
+function fifth() {
+	"use strict";
+	var a = "fifth"
+	return (function() {
+	    eval('var a = "error"');
+	    return eval(str)();
+	})();
+}
+
+function sixth() {
+    var a = "sixth"
+    try {
+	    return eval(str)();
+	} catch (e) {
+	}
+}
+
+function seventh() {
+    try {
+    	throw "seventh"
+    } catch (a) {
+    	return eval(str)();
+    }
+}
+
+function eighth() {
+	var a = "eighth"
+    try {
+    	throw "error"
+    } catch (b) {
+    	return eval(str)();
+    }
+}
+
+function nineth() {
+	var a = "nineth"
+	eval(str)();
+    try {
+    	throw "error"
+    } catch (b) {
+    	return eval(str)();
+    }
+}
+
+function tenth() {
+	var a = "error"
+	eval(str)();
+    try {
+    	throw "tenth"
+    } catch (a) {
+    	return eval(str)();
+    }
+}
+
+function eleventh() {
+	var a = "eleventh"
+    try {
+    	throw "error"
+    } catch (a) {
+    	eval(str)();
+    }
+	return eval(str)();
+}
+
+
+shouldBe("first()", "'first'");
+shouldBe("second()", "'second'");
+shouldBe("third()", "'third'");
+shouldBe("fourth()", "'fourth'");
+shouldBe("fifth()", "'fifth'");
+shouldBe("sixth()", "'sixth'");
+shouldBe("seventh()", "'seventh'");
+shouldBe("eighth()", "'eighth'");
+shouldBe("nineth()", "'nineth'");
+shouldBe("tenth()", "'tenth'");
+shouldBe("eleventh()", "'eleventh'");
+

Modified: trunk/Source/_javascript_Core/ChangeLog (149835 => 149836)


--- trunk/Source/_javascript_Core/ChangeLog	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-05-09 20:03:23 UTC (rev 149836)
@@ -1,3 +1,60 @@
+2013-05-08  Oliver Hunt  <[email protected]>
+
+        Code cache stores bogus var references for functions in eval code
+        https://bugs.webkit.org/show_bug.cgi?id=115747
+
+        Reviewed by Mark Hahnenberg.
+
+        Non-global eval now uses a per-CodeBlock cache, and only use it
+        when we're at the top of a function's scope.  This means that we
+        will no longer cache the parsing of a single string across
+        multiple functions, and we won't cache when we're nested inside
+        constructs like |with| and |catch| where previously we would, which
+        is good because caching in those cases is unsound.
+
+        * bytecode/EvalCodeCache.h:
+        (JSC):
+        (JSC::EvalCodeCache::getSlow):
+        (JSC::EvalCodeCache::get):
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::codeCacheForEval):
+        (UnlinkedCodeBlock):
+        (RareData):
+        * debugger/Debugger.cpp:
+        (JSC::evaluateInGlobalCallFrame):
+        * debugger/DebuggerCallFrame.cpp:
+        (JSC::DebuggerCallFrame::evaluate):
+        * interpreter/Interpreter.cpp:
+        (JSC::eval):
+        * runtime/CodeCache.cpp:
+        (JSC::CodeCache::CodeCache):
+        (JSC::CodeCache::generateBytecode):
+        (JSC):
+        (JSC::CodeCache::getCodeBlock):
+        * runtime/CodeCache.h:
+        (JSC::CodeCacheMap::CodeCacheMap):
+        (CodeCacheMap):
+        (JSC::CodeCacheMap::canPruneQuickly):
+        (JSC::CodeCacheMap::prune):
+        (JSC::CodeCache::create):
+        (CodeCache):
+        * runtime/Executable.cpp:
+        (JSC::EvalExecutable::EvalExecutable):
+        (JSC::EvalExecutable::compileInternal):
+        * runtime/Executable.h:
+        (JSC::EvalExecutable::create):
+        (EvalExecutable):
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::createEvalCodeBlock):
+        * runtime/JSGlobalObject.h:
+        (JSGlobalObject):
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::globalFuncEval):
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        (VM):
+
 2013-05-08  Mark Hahnenberg  <[email protected]>
 
         DFGArrayMode::fromObserved is too liberal when it sees different Array and NonArray shapes

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (149835 => 149836)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2013-05-09 20:03:23 UTC (rev 149836)
@@ -874,7 +874,7 @@
 		FE4A332015BD2E07006F54F3 /* VMInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4A331E15BD2E07006F54F3 /* VMInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = FED287B115EC9A5700DA8161 /* LLIntOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FED94F2E171E3E2300BE77A4 /* Watchdog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */; };
-		FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); };};
+		FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		FED94F30171E3E2300BE77A4 /* WatchdogMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */; };
 /* End PBXBuildFile section */
 

Modified: trunk/Source/_javascript_Core/bytecode/EvalCodeCache.h (149835 => 149836)


--- trunk/Source/_javascript_Core/bytecode/EvalCodeCache.h	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/bytecode/EvalCodeCache.h	2013-05-09 20:03:23 UTC (rev 149836)
@@ -38,6 +38,7 @@
 
 namespace JSC {
 
+    class CodeCache;
     class SlotVisitor;
 
     class EvalCodeCache {
@@ -49,9 +50,9 @@
             return 0;
         }
         
-        EvalExecutable* getSlow(ExecState* exec, ScriptExecutable* owner, bool inStrictContext, const String& evalSource, JSScope* scope, JSValue& exceptionValue)
+        EvalExecutable* getSlow(ExecState* exec, CodeCache* codeCache, ScriptExecutable* owner, bool inStrictContext, const String& evalSource, JSScope* scope, JSValue& exceptionValue)
         {
-            EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext);
+            EvalExecutable* evalExecutable = EvalExecutable::create(exec, codeCache, makeSource(evalSource), inStrictContext);
             exceptionValue = evalExecutable->compile(exec, scope);
             if (exceptionValue)
                 return 0;
@@ -62,12 +63,12 @@
             return evalExecutable;
         }
 
-        EvalExecutable* get(ExecState* exec, ScriptExecutable* owner, bool inStrictContext, const String& evalSource, JSScope* scope, JSValue& exceptionValue)
+        EvalExecutable* get(ExecState* exec, CodeCache* codeCache, ScriptExecutable* owner, bool inStrictContext, const String& evalSource, JSScope* scope, JSValue& exceptionValue)
         {
             EvalExecutable* evalExecutable = tryGet(inStrictContext, evalSource, scope);
 
             if (!evalExecutable)
-                evalExecutable = getSlow(exec, owner, inStrictContext, evalSource, scope, exceptionValue);
+                evalExecutable = getSlow(exec, codeCache, owner, inStrictContext, evalSource, scope, exceptionValue);
 
             return evalExecutable;
         }

Modified: trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.h (149835 => 149836)


--- trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.h	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/bytecode/UnlinkedCodeBlock.h	2013-05-09 20:03:23 UTC (rev 149836)
@@ -27,6 +27,7 @@
 #define UnlinkedCodeBlock_h
 
 #include "BytecodeConventions.h"
+#include "CodeCache.h"
 #include "CodeSpecializationKind.h"
 #include "CodeType.h"
 #include "ExpressionRangeInfo.h"
@@ -470,6 +471,16 @@
     unsigned firstLine() const { return m_firstLine; }
     unsigned lineCount() const { return m_lineCount; }
 
+    PassRefPtr<CodeCache> codeCacheForEval()
+    {
+        if (m_codeType == GlobalCode)
+            return m_vm->codeCache();
+        createRareDataIfNecessary();
+        if (!m_rareData->m_evalCodeCache)
+            m_rareData->m_evalCodeCache = CodeCache::create(CodeCache::NonGlobalCodeCache);
+        return m_rareData->m_evalCodeCache.get();
+    }
+
 protected:
     UnlinkedCodeBlock(VM*, Structure*, CodeType, const ExecutableInfo&);
     ~UnlinkedCodeBlock();
@@ -556,8 +567,7 @@
         Vector<UnlinkedSimpleJumpTable> m_immediateSwitchJumpTables;
         Vector<UnlinkedSimpleJumpTable> m_characterSwitchJumpTables;
         Vector<UnlinkedStringJumpTable> m_stringSwitchJumpTables;
-
-        // _expression_ info - present if debugging.
+        RefPtr<CodeCache> m_evalCodeCache;
     };
 
 private:

Modified: trunk/Source/_javascript_Core/debugger/Debugger.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/debugger/Debugger.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/debugger/Debugger.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -128,7 +128,7 @@
     CallFrame* globalCallFrame = globalObject->globalExec();
     VM& vm = globalObject->vm();
 
-    EvalExecutable* eval = EvalExecutable::create(globalCallFrame, makeSource(script), false);
+    EvalExecutable* eval = EvalExecutable::create(globalCallFrame, vm.codeCache(), makeSource(script), false);
     if (!eval) {
         exception = vm.exception;
         vm.exception = JSValue();

Modified: trunk/Source/_javascript_Core/debugger/DebuggerCallFrame.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/debugger/DebuggerCallFrame.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/debugger/DebuggerCallFrame.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -91,7 +91,7 @@
         return JSValue();
     
     VM& vm = m_callFrame->vm();
-    EvalExecutable* eval = EvalExecutable::create(m_callFrame, makeSource(script), m_callFrame->codeBlock()->isStrictMode());
+    EvalExecutable* eval = EvalExecutable::create(m_callFrame, m_callFrame->codeBlock()->unlinkedCodeBlock()->codeCacheForEval(), makeSource(script), m_callFrame->codeBlock()->isStrictMode());
     if (vm.exception) {
         exception = vm.exception;
         vm.exception = JSValue();

Modified: trunk/Source/_javascript_Core/interpreter/Interpreter.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/interpreter/Interpreter.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -185,7 +185,7 @@
         ASSERT(!callFrame->vm().exception);
 
         JSValue exceptionValue;
-        eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain, exceptionValue);
+        eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->unlinkedCodeBlock()->codeCacheForEval().get(), callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain, exceptionValue);
         
         ASSERT(!eval == exceptionValue);
         if (UNLIKELY(!eval))

Modified: trunk/Source/_javascript_Core/runtime/CodeCache.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/CodeCache.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -54,7 +54,9 @@
     }
 }
 
-CodeCache::CodeCache()
+CodeCache::CodeCache(CodeCacheKind kind)
+: m_sourceCode(kind == GlobalCodeCache ? CodeCacheMap::globalWorkingSetMaxBytes : CodeCacheMap::nonGlobalWorkingSetMaxBytes,
+    kind == GlobalCodeCache ? CodeCacheMap::globalWorkingSetMaxEntries : CodeCacheMap::nonGlobalWorkingSetMaxEntries)
 {
 }
 
@@ -75,24 +77,12 @@
 };
 
 template <class UnlinkedCodeBlockType, class ExecutableType>
-UnlinkedCodeBlockType* CodeCache::getCodeBlock(VM& vm, JSScope* scope, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
+UnlinkedCodeBlockType* CodeCache::generateBytecode(VM& vm, JSScope* scope, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
 {
-    SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictness);
-    CodeCacheMap::AddResult addResult = m_sourceCode.add(key, SourceCodeValue());
-    bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff;
-    if (!addResult.isNewEntry && canCache) {
-        UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(addResult.iterator->value.cell.get());
-        unsigned firstLine = source.firstLine() + unlinkedCode->firstLine();
-        executable->recordParse(unlinkedCode->codeFeatures(), unlinkedCode->hasCapturedVariables(), firstLine, firstLine + unlinkedCode->lineCount());
-        return unlinkedCode;
-    }
-
     typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
     RefPtr<RootNode> rootNode = parse<RootNode>(&vm, source, 0, Identifier(), strictness, JSParseProgramCode, error);
-    if (!rootNode) {
-        m_sourceCode.remove(addResult.iterator);
+    if (!rootNode)
         return 0;
-    }
     executable->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->lineNo(), rootNode->lastLine());
 
     UnlinkedCodeBlockType* unlinkedCode = UnlinkedCodeBlockType::create(&vm, executable->executableInfo());
@@ -100,12 +90,33 @@
     OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(vm, scope, rootNode.get(), unlinkedCode, debuggerMode, profilerMode)));
     error = generator->generate();
     rootNode->destroyData();
-    if (error.m_type != ParserError::ErrorNone) {
-        m_sourceCode.remove(addResult.iterator);
+    if (error.m_type != ParserError::ErrorNone)
         return 0;
+    return unlinkedCode;
+}
+
+template <class UnlinkedCodeBlockType, class ExecutableType>
+UnlinkedCodeBlockType* CodeCache::getCodeBlock(VM& vm, JSScope* scope, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
+{
+    // We completely skip the cache if we're an eval that isn't at the top of the scope chain.
+    if (CacheTypes<UnlinkedCodeBlockType>::codeType == SourceCodeKey::EvalType) {
+        if (scope->next() && !scope->isActivationObject())
+            return generateBytecode<UnlinkedCodeBlockType, ExecutableType>(vm, scope, executable, source, strictness, debuggerMode, profilerMode, error);
     }
 
-    if (!canCache) {
+    SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictness);
+    CodeCacheMap::AddResult addResult = m_sourceCode.add(key, SourceCodeValue());
+    bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff;
+
+    if (!addResult.isNewEntry && canCache) {
+        UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(addResult.iterator->value.cell.get());
+        unsigned firstLine = source.firstLine() + unlinkedCode->firstLine();
+        executable->recordParse(unlinkedCode->codeFeatures(), unlinkedCode->hasCapturedVariables(), firstLine, firstLine + unlinkedCode->lineCount());
+        return unlinkedCode;
+    }
+    UnlinkedCodeBlockType* unlinkedCode = generateBytecode<UnlinkedCodeBlockType, ExecutableType>(vm, scope, executable, source, strictness, debuggerMode, profilerMode, error);
+
+    if (!canCache || !unlinkedCode) {
         m_sourceCode.remove(addResult.iterator);
         return unlinkedCode;
     }

Modified: trunk/Source/_javascript_Core/runtime/CodeCache.h (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/CodeCache.h	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/CodeCache.h	2013-05-09 20:03:23 UTC (rev 149836)
@@ -136,13 +136,15 @@
     typedef MapType::iterator iterator;
     typedef MapType::AddResult AddResult;
 
-    CodeCacheMap()
+    CodeCacheMap(int64_t workingSetMaxBytes, size_t workingSetMaxEntries)
         : m_size(0)
         , m_sizeAtLastPrune(0)
         , m_timeAtLastPrune(monotonicallyIncreasingTime())
         , m_minCapacity(0)
         , m_capacity(0)
         , m_age(0)
+        , m_workingSetMaxBytes(workingSetMaxBytes)
+        , m_workingSetMaxEntries(workingSetMaxEntries)
     {
     }
 
@@ -192,12 +194,20 @@
 
     int64_t age() { return m_age; }
 
+    static const int64_t globalWorkingSetMaxBytes = 16000000;
+    static const size_t globalWorkingSetMaxEntries = 2000;
+
+    // We have a smaller cap for the per-codeblock CodeCache that approximates the
+    // linked EvalCodeCache limits, but still allows us to keep large string based
+    // evals at least partially cached.
+    static const unsigned nonGlobalWorkingSetScale = 20;
+    static const int64_t nonGlobalWorkingSetMaxBytes = globalWorkingSetMaxBytes / nonGlobalWorkingSetScale;
+    static const size_t nonGlobalWorkingSetMaxEntries = globalWorkingSetMaxEntries / nonGlobalWorkingSetScale;
+
 private:
     // This constant factor biases cache capacity toward allowing a minimum
     // working set to enter the cache before it starts evicting.
     static const double workingSetTime;
-    static const int64_t workingSetMaxBytes = 16000000;
-    static const size_t workingSetMaxEntries = 2000;
 
     // This constant factor biases cache capacity toward recent activity. We
     // want to adapt to changing workloads.
@@ -209,7 +219,7 @@
     static const int64_t oldObjectSamplingMultiplier = 32;
 
     size_t numberOfEntries() const { return static_cast<size_t>(m_map.size()); }
-    bool canPruneQuickly() const { return numberOfEntries() < workingSetMaxEntries; }
+    bool canPruneQuickly() const { return numberOfEntries() < m_workingSetMaxEntries; }
 
     void pruneSlowCase();
     void prune()
@@ -218,7 +228,7 @@
             return;
 
         if (monotonicallyIncreasingTime() - m_timeAtLastPrune < workingSetTime
-            && m_size - m_sizeAtLastPrune < workingSetMaxBytes
+            && m_size - m_sizeAtLastPrune < m_workingSetMaxBytes
             && canPruneQuickly())
                 return;
 
@@ -232,12 +242,15 @@
     int64_t m_minCapacity;
     int64_t m_capacity;
     int64_t m_age;
+    const int64_t m_workingSetMaxBytes;
+    const size_t m_workingSetMaxEntries;
 };
 
 // Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript().
-class CodeCache {
+class CodeCache : public RefCounted<CodeCache> {
 public:
-    static PassOwnPtr<CodeCache> create() { return adoptPtr(new CodeCache); }
+    enum CodeCacheKind { GlobalCodeCache, NonGlobalCodeCache };
+    static PassRefPtr<CodeCache> create(CodeCacheKind kind) { return adoptRef(new CodeCache(kind)); }
 
     UnlinkedProgramCodeBlock* getProgramCodeBlock(VM&, ProgramExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
     UnlinkedEvalCodeBlock* getEvalCodeBlock(VM&, JSScope*, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
@@ -250,11 +263,14 @@
     }
 
 private:
-    CodeCache();
+    CodeCache(CodeCacheKind);
 
     template <class UnlinkedCodeBlockType, class ExecutableType> 
     UnlinkedCodeBlockType* getCodeBlock(VM&, JSScope*, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
 
+    template <class UnlinkedCodeBlockType, class ExecutableType>
+    UnlinkedCodeBlockType* generateBytecode(VM&, JSScope*, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&);
+
     CodeCacheMap m_sourceCode;
 };
 

Modified: trunk/Source/_javascript_Core/runtime/Executable.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/Executable.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/Executable.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -116,8 +116,9 @@
 
 const ClassInfo EvalExecutable::s_info = { "EvalExecutable", &ScriptExecutable::s_info, 0, 0, CREATE_METHOD_TABLE(EvalExecutable) };
 
-EvalExecutable::EvalExecutable(ExecState* exec, const SourceCode& source, bool inStrictContext)
+EvalExecutable::EvalExecutable(ExecState* exec, PassRefPtr<CodeCache> codeCache, const SourceCode& source, bool inStrictContext)
     : ScriptExecutable(exec->vm().evalExecutableStructure.get(), exec, source, inStrictContext)
+    , m_codeCache(codeCache)
 {
 }
 
@@ -211,7 +212,7 @@
             return throwError(exec, createEvalError(exec, lexicalGlobalObject->evalDisabledErrorMessage()));
 
         JSObject* exception = 0;
-        UnlinkedEvalCodeBlock* unlinkedEvalCode = lexicalGlobalObject->createEvalCodeBlock(exec, scope, this, &exception);
+        UnlinkedEvalCodeBlock* unlinkedEvalCode = lexicalGlobalObject->createEvalCodeBlock(m_codeCache.get(), exec, scope, this, &exception);
         if (!unlinkedEvalCode)
             return exception;
 

Modified: trunk/Source/_javascript_Core/runtime/Executable.h (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/Executable.h	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/Executable.h	2013-05-09 20:03:23 UTC (rev 149836)
@@ -451,9 +451,9 @@
             return *m_evalCodeBlock;
         }
 
-        static EvalExecutable* create(ExecState* exec, const SourceCode& source, bool isInStrictContext) 
+        static EvalExecutable* create(ExecState* exec, PassRefPtr<CodeCache> cache, const SourceCode& source, bool isInStrictContext)
         {
-            EvalExecutable* executable = new (NotNull, allocateCell<EvalExecutable>(*exec->heap())) EvalExecutable(exec, source, isInStrictContext);
+            EvalExecutable* executable = new (NotNull, allocateCell<EvalExecutable>(*exec->heap())) EvalExecutable(exec, cache, source, isInStrictContext);
             executable->finishCreation(exec->vm());
             return executable;
         }
@@ -479,13 +479,14 @@
 
     private:
         static const unsigned StructureFlags = OverridesVisitChildren | ScriptExecutable::StructureFlags;
-        EvalExecutable(ExecState*, const SourceCode&, bool);
+        EvalExecutable(ExecState*, PassRefPtr<CodeCache>, const SourceCode&, bool);
 
         JSObject* compileInternal(ExecState*, JSScope*, JITCode::JITType, unsigned bytecodeIndex = UINT_MAX);
         static void visitChildren(JSCell*, SlotVisitor&);
 
         OwnPtr<EvalCodeBlock> m_evalCodeBlock;
         WriteBarrier<UnlinkedEvalCodeBlock> m_unlinkedEvalCodeBlock;
+        RefPtr<CodeCache> m_codeCache;
     };
 
     class ProgramExecutable : public ScriptExecutable {

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -620,13 +620,13 @@
     return unlinkedCode;
 }
 
-UnlinkedEvalCodeBlock* JSGlobalObject::createEvalCodeBlock(CallFrame* callFrame, JSScope* scope, EvalExecutable* executable, JSObject** exception)
+UnlinkedEvalCodeBlock* JSGlobalObject::createEvalCodeBlock(CodeCache* cache, CallFrame* callFrame, JSScope* scope, EvalExecutable* executable, JSObject** exception)
 {
     ParserError error;
     JSParserStrictness strictness = executable->isStrictMode() ? JSParseStrict : JSParseNormal;
     DebuggerMode debuggerMode = hasDebugger() ? DebuggerOn : DebuggerOff;
     ProfilerMode profilerMode = hasProfiler() ? ProfilerOn : ProfilerOff;
-    UnlinkedEvalCodeBlock* unlinkedCode = vm().codeCache()->getEvalCodeBlock(vm(), scope, executable, executable->source(), strictness, debuggerMode, profilerMode, error);
+    UnlinkedEvalCodeBlock* unlinkedCode = cache->getEvalCodeBlock(vm(), scope, executable, executable->source(), strictness, debuggerMode, profilerMode, error);
 
     if (hasDebugger())
         debugger()->sourceParsed(callFrame, executable->source().provider(), error.m_line, error.m_message);

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2013-05-09 20:03:23 UTC (rev 149836)
@@ -412,7 +412,7 @@
     unsigned weakRandomInteger() { return m_weakRandom.getUint32(); }
 
     UnlinkedProgramCodeBlock* createProgramCodeBlock(CallFrame*, ProgramExecutable*, JSObject** exception);
-    UnlinkedEvalCodeBlock* createEvalCodeBlock(CallFrame*, JSScope*, EvalExecutable*, JSObject** exception);
+    UnlinkedEvalCodeBlock* createEvalCodeBlock(CodeCache*, CallFrame*, JSScope*, EvalExecutable*, JSObject** exception);
 
 protected:
 

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObjectFunctions.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -515,7 +515,7 @@
     }
 
     JSGlobalObject* calleeGlobalObject = exec->callee()->globalObject();
-    EvalExecutable* eval = EvalExecutable::create(exec, makeSource(s), false);
+    EvalExecutable* eval = EvalExecutable::create(exec, exec->vm().codeCache(), makeSource(s), false);
     JSObject* error = eval->compile(exec, calleeGlobalObject);
     if (error)
         return throwVMError(exec, error);

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2013-05-09 20:03:23 UTC (rev 149836)
@@ -194,7 +194,7 @@
     , m_initializingObjectClass(0)
 #endif
     , m_inDefineOwnProperty(false)
-    , m_codeCache(CodeCache::create())
+    , m_codeCache(CodeCache::create(CodeCache::GlobalCodeCache))
 {
     interpreter = new Interpreter(*this);
 

Modified: trunk/Source/_javascript_Core/runtime/VM.h (149835 => 149836)


--- trunk/Source/_javascript_Core/runtime/VM.h	2013-05-09 19:41:23 UTC (rev 149835)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2013-05-09 20:03:23 UTC (rev 149836)
@@ -490,7 +490,7 @@
         const ClassInfo* m_initializingObjectClass;
 #endif
         bool m_inDefineOwnProperty;
-        OwnPtr<CodeCache> m_codeCache;
+        RefPtr<CodeCache> m_codeCache;
 
         TypedArrayDescriptor m_int8ArrayDescriptor;
         TypedArrayDescriptor m_int16ArrayDescriptor;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to