+2015-03-13 Ryosuke Niwa <rniwa@webkit.org>
+
+ BytecodeGenerator needs to be re-entrant to support miranda functions
+ https://bugs.webkit.org/show_bug.cgi?id=142627
+
+ Reviewed by Filip Pizlo.
+
+ Made CodeCache::getGlobalCodeBlock and CodeCache::getFunctionExecutableFromGlobalCode re-entrant
+ by not keeping AddResult while invoking BytecodeGenerator::generate.
+
+ This is needed to support Miranda functions since they need to be lazily initialized.
+
+ * runtime/CodeCache.cpp:
+ (JSC::CodeCache::getGlobalCodeBlock):
+ (JSC::CodeCache::getFunctionExecutableFromGlobalCode):
+ * runtime/CodeCache.h:
+ (JSC::CodeCacheMap::findCacheAndUpdateAge): Extracted from add.
+ (JSC::CodeCacheMap::addCache): Extracted from add.
+ (JSC::CodeCacheMap::add): Deleted.
+
2015-03-13 Mark Lam <mark.lam@apple.com>
Introduce WTF::Atomic to wrap std::atomic for a friendlier CAS.
UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, 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());
+ SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff && !vm.typeProfiler() && !vm.controlFlowProfiler();
- if (!addResult.isNewEntry && canCache) {
- UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(addResult.iterator->value.cell.get());
+ if (cache && canCache) {
+ UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get());
unsigned firstLine = source.firstLine() + unlinkedCodeBlock->firstLine();
unsigned lineCount = unlinkedCodeBlock->lineCount();
unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn();
typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
std::unique_ptr<RootNode> rootNode = parse<RootNode>(&vm, source, 0, Identifier(), strictness, JSParseProgramCode, error);
- if (!rootNode) {
- m_sourceCode.remove(addResult.iterator);
- return 0;
- }
+ if (!rootNode)
+ return nullptr;
+
unsigned lineCount = rootNode->lastLine() - rootNode->lineNo();
unsigned startColumn = rootNode->startColumn() + 1;
bool endColumnIsOnStartLine = !lineCount;
auto generator = std::make_unique<BytecodeGenerator>(vm, rootNode.get(), unlinkedCodeBlock, debuggerMode, profilerMode);
error = generator->generate();
- if (error.isValid()) {
- m_sourceCode.remove(addResult.iterator);
+ if (error.isValid())
return nullptr;
- }
- if (!canCache) {
- m_sourceCode.remove(addResult.iterator);
+ if (!canCache)
return unlinkedCodeBlock;
- }
- addResult.iterator->value = SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age());
+ m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
return unlinkedCodeBlock;
}
UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(VM& vm, const Identifier& name, const SourceCode& source, ParserError& error)
{
SourceCodeKey key = SourceCodeKey(source, name.string(), SourceCodeKey::FunctionType, JSParseNormal);
- CodeCacheMap::AddResult addResult = m_sourceCode.add(key, SourceCodeValue());
- if (!addResult.isNewEntry)
- return jsCast<UnlinkedFunctionExecutable*>(addResult.iterator->value.cell.get());
+ SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
+ if (cache)
+ return jsCast<UnlinkedFunctionExecutable*>(cache->cell.get());
JSTextPosition positionBeforeLastNewline;
std::unique_ptr<ProgramNode> program = parse<ProgramNode>(&vm, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error, &positionBeforeLastNewline);
if (!program) {
RELEASE_ASSERT(error.isValid());
- m_sourceCode.remove(addResult.iterator);
return nullptr;
}
UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, body, UnlinkedNormalFunction);
functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string()));
- addResult.iterator->value = SourceCodeValue(vm, functionExecutable, m_sourceCode.age());
+ m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age()));
return functionExecutable;
}
{
}
- AddResult add(const SourceCodeKey& key, const SourceCodeValue& value)
+ SourceCodeValue* findCacheAndUpdateAge(const SourceCodeKey& key)
{
prune();
- AddResult addResult = m_map.add(key, value);
- if (addResult.isNewEntry) {
- m_size += key.length();
- m_age += key.length();
- return addResult;
- }
+ iterator findResult = m_map.find(key);
+ if (findResult == m_map.end())
+ return nullptr;
- int64_t age = m_age - addResult.iterator->value.age;
+ int64_t age = m_age - findResult->value.age;
if (age > m_capacity) {
// A requested object is older than the cache's capacity. We can
// infer that requested objects are subject to high eviction probability,
m_capacity = m_minCapacity;
}
- addResult.iterator->value.age = m_age;
+ findResult->value.age = m_age;
+ m_age += key.length();
+
+ return &findResult->value;
+ }
+
+ AddResult addCache(const SourceCodeKey& key, const SourceCodeValue& value)
+ {
+ prune();
+
+ AddResult addResult = m_map.add(key, value);
+ ASSERT(addResult.isNewEntry);
+
+ m_size += key.length();
m_age += key.length();
return addResult;
}