Cache bytecode to disk
authortzagallo@apple.com <tzagallo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 Jan 2019 18:00:14 +0000 (18:00 +0000)
committertzagallo@apple.com <tzagallo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 22 Jan 2019 18:00:14 +0000 (18:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192782
<rdar://problem/46084932>

Reviewed by Keith Miller.

Source/JavaScriptCore:

Add the logic to serialize and deserialize the new JSC bytecode. For now,
the cache is only used for tests.

Each class that can be serialized has a counterpart in CachedTypes, which
handles the decoding and encoding. When decoding, the cached objects are
mmap'd from disk, but only used for creating instances of the respective
in-memory version of each object. Ideally, the mmap'd objects should be
used at runtime in the future.

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Sources.txt:
* builtins/BuiltinNames.cpp:
(JSC::BuiltinNames::BuiltinNames):
* builtins/BuiltinNames.h:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::setConstantIdentifierSetRegisters):
* bytecode/CodeBlock.h:
* bytecode/HandlerInfo.h:
(JSC::UnlinkedHandlerInfo::UnlinkedHandlerInfo):
* bytecode/InstructionStream.h:
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addSetConstant):
(JSC::UnlinkedCodeBlock::constantIdentifierSets):
* bytecode/UnlinkedEvalCodeBlock.h:
* bytecode/UnlinkedFunctionCodeBlock.h:
* bytecode/UnlinkedFunctionExecutable.h:
* bytecode/UnlinkedGlobalCodeBlock.h:
(JSC::UnlinkedGlobalCodeBlock::UnlinkedGlobalCodeBlock):
* bytecode/UnlinkedMetadataTable.h:
* bytecode/UnlinkedModuleProgramCodeBlock.h:
* bytecode/UnlinkedProgramCodeBlock.h:
* interpreter/Interpreter.cpp:
* jsc.cpp:
(functionQuit):
(runJSC):
* parser/SourceCode.h:
* parser/SourceCodeKey.h:
(JSC::SourceCodeKey::operator!= const):
* parser/UnlinkedSourceCode.h:
* parser/VariableEnvironment.h:
* runtime/CachedTypes.cpp: Added.
(JSC::Encoder::Allocation::buffer const):
(JSC::Encoder::Allocation::offset const):
(JSC::Encoder::Allocation::Allocation):
(JSC::Encoder::Encoder):
(JSC::Encoder::vm):
(JSC::Encoder::malloc):
(JSC::Encoder::offsetOf):
(JSC::Encoder::cachePtr):
(JSC::Encoder::offsetForPtr):
(JSC::Encoder::release):
(JSC::Encoder::Page::Page):
(JSC::Encoder::Page::malloc):
(JSC::Encoder::Page::buffer const):
(JSC::Encoder::Page::size const):
(JSC::Encoder::Page::getOffset const):
(JSC::Encoder::allocateNewPage):
(JSC::Decoder::Decoder):
(JSC::Decoder::~Decoder):
(JSC::Decoder::vm):
(JSC::Decoder::offsetOf):
(JSC::Decoder::cacheOffset):
(JSC::Decoder::addFinalizer):
(JSC::encode):
(JSC::decode):
(JSC::VariableLengthObject::buffer const):
(JSC::VariableLengthObject::allocate):
(JSC::CachedPtr::encode):
(JSC::CachedPtr::decode const):
(JSC::CachedPtr::operator-> const):
(JSC::CachedPtr::get const):
(JSC::CachedRefPtr::encode):
(JSC::CachedRefPtr::decode const):
(JSC::CachedWriteBarrier::encode):
(JSC::CachedWriteBarrier::decode const):
(JSC::CachedVector::encode):
(JSC::CachedVector::decode const):
(JSC::CachedPair::encode):
(JSC::CachedPair::decode const):
(JSC::CachedHashMap::encode):
(JSC::CachedHashMap::decode const):
(JSC::CachedUniquedStringImpl::encode):
(JSC::CachedUniquedStringImpl::decode const):
(JSC::CachedStringImpl::encode):
(JSC::CachedStringImpl::decode const):
(JSC::CachedString::encode):
(JSC::CachedString::decode const):
(JSC::CachedIdentifier::encode):
(JSC::CachedIdentifier::decode const):
(JSC::CachedOptional::encode):
(JSC::CachedOptional::decode const):
(JSC::CachedOptional::decodeAsPtr const):
(JSC::CachedSimpleJumpTable::encode):
(JSC::CachedSimpleJumpTable::decode const):
(JSC::CachedStringJumpTable::encode):
(JSC::CachedStringJumpTable::decode const):
(JSC::CachedCodeBlockRareData::encode):
(JSC::CachedCodeBlockRareData::decode const):
(JSC::CachedBitVector::encode):
(JSC::CachedBitVector::decode const):
(JSC::CachedHashSet::encode):
(JSC::CachedHashSet::decode const):
(JSC::CachedConstantIdentifierSetEntry::encode):
(JSC::CachedConstantIdentifierSetEntry::decode const):
(JSC::CachedVariableEnvironment::encode):
(JSC::CachedVariableEnvironment::decode const):
(JSC::CachedArray::encode):
(JSC::CachedArray::decode const):
(JSC::CachedScopedArgumentsTable::encode):
(JSC::CachedScopedArgumentsTable::decode const):
(JSC::CachedSymbolTableEntry::encode):
(JSC::CachedSymbolTableEntry::decode const):
(JSC::CachedSymbolTable::encode):
(JSC::CachedSymbolTable::decode const):
(JSC::CachedImmutableButterfly::encode):
(JSC::CachedImmutableButterfly::decode const):
(JSC::CachedRegExp::encode):
(JSC::CachedRegExp::decode const):
(JSC::CachedTemplateObjectDescriptor::encode):
(JSC::CachedTemplateObjectDescriptor::decode const):
(JSC::CachedBigInt::encode):
(JSC::CachedBigInt::decode const):
(JSC::CachedJSValue::encode):
(JSC::CachedJSValue::decode const):
(JSC::CachedInstructionStream::encode):
(JSC::CachedInstructionStream::decode const):
(JSC::CachedMetadataTable::encode):
(JSC::CachedMetadataTable::decode const):
(JSC::CachedSourceOrigin::encode):
(JSC::CachedSourceOrigin::decode const):
(JSC::CachedTextPosition::encode):
(JSC::CachedTextPosition::decode const):
(JSC::CachedSourceProviderShape::encode):
(JSC::CachedSourceProviderShape::decode const):
(JSC::CachedStringSourceProvider::encode):
(JSC::CachedStringSourceProvider::decode const):
(JSC::CachedWebAssemblySourceProvider::encode):
(JSC::CachedWebAssemblySourceProvider::decode const):
(JSC::CachedSourceProvider::encode):
(JSC::CachedSourceProvider::decode const):
(JSC::CachedUnlinkedSourceCodeShape::encode):
(JSC::CachedUnlinkedSourceCodeShape::decode const):
(JSC::CachedSourceCode::encode):
(JSC::CachedSourceCode::decode const):
(JSC::CachedFunctionExecutable::firstLineOffset const):
(JSC::CachedFunctionExecutable::lineCount const):
(JSC::CachedFunctionExecutable::unlinkedFunctionNameStart const):
(JSC::CachedFunctionExecutable::unlinkedBodyStartColumn const):
(JSC::CachedFunctionExecutable::unlinkedBodyEndColumn const):
(JSC::CachedFunctionExecutable::startOffset const):
(JSC::CachedFunctionExecutable::sourceLength const):
(JSC::CachedFunctionExecutable::parametersStartOffset const):
(JSC::CachedFunctionExecutable::typeProfilingStartOffset const):
(JSC::CachedFunctionExecutable::typeProfilingEndOffset const):
(JSC::CachedFunctionExecutable::parameterCount const):
(JSC::CachedFunctionExecutable::features const):
(JSC::CachedFunctionExecutable::sourceParseMode const):
(JSC::CachedFunctionExecutable::isInStrictContext const):
(JSC::CachedFunctionExecutable::hasCapturedVariables const):
(JSC::CachedFunctionExecutable::isBuiltinFunction const):
(JSC::CachedFunctionExecutable::isBuiltinDefaultClassConstructor const):
(JSC::CachedFunctionExecutable::constructAbility const):
(JSC::CachedFunctionExecutable::constructorKind const):
(JSC::CachedFunctionExecutable::functionMode const):
(JSC::CachedFunctionExecutable::scriptMode const):
(JSC::CachedFunctionExecutable::superBinding const):
(JSC::CachedFunctionExecutable::derivedContextType const):
(JSC::CachedFunctionExecutable::name const):
(JSC::CachedFunctionExecutable::ecmaName const):
(JSC::CachedFunctionExecutable::inferredName const):
(JSC::CachedCodeBlock::instructions const):
(JSC::CachedCodeBlock::thisRegister const):
(JSC::CachedCodeBlock::scopeRegister const):
(JSC::CachedCodeBlock::globalObjectRegister const):
(JSC::CachedCodeBlock::sourceURLDirective const):
(JSC::CachedCodeBlock::sourceMappingURLDirective const):
(JSC::CachedCodeBlock::usesEval const):
(JSC::CachedCodeBlock::isStrictMode const):
(JSC::CachedCodeBlock::isConstructor const):
(JSC::CachedCodeBlock::hasCapturedVariables const):
(JSC::CachedCodeBlock::isBuiltinFunction const):
(JSC::CachedCodeBlock::superBinding const):
(JSC::CachedCodeBlock::scriptMode const):
(JSC::CachedCodeBlock::isArrowFunctionContext const):
(JSC::CachedCodeBlock::isClassContext const):
(JSC::CachedCodeBlock::wasCompiledWithDebuggingOpcodes const):
(JSC::CachedCodeBlock::constructorKind const):
(JSC::CachedCodeBlock::derivedContextType const):
(JSC::CachedCodeBlock::evalContextType const):
(JSC::CachedCodeBlock::hasTailCalls const):
(JSC::CachedCodeBlock::lineCount const):
(JSC::CachedCodeBlock::endColumn const):
(JSC::CachedCodeBlock::numVars const):
(JSC::CachedCodeBlock::numCalleeLocals const):
(JSC::CachedCodeBlock::numParameters const):
(JSC::CachedCodeBlock::features const):
(JSC::CachedCodeBlock::parseMode const):
(JSC::CachedCodeBlock::codeType const):
(JSC::CachedCodeBlock::rareData const):
(JSC::CachedProgramCodeBlock::encode):
(JSC::CachedProgramCodeBlock::decode const):
(JSC::CachedModuleCodeBlock::encode):
(JSC::CachedModuleCodeBlock::decode const):
(JSC::CachedEvalCodeBlock::encode):
(JSC::CachedEvalCodeBlock::decode const):
(JSC::CachedFunctionCodeBlock::encode):
(JSC::CachedFunctionCodeBlock::decode const):
(JSC::UnlinkedFunctionCodeBlock::UnlinkedFunctionCodeBlock):
(JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
(JSC::CachedCodeBlock<CodeBlockType>::decode const):
(JSC::UnlinkedProgramCodeBlock::UnlinkedProgramCodeBlock):
(JSC::UnlinkedModuleProgramCodeBlock::UnlinkedModuleProgramCodeBlock):
(JSC::UnlinkedEvalCodeBlock::UnlinkedEvalCodeBlock):
(JSC::CachedFunctionExecutable::encode):
(JSC::CachedFunctionExecutable::decode const):
(JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
(JSC::CachedCodeBlock<CodeBlockType>::encode):
(JSC::CachedSourceCodeKey::encode):
(JSC::CachedSourceCodeKey::decode const):
(JSC::CacheEntry::encode):
(JSC::CacheEntry:: const):
(JSC:: const):
(JSC::encodeCodeBlock):
(JSC::decodeCodeBlockImpl):
* runtime/CachedTypes.h: Copied from Source/JavaScriptCore/bytecode/UnlinkedGlobalCodeBlock.h.
(JSC::decodeCodeBlock):
* runtime/CodeCache.cpp:
(JSC::CodeCacheMap::pruneSlowCase):
(JSC::CodeCache::getUnlinkedGlobalCodeBlock):
(JSC::CodeCache::getUnlinkedGlobalFunctionExecutable):
(JSC::CodeCache::write):
* runtime/CodeCache.h:
(JSC::CodeCacheMap::begin):
(JSC::CodeCacheMap::end):
(JSC::CodeCacheMap::fetchFromDiskImpl):
(JSC::CodeCacheMap::findCacheAndUpdateAge):
(JSC::writeCodeBlock):
* runtime/JSBigInt.cpp:
* runtime/JSBigInt.h:
* runtime/Options.cpp:
(JSC::recomputeDependentOptions):
* runtime/RegExp.h:
* runtime/ScopedArgumentsTable.h:
* runtime/StackFrame.h:
* runtime/StructureInlines.h:
* runtime/SymbolTable.h:

Source/WTF:

BitVectors have to be friends with JSC::CacheBitVector to allow
serializing its buffer as part of the bytecode cache encoding.

* wtf/BitVector.h:

Tools:

Add test helper to execute bytecode-cache tests: it executes each test
twice, the first with JSC_diskCachePath set to a temporary directory
and second with JSC_forceDiskCache=true (in addition to the cache path)
to guarantee that only the disk cache is being used and no new
UnlinkedCodeBlocks are being created.

* Scripts/jsc-stress-test-helpers/bytecode-cache-test-helper.sh: Added.
* Scripts/run-jsc-stress-tests:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@240255 268f45cc-cd09-0410-ab3c-d52691b4dbfc

41 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/Sources.txt
Source/JavaScriptCore/builtins/BuiltinNames.cpp
Source/JavaScriptCore/builtins/BuiltinNames.h
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlock.h
Source/JavaScriptCore/bytecode/HandlerInfo.h
Source/JavaScriptCore/bytecode/InstructionStream.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedEvalCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedFunctionCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedFunctionExecutable.h
Source/JavaScriptCore/bytecode/UnlinkedGlobalCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedMetadataTable.h
Source/JavaScriptCore/bytecode/UnlinkedModuleProgramCodeBlock.h
Source/JavaScriptCore/bytecode/UnlinkedProgramCodeBlock.h
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/parser/SourceCode.h
Source/JavaScriptCore/parser/SourceCodeKey.h
Source/JavaScriptCore/parser/UnlinkedSourceCode.h
Source/JavaScriptCore/parser/VariableEnvironment.h
Source/JavaScriptCore/runtime/CachedTypes.cpp [new file with mode: 0644]
Source/JavaScriptCore/runtime/CachedTypes.h [new file with mode: 0644]
Source/JavaScriptCore/runtime/CodeCache.cpp
Source/JavaScriptCore/runtime/CodeCache.h
Source/JavaScriptCore/runtime/JSBigInt.cpp
Source/JavaScriptCore/runtime/JSBigInt.h
Source/JavaScriptCore/runtime/Options.cpp
Source/JavaScriptCore/runtime/RegExp.h
Source/JavaScriptCore/runtime/ScopedArgumentsTable.h
Source/JavaScriptCore/runtime/StackFrame.h
Source/JavaScriptCore/runtime/StructureInlines.h
Source/JavaScriptCore/runtime/SymbolTable.h
Source/WTF/ChangeLog
Source/WTF/wtf/BitVector.h
Tools/ChangeLog
Tools/Scripts/jsc-stress-test-helpers/bytecode-cache-test-helper.sh [new file with mode: 0644]
Tools/Scripts/run-jsc-stress-tests

index 6acdfb1..ea858b1 100644 (file)
@@ -833,6 +833,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/JSGlobalLexicalEnvironment.h
     runtime/JSGlobalObject.h
     runtime/JSGlobalObjectInlines.h
+    runtime/JSImmutableButterfly.h
     runtime/JSInternalPromise.h
     runtime/JSInternalPromiseDeferred.h
     runtime/JSMicrotask.h
@@ -848,6 +849,7 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
     runtime/JSPromise.h
     runtime/JSPromiseConstructor.h
     runtime/JSPromiseDeferred.h
+    runtime/JSPropertyNameEnumerator.h
     runtime/JSProxy.h
     runtime/JSRunLoopTimer.h
     runtime/JSScope.h
index ce68cf6..0248aa6 100644 (file)
@@ -1,3 +1,259 @@
+2019-01-22  Tadeu Zagallo  <tzagallo@apple.com>
+
+        Cache bytecode to disk
+        https://bugs.webkit.org/show_bug.cgi?id=192782
+        <rdar://problem/46084932>
+
+        Reviewed by Keith Miller.
+
+        Add the logic to serialize and deserialize the new JSC bytecode. For now,
+        the cache is only used for tests.
+
+        Each class that can be serialized has a counterpart in CachedTypes, which
+        handles the decoding and encoding. When decoding, the cached objects are
+        mmap'd from disk, but only used for creating instances of the respective
+        in-memory version of each object. Ideally, the mmap'd objects should be
+        used at runtime in the future.
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * Sources.txt:
+        * builtins/BuiltinNames.cpp:
+        (JSC::BuiltinNames::BuiltinNames):
+        * builtins/BuiltinNames.h:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::setConstantIdentifierSetRegisters):
+        * bytecode/CodeBlock.h:
+        * bytecode/HandlerInfo.h:
+        (JSC::UnlinkedHandlerInfo::UnlinkedHandlerInfo):
+        * bytecode/InstructionStream.h:
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::addSetConstant):
+        (JSC::UnlinkedCodeBlock::constantIdentifierSets):
+        * bytecode/UnlinkedEvalCodeBlock.h:
+        * bytecode/UnlinkedFunctionCodeBlock.h:
+        * bytecode/UnlinkedFunctionExecutable.h:
+        * bytecode/UnlinkedGlobalCodeBlock.h:
+        (JSC::UnlinkedGlobalCodeBlock::UnlinkedGlobalCodeBlock):
+        * bytecode/UnlinkedMetadataTable.h:
+        * bytecode/UnlinkedModuleProgramCodeBlock.h:
+        * bytecode/UnlinkedProgramCodeBlock.h:
+        * interpreter/Interpreter.cpp:
+        * jsc.cpp:
+        (functionQuit):
+        (runJSC):
+        * parser/SourceCode.h:
+        * parser/SourceCodeKey.h:
+        (JSC::SourceCodeKey::operator!= const):
+        * parser/UnlinkedSourceCode.h:
+        * parser/VariableEnvironment.h:
+        * runtime/CachedTypes.cpp: Added.
+        (JSC::Encoder::Allocation::buffer const):
+        (JSC::Encoder::Allocation::offset const):
+        (JSC::Encoder::Allocation::Allocation):
+        (JSC::Encoder::Encoder):
+        (JSC::Encoder::vm):
+        (JSC::Encoder::malloc):
+        (JSC::Encoder::offsetOf):
+        (JSC::Encoder::cachePtr):
+        (JSC::Encoder::offsetForPtr):
+        (JSC::Encoder::release):
+        (JSC::Encoder::Page::Page):
+        (JSC::Encoder::Page::malloc):
+        (JSC::Encoder::Page::buffer const):
+        (JSC::Encoder::Page::size const):
+        (JSC::Encoder::Page::getOffset const):
+        (JSC::Encoder::allocateNewPage):
+        (JSC::Decoder::Decoder):
+        (JSC::Decoder::~Decoder):
+        (JSC::Decoder::vm):
+        (JSC::Decoder::offsetOf):
+        (JSC::Decoder::cacheOffset):
+        (JSC::Decoder::addFinalizer):
+        (JSC::encode):
+        (JSC::decode):
+        (JSC::VariableLengthObject::buffer const):
+        (JSC::VariableLengthObject::allocate):
+        (JSC::CachedPtr::encode):
+        (JSC::CachedPtr::decode const):
+        (JSC::CachedPtr::operator-> const):
+        (JSC::CachedPtr::get const):
+        (JSC::CachedRefPtr::encode):
+        (JSC::CachedRefPtr::decode const):
+        (JSC::CachedWriteBarrier::encode):
+        (JSC::CachedWriteBarrier::decode const):
+        (JSC::CachedVector::encode):
+        (JSC::CachedVector::decode const):
+        (JSC::CachedPair::encode):
+        (JSC::CachedPair::decode const):
+        (JSC::CachedHashMap::encode):
+        (JSC::CachedHashMap::decode const):
+        (JSC::CachedUniquedStringImpl::encode):
+        (JSC::CachedUniquedStringImpl::decode const):
+        (JSC::CachedStringImpl::encode):
+        (JSC::CachedStringImpl::decode const):
+        (JSC::CachedString::encode):
+        (JSC::CachedString::decode const):
+        (JSC::CachedIdentifier::encode):
+        (JSC::CachedIdentifier::decode const):
+        (JSC::CachedOptional::encode):
+        (JSC::CachedOptional::decode const):
+        (JSC::CachedOptional::decodeAsPtr const):
+        (JSC::CachedSimpleJumpTable::encode):
+        (JSC::CachedSimpleJumpTable::decode const):
+        (JSC::CachedStringJumpTable::encode):
+        (JSC::CachedStringJumpTable::decode const):
+        (JSC::CachedCodeBlockRareData::encode):
+        (JSC::CachedCodeBlockRareData::decode const):
+        (JSC::CachedBitVector::encode):
+        (JSC::CachedBitVector::decode const):
+        (JSC::CachedHashSet::encode):
+        (JSC::CachedHashSet::decode const):
+        (JSC::CachedConstantIdentifierSetEntry::encode):
+        (JSC::CachedConstantIdentifierSetEntry::decode const):
+        (JSC::CachedVariableEnvironment::encode):
+        (JSC::CachedVariableEnvironment::decode const):
+        (JSC::CachedArray::encode):
+        (JSC::CachedArray::decode const):
+        (JSC::CachedScopedArgumentsTable::encode):
+        (JSC::CachedScopedArgumentsTable::decode const):
+        (JSC::CachedSymbolTableEntry::encode):
+        (JSC::CachedSymbolTableEntry::decode const):
+        (JSC::CachedSymbolTable::encode):
+        (JSC::CachedSymbolTable::decode const):
+        (JSC::CachedImmutableButterfly::encode):
+        (JSC::CachedImmutableButterfly::decode const):
+        (JSC::CachedRegExp::encode):
+        (JSC::CachedRegExp::decode const):
+        (JSC::CachedTemplateObjectDescriptor::encode):
+        (JSC::CachedTemplateObjectDescriptor::decode const):
+        (JSC::CachedBigInt::encode):
+        (JSC::CachedBigInt::decode const):
+        (JSC::CachedJSValue::encode):
+        (JSC::CachedJSValue::decode const):
+        (JSC::CachedInstructionStream::encode):
+        (JSC::CachedInstructionStream::decode const):
+        (JSC::CachedMetadataTable::encode):
+        (JSC::CachedMetadataTable::decode const):
+        (JSC::CachedSourceOrigin::encode):
+        (JSC::CachedSourceOrigin::decode const):
+        (JSC::CachedTextPosition::encode):
+        (JSC::CachedTextPosition::decode const):
+        (JSC::CachedSourceProviderShape::encode):
+        (JSC::CachedSourceProviderShape::decode const):
+        (JSC::CachedStringSourceProvider::encode):
+        (JSC::CachedStringSourceProvider::decode const):
+        (JSC::CachedWebAssemblySourceProvider::encode):
+        (JSC::CachedWebAssemblySourceProvider::decode const):
+        (JSC::CachedSourceProvider::encode):
+        (JSC::CachedSourceProvider::decode const):
+        (JSC::CachedUnlinkedSourceCodeShape::encode):
+        (JSC::CachedUnlinkedSourceCodeShape::decode const):
+        (JSC::CachedSourceCode::encode):
+        (JSC::CachedSourceCode::decode const):
+        (JSC::CachedFunctionExecutable::firstLineOffset const):
+        (JSC::CachedFunctionExecutable::lineCount const):
+        (JSC::CachedFunctionExecutable::unlinkedFunctionNameStart const):
+        (JSC::CachedFunctionExecutable::unlinkedBodyStartColumn const):
+        (JSC::CachedFunctionExecutable::unlinkedBodyEndColumn const):
+        (JSC::CachedFunctionExecutable::startOffset const):
+        (JSC::CachedFunctionExecutable::sourceLength const):
+        (JSC::CachedFunctionExecutable::parametersStartOffset const):
+        (JSC::CachedFunctionExecutable::typeProfilingStartOffset const):
+        (JSC::CachedFunctionExecutable::typeProfilingEndOffset const):
+        (JSC::CachedFunctionExecutable::parameterCount const):
+        (JSC::CachedFunctionExecutable::features const):
+        (JSC::CachedFunctionExecutable::sourceParseMode const):
+        (JSC::CachedFunctionExecutable::isInStrictContext const):
+        (JSC::CachedFunctionExecutable::hasCapturedVariables const):
+        (JSC::CachedFunctionExecutable::isBuiltinFunction const):
+        (JSC::CachedFunctionExecutable::isBuiltinDefaultClassConstructor const):
+        (JSC::CachedFunctionExecutable::constructAbility const):
+        (JSC::CachedFunctionExecutable::constructorKind const):
+        (JSC::CachedFunctionExecutable::functionMode const):
+        (JSC::CachedFunctionExecutable::scriptMode const):
+        (JSC::CachedFunctionExecutable::superBinding const):
+        (JSC::CachedFunctionExecutable::derivedContextType const):
+        (JSC::CachedFunctionExecutable::name const):
+        (JSC::CachedFunctionExecutable::ecmaName const):
+        (JSC::CachedFunctionExecutable::inferredName const):
+        (JSC::CachedCodeBlock::instructions const):
+        (JSC::CachedCodeBlock::thisRegister const):
+        (JSC::CachedCodeBlock::scopeRegister const):
+        (JSC::CachedCodeBlock::globalObjectRegister const):
+        (JSC::CachedCodeBlock::sourceURLDirective const):
+        (JSC::CachedCodeBlock::sourceMappingURLDirective const):
+        (JSC::CachedCodeBlock::usesEval const):
+        (JSC::CachedCodeBlock::isStrictMode const):
+        (JSC::CachedCodeBlock::isConstructor const):
+        (JSC::CachedCodeBlock::hasCapturedVariables const):
+        (JSC::CachedCodeBlock::isBuiltinFunction const):
+        (JSC::CachedCodeBlock::superBinding const):
+        (JSC::CachedCodeBlock::scriptMode const):
+        (JSC::CachedCodeBlock::isArrowFunctionContext const):
+        (JSC::CachedCodeBlock::isClassContext const):
+        (JSC::CachedCodeBlock::wasCompiledWithDebuggingOpcodes const):
+        (JSC::CachedCodeBlock::constructorKind const):
+        (JSC::CachedCodeBlock::derivedContextType const):
+        (JSC::CachedCodeBlock::evalContextType const):
+        (JSC::CachedCodeBlock::hasTailCalls const):
+        (JSC::CachedCodeBlock::lineCount const):
+        (JSC::CachedCodeBlock::endColumn const):
+        (JSC::CachedCodeBlock::numVars const):
+        (JSC::CachedCodeBlock::numCalleeLocals const):
+        (JSC::CachedCodeBlock::numParameters const):
+        (JSC::CachedCodeBlock::features const):
+        (JSC::CachedCodeBlock::parseMode const):
+        (JSC::CachedCodeBlock::codeType const):
+        (JSC::CachedCodeBlock::rareData const):
+        (JSC::CachedProgramCodeBlock::encode):
+        (JSC::CachedProgramCodeBlock::decode const):
+        (JSC::CachedModuleCodeBlock::encode):
+        (JSC::CachedModuleCodeBlock::decode const):
+        (JSC::CachedEvalCodeBlock::encode):
+        (JSC::CachedEvalCodeBlock::decode const):
+        (JSC::CachedFunctionCodeBlock::encode):
+        (JSC::CachedFunctionCodeBlock::decode const):
+        (JSC::UnlinkedFunctionCodeBlock::UnlinkedFunctionCodeBlock):
+        (JSC::UnlinkedCodeBlock::UnlinkedCodeBlock):
+        (JSC::CachedCodeBlock<CodeBlockType>::decode const):
+        (JSC::UnlinkedProgramCodeBlock::UnlinkedProgramCodeBlock):
+        (JSC::UnlinkedModuleProgramCodeBlock::UnlinkedModuleProgramCodeBlock):
+        (JSC::UnlinkedEvalCodeBlock::UnlinkedEvalCodeBlock):
+        (JSC::CachedFunctionExecutable::encode):
+        (JSC::CachedFunctionExecutable::decode const):
+        (JSC::UnlinkedFunctionExecutable::UnlinkedFunctionExecutable):
+        (JSC::CachedCodeBlock<CodeBlockType>::encode):
+        (JSC::CachedSourceCodeKey::encode):
+        (JSC::CachedSourceCodeKey::decode const):
+        (JSC::CacheEntry::encode):
+        (JSC::CacheEntry:: const):
+        (JSC:: const):
+        (JSC::encodeCodeBlock):
+        (JSC::decodeCodeBlockImpl):
+        * runtime/CachedTypes.h: Copied from Source/JavaScriptCore/bytecode/UnlinkedGlobalCodeBlock.h.
+        (JSC::decodeCodeBlock):
+        * runtime/CodeCache.cpp:
+        (JSC::CodeCacheMap::pruneSlowCase):
+        (JSC::CodeCache::getUnlinkedGlobalCodeBlock):
+        (JSC::CodeCache::getUnlinkedGlobalFunctionExecutable):
+        (JSC::CodeCache::write):
+        * runtime/CodeCache.h:
+        (JSC::CodeCacheMap::begin):
+        (JSC::CodeCacheMap::end):
+        (JSC::CodeCacheMap::fetchFromDiskImpl):
+        (JSC::CodeCacheMap::findCacheAndUpdateAge):
+        (JSC::writeCodeBlock):
+        * runtime/JSBigInt.cpp:
+        * runtime/JSBigInt.h:
+        * runtime/Options.cpp:
+        (JSC::recomputeDependentOptions):
+        * runtime/RegExp.h:
+        * runtime/ScopedArgumentsTable.h:
+        * runtime/StackFrame.h:
+        * runtime/StructureInlines.h:
+        * runtime/SymbolTable.h:
+
 2019-01-22  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Invalidate old scope operations using global lexical binding epoch
index 3e744db..852cc04 100644 (file)
                262D85B71C0D650F006ACB61 /* AirFixPartialRegisterStalls.h in Headers */ = {isa = PBXBuildFile; fileRef = 262D85B51C0D650F006ACB61 /* AirFixPartialRegisterStalls.h */; };
                2684D4381C00161C0081D663 /* AirLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 2684D4371C00161C0081D663 /* AirLiveness.h */; };
                269D636E1BFBE5D100101B1D /* FTLOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 269D636D1BFBE5D000101B1D /* FTLOutput.h */; };
-               2A05ABD61961DF2400341750 /* JSPropertyNameEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */; };
+               2A05ABD61961DF2400341750 /* JSPropertyNameEnumerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A05ABD41961DF2400341750 /* JSPropertyNameEnumerator.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2A111246192FCE79005EE18D /* CustomGetterSetter.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A111244192FCE79005EE18D /* CustomGetterSetter.h */; settings = {ATTRIBUTES = (Private, ); }; };
                2A48D1911772365B00C65A5F /* APICallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = C211B574176A224D000E2A23 /* APICallbackFunction.h */; };
                2A4BB7F318A41179008A0FCD /* JSManagedValueInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A4BB7F218A41179008A0FCD /* JSManagedValueInternal.h */; };
                53EAFE2F208DFAB4007D524B /* testapi.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 531D4E191F59CDD200EC836C /* testapi.cpp */; };
                53EE01B6218F691600AD1F8D /* JSScript.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EE01B5218F690F00AD1F8D /* JSScript.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53EE01B8218F7EFF00AD1F8D /* JSScriptInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 53EE01B7218F7EFF00AD1F8D /* JSScriptInternal.h */; };
-               53F11F41209138D700E411A7 /* JSImmutableButterfly.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F11F40209138D700E411A7 /* JSImmutableButterfly.h */; };
+               53F11F41209138D700E411A7 /* JSImmutableButterfly.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F11F40209138D700E411A7 /* JSImmutableButterfly.h */; settings = {ATTRIBUTES = (Private, ); }; };
                53F40E851D58F9770099A1B6 /* WasmSections.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E841D58F9770099A1B6 /* WasmSections.h */; };
                53F40E8B1D5901BB0099A1B6 /* WasmFunctionParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8A1D5901BB0099A1B6 /* WasmFunctionParser.h */; };
                53F40E8D1D5901F20099A1B6 /* WasmParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 53F40E8C1D5901F20099A1B6 /* WasmParser.h */; };
                14386A731DD69895008652C4 /* DirectEvalExecutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DirectEvalExecutable.h; sourceTree = "<group>"; };
                14386A761DD6989C008652C4 /* IndirectEvalExecutable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IndirectEvalExecutable.cpp; sourceTree = "<group>"; };
                14386A771DD6989C008652C4 /* IndirectEvalExecutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndirectEvalExecutable.h; sourceTree = "<group>"; };
+               143BE26521C857770020CD17 /* CachedTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CachedTypes.h; sourceTree = "<group>"; };
                1440051F0A531D3B0005F061 /* Node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Node.h; path = tests/Node.h; sourceTree = "<group>"; };
                144005200A531D3B0005F061 /* Node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = Node.c; path = tests/Node.c; sourceTree = "<group>"; };
                144007480A536CC20005F061 /* NodeList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = NodeList.h; path = tests/NodeList.h; sourceTree = "<group>"; };
                14D857740A4696C80032146C /* testapi.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = testapi.js; path = API/tests/testapi.js; sourceTree = "<group>"; };
                14DA818E0D99FD2000B0A4FB /* JSLexicalEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSLexicalEnvironment.h; sourceTree = "<group>"; };
                14DA818F0D99FD2000B0A4FB /* JSLexicalEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSLexicalEnvironment.cpp; sourceTree = "<group>"; };
+               14DAFA4521E3B871004B68F7 /* CachedTypes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CachedTypes.cpp; sourceTree = "<group>"; };
                14DE0D680D02431400AACCA2 /* JSGlobalObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = JSGlobalObject.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
                14DF04D916B3996D0016A513 /* StaticPropertyAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StaticPropertyAnalysis.h; sourceTree = "<group>"; };
                14E84F9914EE1ACC00D6D5D4 /* WeakBlock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WeakBlock.cpp; sourceTree = "<group>"; };
                                9E729409190F0306001A91B5 /* BundlePath.mm */,
                                0FB7F38B15ED8E3800F167B2 /* Butterfly.h */,
                                0FB7F38C15ED8E3800F167B2 /* ButterflyInlines.h */,
+                               14DAFA4521E3B871004B68F7 /* CachedTypes.cpp */,
+                               143BE26521C857770020CD17 /* CachedTypes.h */,
                                0FEC3C5F1F379F5300F59B6C /* CagedBarrierPtr.h */,
                                BCA62DFE0E2826230004F30D /* CallData.cpp */,
                                145C507F0D9DF63B0088F6B9 /* CallData.h */,
index 949edca..258d49c 100644 (file)
@@ -713,6 +713,7 @@ runtime/BooleanConstructor.cpp
 runtime/BooleanObject.cpp
 runtime/BooleanPrototype.cpp
 runtime/CallData.cpp
+runtime/CachedTypes.cpp
 runtime/CatchScope.cpp
 runtime/ClassInfo.cpp
 runtime/ClonedArguments.cpp
index 45de82e..a0f853a 100644 (file)
@@ -63,6 +63,7 @@ BuiltinNames::BuiltinNames(VM* vm, CommonIdentifiers* commonIdentifiers)
     JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY)
     JSC_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_PUBLIC_TO_PRIVATE_ENTRY)
     JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PUBLIC_TO_PRIVATE_ENTRY)
+    JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL_PRIVATE_TO_PUBLIC_ENTRY)
     JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL_PUBLIC_TO_PRIVATE_ENTRY)
     m_privateToPublicMap.add(m_dollarVMPrivateName.impl(), &m_dollarVMName);
     m_publicToPrivateMap.add(m_dollarVMName.impl(), &m_dollarVMPrivateName);
index f66b8cb..8531db2 100644 (file)
@@ -213,6 +213,7 @@ extern SymbolImpl::StaticSymbolImpl polyProtoPrivateName;
 // We commandeer the publicToPrivateMap to allow us to convert private symbol names into the appropriate symbol.
 // e.g. @iteratorSymbol points to Symbol.iterator in this map rather than to a an actual private name.
 // FIXME: This is a weird hack and we shouldn't need to do this.
+#define INITIALIZE_SYMBOL_PRIVATE_TO_PUBLIC_ENTRY(name) m_privateToPublicMap.add(m_##name##Symbol.impl(), &m_##name##SymbolPrivateIdentifier);
 #define INITIALIZE_SYMBOL_PUBLIC_TO_PRIVATE_ENTRY(name) m_publicToPrivateMap.add(m_##name##SymbolPrivateIdentifier.impl(), &m_##name##Symbol);
 
 class BuiltinNames {
index 6517925..bf863fb 100644 (file)
@@ -869,7 +869,7 @@ CodeBlock::~CodeBlock()
 #endif // ENABLE(JIT)
 }
 
-void CodeBlock::setConstantIdentifierSetRegisters(VM& vm, const Vector<ConstantIndentifierSetEntry>& constants)
+void CodeBlock::setConstantIdentifierSetRegisters(VM& vm, const Vector<ConstantIdentifierSetEntry>& constants)
 {
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSGlobalObject* globalObject = m_globalObject.get();
index 94e6bd2..c747426 100644 (file)
@@ -890,7 +890,7 @@ private:
 
     void updateAllPredictionsAndCountLiveness(unsigned& numberOfLiveNonArgumentValueProfiles, unsigned& numberOfSamplesInProfiles);
 
-    void setConstantIdentifierSetRegisters(VM&, const Vector<ConstantIndentifierSetEntry>& constants);
+    void setConstantIdentifierSetRegisters(VM&, const Vector<ConstantIdentifierSetEntry>& constants);
 
     void setConstantRegisters(const Vector<WriteBarrier<Unknown>>& constants, const Vector<SourceCodeRepresentation>& constantsSourceCodeRepresentation);
 
index 66c3b76..7f465d2 100644 (file)
@@ -89,6 +89,10 @@ struct HandlerInfoBase {
 };
 
 struct UnlinkedHandlerInfo : public HandlerInfoBase {
+    UnlinkedHandlerInfo()
+    {
+    }
+
     UnlinkedHandlerInfo(uint32_t start, uint32_t end, uint32_t target, HandlerType handlerType)
     {
         this->start = start;
index 70ede3e..b40480a 100644 (file)
@@ -39,6 +39,7 @@ class InstructionStream {
     using InstructionBuffer = Vector<uint8_t, 0, UnsafeVectorOverflow>;
 
     friend class InstructionStreamWriter;
+    friend class CachedInstructionStream;
 public:
     size_t sizeInBytes() const;
 
index f0e9d64..148a535 100644 (file)
@@ -65,12 +65,15 @@ class UnlinkedFunctionCodeBlock;
 class UnlinkedFunctionExecutable;
 struct ExecutableInfo;
 
+template<typename CodeBlockType>
+class CachedCodeBlock;
+
 typedef unsigned UnlinkedValueProfile;
 typedef unsigned UnlinkedArrayProfile;
 typedef unsigned UnlinkedArrayAllocationProfile;
 typedef unsigned UnlinkedObjectAllocationProfile;
 typedef unsigned UnlinkedLLIntCallLinkInfo;
-using ConstantIndentifierSetEntry = std::pair<IdentifierSet, unsigned>;
+using ConstantIdentifierSetEntry = std::pair<IdentifierSet, unsigned>;
 
 struct UnlinkedStringJumpTable {
     struct OffsetLocation {
@@ -168,7 +171,7 @@ public:
         unsigned result = m_constantRegisters.size();
         m_constantRegisters.append(WriteBarrier<Unknown>());
         m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other);
-        m_constantIdentifierSets.append(ConstantIndentifierSetEntry(set, result));
+        m_constantIdentifierSets.append(ConstantIdentifierSetEntry(set, result));
     }
 
     unsigned addConstant(JSValue v, SourceCodeRepresentation sourceCodeRepresentation = SourceCodeRepresentation::Other)
@@ -202,7 +205,7 @@ public:
         return m_linkTimeConstants[index];
     }
     const Vector<WriteBarrier<Unknown>>& constantRegisters() { return m_constantRegisters; }
-    const Vector<ConstantIndentifierSetEntry>& constantIdentifierSets() { return m_constantIdentifierSets; }
+    const Vector<ConstantIdentifierSetEntry>& constantIdentifierSets() { return m_constantIdentifierSets; }
     const WriteBarrier<Unknown>& constantRegister(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex]; }
     ALWAYS_INLINE bool isConstantRegisterIndex(int index) const { return index >= FirstConstantRegisterIndex; }
     ALWAYS_INLINE JSValue getConstant(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex].get(); }
@@ -367,6 +370,10 @@ public:
 
 protected:
     UnlinkedCodeBlock(VM*, Structure*, CodeType, const ExecutableInfo&, DebuggerMode);
+
+    template<typename CodeBlockType>
+    UnlinkedCodeBlock(Decoder&, Structure*, const CachedCodeBlock<CodeBlockType>&);
+
     ~UnlinkedCodeBlock();
 
     void finishCreation(VM& vm)
@@ -378,6 +385,9 @@ private:
     friend class BytecodeRewriter;
     friend class BytecodeGenerator;
 
+    template<typename CodeBlockType>
+    friend class CachedCodeBlock;
+
     void applyModification(BytecodeRewriter&, InstructionStreamWriter&);
 
     void createRareDataIfNecessary()
@@ -444,7 +454,7 @@ private:
     Vector<Identifier> m_identifiers;
     Vector<BitVector> m_bitVectors;
     Vector<WriteBarrier<Unknown>> m_constantRegisters;
-    Vector<ConstantIndentifierSetEntry> m_constantIdentifierSets;
+    Vector<ConstantIdentifierSetEntry> m_constantIdentifierSets;
     Vector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation;
     typedef Vector<WriteBarrier<UnlinkedFunctionExecutable>> FunctionExpressionVector;
     FunctionExpressionVector m_functionDecls;
index 9987b43..2f32379 100644 (file)
@@ -29,6 +29,8 @@
 
 namespace JSC {
 
+class CachedEvalCodeBlock;
+
 class UnlinkedEvalCodeBlock final : public UnlinkedGlobalCodeBlock {
 public:
     typedef UnlinkedGlobalCodeBlock Base;
@@ -59,11 +61,15 @@ public:
         m_functionHoistingCandidates = WTFMove(functionHoistingCandidates);
     }
 private:
+    friend CachedEvalCodeBlock;
+
     UnlinkedEvalCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info, DebuggerMode debuggerMode)
         : Base(vm, structure, EvalCode, info, debuggerMode)
     {
     }
 
+    UnlinkedEvalCodeBlock(Decoder&, const CachedEvalCodeBlock&);
+
     Vector<Identifier, 0, UnsafeVectorOverflow> m_variables;
     Vector<Identifier, 0, UnsafeVectorOverflow> m_functionHoistingCandidates;
 
index b5482b6..65aafc8 100644 (file)
@@ -29,6 +29,8 @@
 
 namespace JSC {
 
+class CachedFunctionCodeBlock;
+
 class UnlinkedFunctionCodeBlock final : public UnlinkedCodeBlock {
 public:
     typedef UnlinkedCodeBlock Base;
@@ -44,10 +46,14 @@ public:
     static void destroy(JSCell*);
 
 private:
+    friend CachedFunctionCodeBlock;
+
     UnlinkedFunctionCodeBlock(VM* vm, Structure* structure, CodeType codeType, const ExecutableInfo& info, DebuggerMode debuggerMode)
         : Base(vm, structure, codeType, info, debuggerMode)
     {
     }
+
+    UnlinkedFunctionCodeBlock(Decoder&, const CachedFunctionCodeBlock&);
     
 public:
     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue proto)
index 526898c..6526cd9 100644 (file)
 
 namespace JSC {
 
+class Decoder;
 class FunctionMetadataNode;
 class FunctionExecutable;
 class ParserError;
 class SourceProvider;
 class UnlinkedFunctionCodeBlock;
+class CachedFunctionExecutable;
 
 enum UnlinkedFunctionKind {
     UnlinkedNormalFunction,
@@ -54,6 +56,7 @@ class UnlinkedFunctionExecutable final : public JSCell {
 public:
     friend class CodeCache;
     friend class VM;
+    friend CachedFunctionExecutable;
 
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
@@ -147,6 +150,7 @@ public:
 
 private:
     UnlinkedFunctionExecutable(VM*, Structure*, const SourceCode&, FunctionMetadataNode*, UnlinkedFunctionKind, ConstructAbility, JSParserScriptMode, VariableEnvironment&,  JSC::DerivedContextType, bool isBuiltinDefaultClassConstructor);
+    UnlinkedFunctionExecutable(Decoder&, VariableEnvironment&, const CachedFunctionExecutable&);
 
     unsigned m_firstLineOffset;
     unsigned m_lineCount;
index 343862e..3d042ef 100644 (file)
@@ -38,6 +38,12 @@ protected:
         : Base(vm, structure, codeType, info, debuggerMode)
     {
     }
+
+    template<typename CodeBlockType>
+    UnlinkedGlobalCodeBlock(Decoder& decoder, Structure* structure, const CachedCodeBlock<CodeBlockType>& cachedCodeBlock)
+        : Base(decoder, structure, cachedCodeBlock)
+    {
+    }
 };
 
 }
index 3e3e8df..f94707e 100644 (file)
@@ -35,6 +35,7 @@ class MetadataTable;
 class UnlinkedMetadataTable {
     friend class LLIntOffsetsExtractor;
     friend class MetadataTable;
+    friend class CachedMetadataTable;
 
 public:
     struct LinkingData {
index 0e9c5f5..3933bfd 100644 (file)
@@ -29,6 +29,9 @@
 
 namespace JSC {
 
+class Decoder;
+class CachedModuleCodeBlock;
+
 class UnlinkedModuleProgramCodeBlock final : public UnlinkedGlobalCodeBlock {
 public:
     typedef UnlinkedGlobalCodeBlock Base;
@@ -74,11 +77,15 @@ public:
     }
 
 private:
+    friend CachedModuleCodeBlock;
+
     UnlinkedModuleProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info, DebuggerMode debuggerMode)
         : Base(vm, structure, ModuleCode, info, debuggerMode)
     {
     }
 
+    UnlinkedModuleProgramCodeBlock(Decoder&, const CachedModuleCodeBlock&);
+
     int m_moduleEnvironmentSymbolTableConstantRegisterOffset { 0 };
 
 public:
index afe26ae..76844a3 100644 (file)
@@ -29,6 +29,8 @@
 
 namespace JSC {
 
+class CachedProgramCodeBlock;
+
 class UnlinkedProgramCodeBlock final : public UnlinkedGlobalCodeBlock {
 public:
     typedef UnlinkedGlobalCodeBlock Base;
@@ -50,11 +52,15 @@ public:
     const VariableEnvironment& lexicalDeclarations() const { return m_lexicalDeclarations; }
 
 private:
+    friend CachedProgramCodeBlock;
+
     UnlinkedProgramCodeBlock(VM* vm, Structure* structure, const ExecutableInfo& info, DebuggerMode debuggerMode)
         : Base(vm, structure, GlobalCode, info, debuggerMode)
     {
     }
 
+    UnlinkedProgramCodeBlock(Decoder&, const CachedProgramCodeBlock&);
+
     VariableEnvironment m_varDeclarations;
     VariableEnvironment m_lexicalDeclarations;
 
index ad65361..8e82560 100644 (file)
@@ -35,6 +35,7 @@
 #include "CallFrameClosure.h"
 #include "CatchScope.h"
 #include "CodeBlock.h"
+#include "CodeCache.h"
 #include "DirectArguments.h"
 #include "Heap.h"
 #include "Debugger.h"
index b5d47dd..deba637 100644 (file)
@@ -28,6 +28,7 @@
 #include "ButterflyInlines.h"
 #include "CatchScope.h"
 #include "CodeBlock.h"
+#include "CodeCache.h"
 #include "Completion.h"
 #include "ConfigFile.h"
 #include "Disassembler.h"
@@ -1988,8 +1989,11 @@ EncodedJSValue JSC_HOST_CALL functionFailNextNewCodeBlock(ExecState* exec)
     return JSValue::encode(jsUndefined());
 }
 
-EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*)
+EncodedJSValue JSC_HOST_CALL functionQuit(ExecState* exec)
 {
+    VM& vm = exec->vm();
+    vm.codeCache()->write(vm);
+
     jscExit(EXIT_SUCCESS);
 
 #if COMPILER(MSVC)
@@ -2876,6 +2880,8 @@ int runJSC(const CommandLine& options, bool isWorker, const Func& func)
 #endif
     }
 
+    vm.codeCache()->write(vm);
+
     if (isWorker) {
         JSLockHolder locker(vm);
         // This is needed because we don't want the worker's main
index c3c0f73..fbe4c9a 100644 (file)
@@ -33,6 +33,8 @@
 namespace JSC {
 
     class SourceCode : public UnlinkedSourceCode {
+        friend class CachedSourceCode;
+
     public:
         SourceCode()
             : UnlinkedSourceCode()
index a8e603c..cc3abe2 100644 (file)
@@ -37,6 +37,8 @@ enum class TypeProfilerEnabled { No, Yes };
 enum class ControlFlowProfilerEnabled { No, Yes };
 
 class SourceCodeFlags {
+    friend class CachedSourceCodeKey;
+
 public:
     SourceCodeFlags() = default;
 
@@ -70,6 +72,8 @@ private:
 };
 
 class SourceCodeKey {
+    friend class CachedSourceCodeKey;
+
 public:
     SourceCodeKey()
     {
@@ -114,6 +118,11 @@ public:
             && string() == other.string();
     }
 
+    bool operator!=(const SourceCodeKey& other) const
+    {
+        return !(*this == other);
+    }
+
     struct Hash {
         static unsigned hash(const SourceCodeKey& key) { return key.hash(); }
         static bool equal(const SourceCodeKey& a, const SourceCodeKey& b) { return a == b; }
index 6ec590b..aaaad3f 100644 (file)
@@ -35,6 +35,9 @@
 namespace JSC {
 
     class UnlinkedSourceCode {
+        template<typename SourceType>
+        friend class CachedUnlinkedSourceCodeShape;
+
     public:
         UnlinkedSourceCode()
             : m_provider(0)
index 129b5ea..d84b2e1 100644 (file)
@@ -116,6 +116,8 @@ public:
     bool isEverythingCaptured() const { return m_isEverythingCaptured; }
 
 private:
+    friend class CachedVariableEnvironment;
+
     Map m_map;
     bool m_isEverythingCaptured { false };
 };
diff --git a/Source/JavaScriptCore/runtime/CachedTypes.cpp b/Source/JavaScriptCore/runtime/CachedTypes.cpp
new file mode 100644 (file)
index 0000000..9d9554c
--- /dev/null
@@ -0,0 +1,2021 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CachedTypes.h"
+
+#include "BytecodeLivenessAnalysis.h"
+#include "JSCast.h"
+#include "JSImmutableButterfly.h"
+#include "JSTemplateObjectDescriptor.h"
+#include "ScopedArgumentsTable.h"
+#include "SourceCodeKey.h"
+#include "UnlinkedEvalCodeBlock.h"
+#include "UnlinkedFunctionCodeBlock.h"
+#include "UnlinkedMetadataTableInlines.h"
+#include "UnlinkedModuleProgramCodeBlock.h"
+#include "UnlinkedProgramCodeBlock.h"
+#include <wtf/FastMalloc.h>
+#include <wtf/Forward.h>
+#include <wtf/Optional.h>
+#include <wtf/text/AtomicStringImpl.h>
+
+namespace JSC {
+
+template <typename T, typename = void>
+struct SourceTypeImpl {
+    using type = T;
+};
+
+template<typename T>
+struct SourceTypeImpl<T, std::enable_if_t<!std::is_fundamental<T>::value && !std::is_same<typename T::SourceType_, void>::value>> {
+    using type = typename T::SourceType_;
+
+};
+
+template<typename T>
+using SourceType = typename SourceTypeImpl<T>::type;
+
+class Encoder {
+    WTF_MAKE_NONCOPYABLE(Encoder);
+    WTF_FORBID_HEAP_ALLOCATION;
+
+public:
+    class Allocation {
+        friend class Encoder;
+
+    public:
+        uint8_t* buffer() const { return m_buffer; }
+        ptrdiff_t offset() const { return m_offset; }
+
+    private:
+        Allocation(uint8_t* buffer, ptrdiff_t offset)
+            : m_buffer(buffer)
+            , m_offset(offset)
+        {
+        }
+
+        uint8_t* m_buffer;
+        ptrdiff_t m_offset;
+    };
+
+    Encoder(VM& vm)
+        : m_vm(vm)
+        , m_baseOffset(0)
+        , m_currentPage(nullptr)
+    {
+        allocateNewPage();
+    }
+
+    VM& vm() { return m_vm; }
+
+    Allocation malloc(unsigned size)
+    {
+        ASSERT(size);
+        ptrdiff_t offset;
+        if (m_currentPage->malloc(size, offset))
+            return Allocation { m_currentPage->buffer() + offset, m_baseOffset + offset };
+        allocateNewPage(size);
+        return malloc(size);
+    }
+
+    template<typename T>
+    T* malloc()
+    {
+        return reinterpret_cast<T*>(malloc(sizeof(T)).buffer());
+    }
+
+    ptrdiff_t offsetOf(const void* address)
+    {
+        ptrdiff_t offset;
+        ptrdiff_t baseOffset = 0;
+        for (const auto& page : m_pages) {
+            if (page.getOffset(address, offset))
+                return baseOffset + offset;
+            baseOffset += page.size();
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+        return 0;
+    }
+
+    void cachePtr(const void* ptr, ptrdiff_t offset)
+    {
+        m_ptrToOffsetMap.add(ptr, offset);
+    }
+
+    WTF::Optional<ptrdiff_t> offsetForPtr(const void* ptr)
+    {
+        auto it = m_ptrToOffsetMap.find(ptr);
+        if (it == m_ptrToOffsetMap.end())
+            return WTF::nullopt;
+        return { it->value };
+    }
+
+    std::pair<MallocPtr<uint8_t>, size_t> release()
+    {
+        size_t size = m_baseOffset + m_currentPage->size();
+        MallocPtr<uint8_t> buffer = MallocPtr<uint8_t>::malloc(size);
+        unsigned offset = 0;
+        for (const auto& page : m_pages) {
+            memcpy(buffer.get() + offset, page.buffer(), page.size());
+            offset += page.size();
+        }
+        RELEASE_ASSERT(offset == size);
+        return { WTFMove(buffer), size };
+    }
+
+private:
+    class Page {
+    public:
+        Page(size_t size)
+            : m_offset(0)
+            , m_capacity(size)
+        {
+            m_buffer = MallocPtr<uint8_t>::malloc(size);
+        }
+
+        bool malloc(size_t size, ptrdiff_t& result)
+        {
+            size_t alignment = std::min(alignof(std::max_align_t), static_cast<size_t>(WTF::roundUpToPowerOfTwo(size)));
+            ptrdiff_t offset = WTF::roundUpToMultipleOf(alignment, m_offset);
+            size = WTF::roundUpToMultipleOf(alignment, size);
+            if (static_cast<size_t>(offset + size) > m_capacity)
+                return false;
+
+            result = offset;
+            m_offset = offset + size;
+            return true;
+        }
+
+        uint8_t* buffer() const { return m_buffer.get(); }
+        size_t size() const { return static_cast<size_t>(m_offset); }
+
+        bool getOffset(const void* address, ptrdiff_t& result) const
+        {
+            const uint8_t* addr = static_cast<const uint8_t*>(address);
+            if (addr >= m_buffer.get() && addr < m_buffer.get() + m_offset) {
+                result = addr - m_buffer.get();
+                return true;
+            }
+            return false;
+        }
+
+    private:
+        MallocPtr<uint8_t> m_buffer;
+        ptrdiff_t m_offset;
+        size_t m_capacity;
+    };
+
+    void allocateNewPage(size_t size = 0)
+    {
+        static size_t minPageSize = WTF::pageSize();
+        if (m_currentPage)
+            m_baseOffset += m_currentPage->size();
+        if (size < minPageSize)
+            size = minPageSize;
+        else
+            size = WTF::roundUpToMultipleOf(minPageSize, size);
+        m_pages.append(Page { size });
+        m_currentPage = &m_pages.last();
+    }
+
+    VM& m_vm;
+    ptrdiff_t m_baseOffset;
+    Page* m_currentPage;
+    Vector<Page> m_pages;
+    HashMap<const void*, ptrdiff_t> m_ptrToOffsetMap;
+};
+
+class Decoder {
+    WTF_MAKE_NONCOPYABLE(Decoder);
+    WTF_FORBID_HEAP_ALLOCATION;
+
+public:
+    Decoder(VM& vm, const void* baseAddress, size_t size)
+        : m_vm(vm)
+        , m_baseAddress(reinterpret_cast<const uint8_t*>(baseAddress))
+#ifndef NDEBUG
+        , m_size(size)
+#endif
+    {
+        UNUSED_PARAM(size);
+    }
+
+    ~Decoder()
+    {
+        for (auto& pair : m_finalizers)
+            pair.value();
+    }
+
+    VM& vm() { return m_vm; }
+
+    ptrdiff_t offsetOf(const void* ptr)
+    {
+        const uint8_t* addr = static_cast<const uint8_t*>(ptr);
+        ASSERT(addr >= m_baseAddress && addr < m_baseAddress + m_size);
+        return addr - m_baseAddress;
+    }
+
+    void cacheOffset(ptrdiff_t offset, void* ptr)
+    {
+        m_offsetToPtrMap.add(offset, ptr);
+    }
+
+    WTF::Optional<void*> ptrForOffset(ptrdiff_t offset)
+    {
+        auto it = m_offsetToPtrMap.find(offset);
+        if (it == m_offsetToPtrMap.end())
+            return WTF::nullopt;
+        return { it->value };
+    }
+
+    template<typename Functor>
+    void addFinalizer(ptrdiff_t offset, const Functor& fn)
+    {
+        m_finalizers.add(offset, fn);
+    }
+
+private:
+    VM& m_vm;
+    const uint8_t* m_baseAddress;
+#ifndef NDEBUG
+    size_t m_size;
+#endif
+    HashMap<ptrdiff_t, void*> m_offsetToPtrMap;
+    HashMap<ptrdiff_t, std::function<void()>> m_finalizers;
+};
+
+template<typename T>
+static std::enable_if_t<std::is_same<T, SourceType<T>>::value> encode(Encoder&, T& dst, const SourceType<T>& src)
+{
+    dst = src;
+}
+
+template<typename T>
+static std::enable_if_t<!std::is_same<T, SourceType<T>>::value> encode(Encoder& encoder, T& dst, const SourceType<T>& src)
+{
+    dst.encode(encoder, src);
+}
+
+template<typename T, typename... Args>
+static std::enable_if_t<std::is_same<T, SourceType<T>>::value> decode(Decoder&, const T& src, SourceType<T>& dst, Args...)
+{
+    dst = src;
+}
+
+template<typename T, typename... Args>
+static std::enable_if_t<!std::is_same<T, SourceType<T>>::value> decode(Decoder& decoder, const T& src, SourceType<T>& dst, Args... args)
+{
+    src.decode(decoder, dst, args...);
+}
+
+template<typename T>
+static std::enable_if_t<std::is_same<T, SourceType<T>>::value, T> decode(Decoder&, T src)
+{
+    return src;
+}
+
+template<typename T>
+static std::enable_if_t<!std::is_same<T, SourceType<T>>::value, SourceType<T>>&& decode(Decoder& decoder, const T& src)
+{
+    return src.decode(decoder);
+}
+
+template<typename Source>
+class CachedObject {
+    WTF_MAKE_NONCOPYABLE(CachedObject<Source>);
+    WTF_FORBID_HEAP_ALLOCATION;
+
+public:
+    using SourceType_ = Source;
+};
+
+template<typename Source>
+class VariableLengthObject : public CachedObject<Source> {
+    template<typename, typename>
+    friend struct CachedPtr;
+
+protected:
+    const uint8_t* buffer() const
+    {
+        ASSERT(m_offset != s_invalidOffset);
+        return reinterpret_cast<const uint8_t*>(this) + m_offset;
+    }
+
+    template<typename T>
+    const T* buffer() const
+    {
+        return reinterpret_cast<const T*>(buffer());
+    }
+
+    uint8_t* allocate(Encoder& encoder, size_t size)
+    {
+        if (!size)
+            return nullptr;
+
+        ptrdiff_t offsetOffset = encoder.offsetOf(&m_offset);
+        auto result = encoder.malloc(size);
+        m_offset = result.offset() - offsetOffset;
+        return result.buffer();
+    }
+
+    template<typename T>
+    T* allocate(Encoder& encoder, unsigned size = 1)
+    {
+        uint8_t* result = allocate(encoder, sizeof(T) * size);
+        return reinterpret_cast<T*>(result);
+    }
+
+private:
+    constexpr static ptrdiff_t s_invalidOffset = std::numeric_limits<ptrdiff_t>::max();
+
+    ptrdiff_t m_offset { s_invalidOffset };
+
+};
+
+template<typename T, typename Source = SourceType<T>>
+class CachedPtr : public VariableLengthObject<Source*> {
+    template<typename, typename>
+    friend struct CachedRefPtr;
+
+public:
+    void encode(Encoder& encoder, const Source* src)
+    {
+        m_isEmpty = !src;
+        if (m_isEmpty)
+            return;
+
+        if (WTF::Optional<ptrdiff_t> offset = encoder.offsetForPtr(src)) {
+            this->m_offset = *offset - encoder.offsetOf(&this->m_offset);
+            return;
+        }
+
+        T* cachedObject = this->template allocate<T>(encoder);
+        cachedObject->encode(encoder, *src);
+        encoder.cachePtr(src, encoder.offsetOf(cachedObject));
+    }
+
+    template<typename... Args>
+    Source* decode(Decoder& decoder, Args... args) const
+    {
+        if (m_isEmpty)
+            return nullptr;
+
+        ptrdiff_t bufferOffset = decoder.offsetOf(this->buffer());
+        if (WTF::Optional<void*> ptr = decoder.ptrForOffset(bufferOffset))
+            return reinterpret_cast<Source*>(*ptr);
+
+        Source* ptr = get()->decode(decoder, args...);
+        decoder.cacheOffset(bufferOffset, ptr);
+        return ptr;
+    }
+
+    const T* operator->() const { return get(); }
+
+private:
+    const T* get() const
+    {
+        if (m_isEmpty)
+            return nullptr;
+        return this->template buffer<T>();
+    }
+
+    bool m_isEmpty;
+
+};
+
+template<typename T, typename Source = SourceType<T>>
+class CachedRefPtr : public CachedObject<RefPtr<Source>> {
+public:
+    void encode(Encoder& encoder, const Source* src)
+    {
+        m_ptr.encode(encoder, src);
+    }
+
+    void encode(Encoder& encoder, const RefPtr<Source> src)
+    {
+        encode(encoder, src.get());
+    }
+
+    RefPtr<Source> decode(Decoder& decoder) const
+    {
+        Source* decodedPtr = m_ptr.decode(decoder);
+        if (!decodedPtr)
+            return nullptr;
+        decoder.addFinalizer(decoder.offsetOf(m_ptr.buffer()), [=] { derefIfNotNull(decodedPtr); });
+        refIfNotNull(decodedPtr);
+        return adoptRef(decodedPtr);
+    }
+
+    void decode(Decoder& decoder, RefPtr<Source>& src) const
+    {
+        src = decode(decoder);
+    }
+
+private:
+    CachedPtr<T, Source> m_ptr;
+};
+
+template<typename T, typename Source = SourceType<T>>
+class CachedWriteBarrier : public CachedObject<WriteBarrier<Source>> {
+public:
+    void encode(Encoder& encoder, const WriteBarrier<Source> src)
+    {
+        m_ptr.encode(encoder, src.get());
+    }
+
+    void decode(Decoder& decoder, WriteBarrier<Source>& src, const JSCell* owner) const
+    {
+        Source* decodedPtr = m_ptr.decode(decoder);
+        if (decodedPtr)
+            src.set(decoder.vm(), owner, decodedPtr);
+    }
+
+private:
+    CachedPtr<T, Source> m_ptr;
+};
+
+template<typename T, size_t InlineCapacity = 0, typename OverflowHandler = CrashOnOverflow>
+class CachedVector : public VariableLengthObject<Vector<SourceType<T>, InlineCapacity, OverflowHandler>> {
+public:
+    void encode(Encoder& encoder, const Vector<SourceType<T>, InlineCapacity, OverflowHandler>& vector)
+    {
+        m_size = vector.size();
+        T* buffer = this->template allocate<T>(encoder, m_size);
+        for (unsigned i = 0; i < m_size; ++i)
+            ::JSC::encode(encoder, buffer[i], vector[i]);
+    }
+
+    template<typename... Args>
+    void decode(Decoder& decoder, Vector<SourceType<T>, InlineCapacity, OverflowHandler>& vector, Args... args) const
+    {
+        vector.resizeToFit(m_size);
+        const T* buffer = this->template buffer<T>();
+        for (unsigned i = 0; i < m_size; ++i)
+            ::JSC::decode(decoder, buffer[i], vector[i], args...);
+    }
+
+private:
+    unsigned m_size;
+};
+
+template<typename First, typename Second>
+class CachedPair : public CachedObject<std::pair<SourceType<First>, SourceType<Second>>> {
+public:
+    void encode(Encoder& encoder, const std::pair<SourceType<First>, SourceType<Second>>& pair)
+    {
+        ::JSC::encode(encoder, m_first, pair.first);
+        ::JSC::encode(encoder, m_second, pair.second);
+    }
+
+    void decode(Decoder& decoder, std::pair<SourceType<First>, SourceType<Second>>& pair) const
+    {
+        ::JSC::decode(decoder, m_first, pair.first);
+        ::JSC::decode(decoder, m_second, pair.second);
+    }
+
+private:
+    First m_first;
+    Second m_second;
+};
+
+template<typename Key, typename Value, typename HashArg = typename DefaultHash<SourceType<Key>>::Hash, typename KeyTraitsArg = HashTraits<SourceType<Key>>, typename MappedTraitsArg = HashTraits<SourceType<Value>>>
+class CachedHashMap : public VariableLengthObject<HashMap<SourceType<Key>, SourceType<Value>, HashArg, KeyTraitsArg, MappedTraitsArg>> {
+    template<typename K, typename V>
+    using Map = HashMap<K, V, HashArg, KeyTraitsArg, MappedTraitsArg>;
+
+public:
+    void encode(Encoder& encoder, const Map<SourceType<Key>, SourceType<Value>>& map)
+    {
+        SourceType<decltype(m_entries)> entriesVector(map.size());
+        unsigned i = 0;
+        for (const auto& it : map)
+            entriesVector[i++] = { it.key, it.value };
+        m_entries.encode(encoder, entriesVector);
+    }
+
+    void decode(Decoder& decoder, Map<SourceType<Key>, SourceType<Value>>& map) const
+    {
+        SourceType<decltype(m_entries)> decodedEntries;
+        m_entries.decode(decoder, decodedEntries);
+        for (const auto& pair : decodedEntries)
+            map.set(pair.first, pair.second);
+    }
+
+private:
+    CachedVector<CachedPair<Key, Value>> m_entries;
+};
+
+class CachedUniquedStringImpl : public VariableLengthObject<UniquedStringImpl> {
+public:
+    void encode(Encoder& encoder, const StringImpl& string)
+    {
+        m_isAtomic = string.isAtomic();
+        m_isSymbol = string.isSymbol();
+        StringImpl* impl = const_cast<StringImpl*>(&string);
+        if (m_isSymbol) {
+            SymbolImpl* symbol = static_cast<SymbolImpl*>(impl);
+            if (!symbol->isNullSymbol()) {
+                Identifier uid = Identifier::fromUid(&encoder.vm(), symbol);
+                impl = encoder.vm().propertyNames->lookUpPublicName(uid).string().impl();
+            }
+        }
+
+        m_is8Bit = impl->is8Bit();
+        m_length = impl->length();
+
+        if (!m_length)
+            return;
+
+        unsigned size = m_length;
+        const void* payload;
+        if (m_is8Bit)
+            payload = impl->characters8();
+        else {
+            payload = impl->characters16();
+            size *= 2;
+        }
+
+        uint8_t* buffer = this->allocate(encoder, size);
+        memcpy(buffer, payload, size);
+    }
+
+    UniquedStringImpl* decode(Decoder& decoder) const
+    {
+        auto create = [&](const auto* buffer) -> UniquedStringImpl* {
+            if (!m_isSymbol)
+                return AtomicStringImpl::add(buffer, m_length).leakRef();
+
+            if (!m_length)
+                return &SymbolImpl::createNullSymbol().leakRef();
+
+            Identifier ident = Identifier::fromString(&decoder.vm(), buffer, m_length);
+            String str = decoder.vm().propertyNames->lookUpPrivateName(ident)->string();
+            StringImpl* impl = str.releaseImpl().get();
+            ASSERT(impl->isSymbol());
+            return static_cast<UniquedStringImpl*>(impl);
+        };
+
+        if (m_is8Bit)
+            return create(this->buffer<LChar>());
+        return create(this->buffer<UChar>());
+    }
+
+private:
+    bool m_is8Bit : 1;
+    bool m_isSymbol : 1;
+    bool m_isAtomic : 1;
+    unsigned m_length;
+};
+
+class CachedStringImpl : public VariableLengthObject<StringImpl> {
+public:
+    void encode(Encoder& encoder, const StringImpl& impl)
+    {
+        m_uniquedStringImpl.encode(encoder, impl);
+    }
+
+    StringImpl* decode(Decoder& decoder) const
+    {
+        return m_uniquedStringImpl.decode(decoder);
+    }
+
+private:
+    CachedUniquedStringImpl m_uniquedStringImpl;
+};
+
+class CachedString : public VariableLengthObject<String> {
+public:
+    void encode(Encoder& encoder, const String& string)
+    {
+        m_impl.encode(encoder, static_cast<UniquedStringImpl*>(string.impl()));
+    }
+
+    String decode(Decoder& decoder) const
+    {
+        return String(static_cast<RefPtr<StringImpl>>(m_impl.decode(decoder)));
+    }
+
+    void decode(Decoder& decoder, String& dst) const
+    {
+        dst = decode(decoder);
+    }
+
+private:
+    CachedRefPtr<CachedUniquedStringImpl> m_impl;
+};
+
+class CachedIdentifier : public VariableLengthObject<Identifier> {
+public:
+    void encode(Encoder& encoder, const Identifier& identifier)
+    {
+        m_string.encode(encoder, identifier.string());
+    }
+
+    Identifier decode(Decoder& decoder) const
+    {
+        String str = m_string.decode(decoder);
+        if (str.isNull())
+            return Identifier();
+
+        return Identifier::fromUid(&decoder.vm(), (UniquedStringImpl*)str.impl());
+    }
+
+    void decode(Decoder& decoder, Identifier& ident) const
+    {
+        ident = decode(decoder);
+    }
+
+private:
+    CachedString m_string;
+};
+
+template<typename T>
+class CachedOptional : public VariableLengthObject<WTF::Optional<SourceType<T>>> {
+public:
+    void encode(Encoder& encoder, const WTF::Optional<SourceType<T>>& source)
+    {
+        m_isEmpty = !source;
+
+        if (m_isEmpty)
+            return;
+
+        this->template allocate<T>(encoder)->encode(encoder, *source);
+    }
+
+    WTF::Optional<SourceType<T>> decode(Decoder& decoder) const
+    {
+        if (m_isEmpty)
+            return WTF::nullopt;
+
+        return { this->template buffer<T>()->decode(decoder) };
+    }
+
+    void decode(Decoder& decoder, WTF::Optional<SourceType<T>>& dst) const
+    {
+        dst = decode(decoder);
+    }
+
+    void encode(Encoder& encoder, const std::unique_ptr<SourceType<T>>& source)
+    {
+        if (!source)
+            encode(encoder, WTF::nullopt);
+        else
+            encode(encoder, { *source });
+    }
+
+    SourceType<T>* decodeAsPtr(Decoder& decoder) const
+    {
+        if (m_isEmpty)
+            return nullptr;
+
+        return this->template buffer<T>()->decode(decoder);
+    }
+
+private:
+    bool m_isEmpty;
+};
+
+class CachedSimpleJumpTable : public CachedObject<UnlinkedSimpleJumpTable> {
+public:
+    void encode(Encoder& encoder, const UnlinkedSimpleJumpTable& jumpTable)
+    {
+        m_min = jumpTable.min;
+        m_branchOffsets.encode(encoder, jumpTable.branchOffsets);
+    }
+
+    void decode(Decoder& decoder, UnlinkedSimpleJumpTable& jumpTable) const
+    {
+        jumpTable.min = m_min;
+        m_branchOffsets.decode(decoder, jumpTable.branchOffsets);
+    }
+
+private:
+    int32_t m_min;
+    CachedVector<int32_t> m_branchOffsets;
+};
+
+class CachedStringJumpTable : public CachedObject<UnlinkedStringJumpTable> {
+public:
+    void encode(Encoder& encoder, const UnlinkedStringJumpTable& jumpTable)
+    {
+        m_offsetTable.encode(encoder, jumpTable.offsetTable);
+    }
+
+    void decode(Decoder& decoder, UnlinkedStringJumpTable& jumpTable) const
+    {
+        m_offsetTable.decode(decoder, jumpTable.offsetTable);
+    }
+
+private:
+    CachedHashMap<CachedRefPtr<CachedStringImpl>, UnlinkedStringJumpTable:: OffsetLocation> m_offsetTable;
+};
+
+class CachedCodeBlockRareData : public CachedObject<UnlinkedCodeBlock::RareData> {
+public:
+    void encode(Encoder& encoder, const UnlinkedCodeBlock::RareData& rareData)
+    {
+        m_exceptionHandlers.encode(encoder, rareData.m_exceptionHandlers);
+        m_switchJumpTables.encode(encoder, rareData.m_switchJumpTables);
+        m_stringSwitchJumpTables.encode(encoder, rareData.m_stringSwitchJumpTables);
+        m_expressionInfoFatPositions.encode(encoder, rareData.m_expressionInfoFatPositions);
+        m_typeProfilerInfoMap.encode(encoder, rareData.m_typeProfilerInfoMap);
+        m_opProfileControlFlowBytecodeOffsets.encode(encoder, rareData.m_opProfileControlFlowBytecodeOffsets);
+    }
+
+    UnlinkedCodeBlock::RareData* decode(Decoder& decoder) const
+    {
+        UnlinkedCodeBlock::RareData* rareData = new UnlinkedCodeBlock::RareData { };
+        m_exceptionHandlers.decode(decoder, rareData->m_exceptionHandlers);
+        m_switchJumpTables.decode(decoder, rareData->m_switchJumpTables);
+        m_stringSwitchJumpTables.decode(decoder, rareData->m_stringSwitchJumpTables);
+        m_expressionInfoFatPositions.decode(decoder, rareData->m_expressionInfoFatPositions);
+        m_typeProfilerInfoMap.decode(decoder, rareData->m_typeProfilerInfoMap);
+        m_opProfileControlFlowBytecodeOffsets.decode(decoder, rareData->m_opProfileControlFlowBytecodeOffsets);
+        return rareData;
+    }
+
+private:
+    CachedVector<UnlinkedHandlerInfo> m_exceptionHandlers;
+    CachedVector<CachedSimpleJumpTable> m_switchJumpTables;
+    CachedVector<CachedStringJumpTable> m_stringSwitchJumpTables;
+    CachedVector<ExpressionRangeInfo::FatPosition> m_expressionInfoFatPositions;
+    CachedHashMap<unsigned, UnlinkedCodeBlock::RareData::TypeProfilerExpressionRange> m_typeProfilerInfoMap;
+    CachedVector<InstructionStream::Offset> m_opProfileControlFlowBytecodeOffsets;
+};
+
+class CachedBitVector : public VariableLengthObject<BitVector> {
+public:
+    void encode(Encoder& encoder, const BitVector& bitVector)
+    {
+        m_size = bitVector.size();
+        uint8_t* buffer = this->allocate(encoder, m_size);
+        memcpy(buffer, bitVector.bits(), m_size);
+    }
+
+    void decode(Decoder&, BitVector& bitVector) const
+    {
+        bitVector.ensureSize(m_size);
+        memcpy(bitVector.bits(), this->buffer(), m_size);
+    }
+
+private:
+    unsigned m_size;
+};
+
+template<typename T, typename HashArg = typename DefaultHash<T>::Hash>
+class CachedHashSet : public CachedObject<HashSet<SourceType<T>, HashArg>> {
+public:
+    void encode(Encoder& encoder, const HashSet<SourceType<T>, HashArg>& set)
+    {
+        SourceType<decltype(m_entries)> entriesVector(set.size());
+        unsigned i = 0;
+        for (const auto& item : set)
+            entriesVector[i++] = item;
+        m_entries.encode(encoder, entriesVector);
+    }
+
+    void decode(Decoder& decoder, HashSet<SourceType<T>, HashArg>& set) const
+    {
+        SourceType<decltype(m_entries)> entriesVector;
+        m_entries.decode(decoder, entriesVector);
+        for (const auto& item : entriesVector)
+            set.add(item);
+    }
+
+private:
+    CachedVector<T> m_entries;
+};
+
+class CachedConstantIdentifierSetEntry : public VariableLengthObject<ConstantIdentifierSetEntry> {
+public:
+    void encode(Encoder& encoder, const ConstantIdentifierSetEntry& entry)
+    {
+        m_constant = entry.second;
+        m_set.encode(encoder, entry.first);
+    }
+
+    void decode(Decoder& decoder, ConstantIdentifierSetEntry& entry) const
+    {
+        entry.second = m_constant;
+        m_set.decode(decoder, entry.first);
+    }
+
+private:
+    unsigned m_constant;
+    CachedHashSet<CachedRefPtr<CachedUniquedStringImpl>, IdentifierRepHash> m_set;
+};
+
+class CachedVariableEnvironment : public CachedObject<VariableEnvironment> {
+public:
+    void encode(Encoder& encoder, const VariableEnvironment& env)
+    {
+        m_isEverythingCaptured = env.m_isEverythingCaptured;
+        m_map.encode(encoder, env.m_map);
+    }
+
+    void decode(Decoder& decoder, VariableEnvironment& env) const
+    {
+        env.m_isEverythingCaptured = m_isEverythingCaptured;
+        m_map.decode(decoder, env.m_map);
+    }
+
+private:
+    bool m_isEverythingCaptured;
+    CachedHashMap<CachedRefPtr<CachedUniquedStringImpl>, VariableEnvironmentEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, VariableEnvironmentEntryHashTraits> m_map;
+};
+
+template<typename T, typename Source = SourceType<T>>
+class CachedArray : public VariableLengthObject<Source*> {
+public:
+    void encode(Encoder& encoder, const Source* array, unsigned size)
+    {
+        T* dst = this->template allocate<T>(encoder, size);
+        for (unsigned i = 0; i < size; ++i)
+            ::JSC::encode(encoder, dst[i], array[i]);
+    }
+
+    template<typename... Args>
+    void decode(Decoder& decoder, Source* array, unsigned size, Args... args) const
+    {
+        const T* buffer = this->template buffer<T>();
+        for (unsigned i = 0; i < size; ++i)
+            ::JSC::decode(decoder, buffer[i], array[i], args...);
+    }
+};
+
+class CachedScopedArgumentsTable : public CachedObject<ScopedArgumentsTable> {
+public:
+    void encode(Encoder& encoder, const ScopedArgumentsTable& scopedArgumentsTable)
+    {
+        m_length = scopedArgumentsTable.m_length;
+        m_arguments.encode(encoder, scopedArgumentsTable.m_arguments.get(), m_length);
+    }
+
+    ScopedArgumentsTable* decode(Decoder& decoder) const
+    {
+        ScopedArgumentsTable* scopedArgumentsTable = ScopedArgumentsTable::create(decoder.vm(), m_length);
+        m_arguments.decode(decoder, scopedArgumentsTable->m_arguments.get(), m_length);
+        return scopedArgumentsTable;
+    }
+
+private:
+    uint32_t m_length;
+    CachedArray<ScopeOffset> m_arguments;
+};
+
+class CachedSymbolTableEntry : public CachedObject<SymbolTableEntry> {
+public:
+    void encode(Encoder&, const SymbolTableEntry& symbolTableEntry)
+    {
+        m_bits = symbolTableEntry.m_bits | SymbolTableEntry::SlimFlag;
+    }
+
+    void decode(Decoder&, SymbolTableEntry& symbolTableEntry) const
+    {
+        symbolTableEntry.m_bits = m_bits;
+    }
+
+private:
+    intptr_t m_bits;
+};
+
+class CachedSymbolTable : public CachedObject<SymbolTable> {
+public:
+    void encode(Encoder& encoder, const SymbolTable& symbolTable)
+    {
+        ASSERT(!symbolTable.m_rareData);
+        m_map.encode(encoder, symbolTable.m_map);
+        m_maxScopeOffset = symbolTable.m_maxScopeOffset;
+        m_usesNonStrictEval = symbolTable.m_usesNonStrictEval;
+        m_nestedLexicalScope = symbolTable.m_nestedLexicalScope;
+        m_scopeType = symbolTable.m_scopeType;
+        m_arguments.encode(encoder, symbolTable.m_arguments.get());
+    }
+
+    SymbolTable* decode(Decoder& decoder) const
+    {
+        SymbolTable* symbolTable = SymbolTable::create(decoder.vm());
+        m_map.decode(decoder, symbolTable->m_map);
+        symbolTable->m_maxScopeOffset = m_maxScopeOffset;
+        symbolTable->m_usesNonStrictEval = m_usesNonStrictEval;
+        symbolTable->m_nestedLexicalScope = m_nestedLexicalScope;
+        symbolTable->m_scopeType = m_scopeType;
+        ScopedArgumentsTable* scopedArgumentsTable = m_arguments.decode(decoder);
+        if (scopedArgumentsTable)
+            symbolTable->m_arguments.set(decoder.vm(), symbolTable, scopedArgumentsTable);
+        return symbolTable;
+    }
+
+private:
+    CachedHashMap<CachedRefPtr<CachedUniquedStringImpl>, CachedSymbolTableEntry, IdentifierRepHash, HashTraits<RefPtr<UniquedStringImpl>>, SymbolTableIndexHashTraits> m_map;
+    ScopeOffset m_maxScopeOffset;
+    unsigned m_usesNonStrictEval : 1;
+    unsigned m_nestedLexicalScope : 1;
+    unsigned m_scopeType : 3;
+    // FIXME: do we need to cached this eventually?
+    // CachedPtr<SymbolTableRareData> rareData;
+    CachedPtr<CachedScopedArgumentsTable> m_arguments;
+};
+
+class CachedJSValue;
+class CachedImmutableButterfly : public CachedObject<JSImmutableButterfly> {
+public:
+    void encode(Encoder& encoder, JSImmutableButterfly& immutableButterfly)
+    {
+        m_length = immutableButterfly.length();
+        m_indexingType = immutableButterfly.indexingTypeAndMisc();
+        if (hasDouble(m_indexingType))
+            m_cachedDoubles.encode(encoder, immutableButterfly.toButterfly()->contiguousDouble().data(), m_length);
+        else
+            m_cachedValues.encode(encoder, immutableButterfly.toButterfly()->contiguous().data(), m_length);
+    }
+
+    JSImmutableButterfly* decode(Decoder& decoder) const
+    {
+        JSImmutableButterfly* immutableButterfly = JSImmutableButterfly::create(decoder.vm(), m_indexingType, m_length);
+        if (hasDouble(m_indexingType))
+            m_cachedDoubles.decode(decoder, immutableButterfly->toButterfly()->contiguousDouble().data(), m_length, immutableButterfly);
+        else
+            m_cachedValues.decode(decoder, immutableButterfly->toButterfly()->contiguous().data(), m_length, immutableButterfly);
+        return immutableButterfly;
+    }
+
+private:
+    IndexingType m_indexingType;
+    unsigned m_length;
+    union {
+        CachedArray<double> m_cachedDoubles;
+        CachedArray<CachedJSValue, WriteBarrier<Unknown>> m_cachedValues;
+    };
+};
+
+class CachedRegExp : public CachedObject<RegExp> {
+public:
+    void encode(Encoder& encoder, const RegExp& regExp)
+    {
+        m_patternString.encode(encoder, regExp.m_patternString);
+        m_flags = regExp.m_flags;
+    }
+
+    RegExp* decode(Decoder& decoder) const
+    {
+        String pattern { m_patternString.decode(decoder) };
+        return RegExp::create(decoder.vm(), pattern, m_flags);
+    }
+
+private:
+    CachedString m_patternString;
+    RegExpFlags m_flags;
+};
+
+class CachedTemplateObjectDescriptor : public CachedObject<TemplateObjectDescriptor> {
+public:
+    void encode(Encoder& encoder, const TemplateObjectDescriptor& templateObjectDescriptor)
+    {
+        m_rawStrings.encode(encoder, templateObjectDescriptor.rawStrings());
+        m_cookedStrings.encode(encoder, templateObjectDescriptor.cookedStrings());
+    }
+
+    Ref<TemplateObjectDescriptor> decode(Decoder& decoder) const
+    {
+        TemplateObjectDescriptor::StringVector decodedRawStrings;
+        TemplateObjectDescriptor::OptionalStringVector decodedCookedStrings;
+        m_rawStrings.decode(decoder, decodedRawStrings);
+        m_cookedStrings.decode(decoder, decodedCookedStrings);
+        return TemplateObjectDescriptor::create(WTFMove(decodedRawStrings), WTFMove(decodedCookedStrings));
+    }
+
+private:
+    CachedVector<CachedString, 4> m_rawStrings;
+    CachedVector<CachedOptional<CachedString>, 4> m_cookedStrings;
+};
+
+class CachedBigInt : public VariableLengthObject<JSBigInt> {
+public:
+    void encode(Encoder& encoder, JSBigInt& bigInt)
+    {
+        m_length = bigInt.length();
+        m_sign = bigInt.sign();
+
+        if (!m_length)
+            return;
+
+        unsigned size = sizeof(JSBigInt::Digit) * m_length;
+        uint8_t* buffer = this->allocate(encoder, size);
+        memcpy(buffer, bigInt.dataStorage(), size);
+    }
+
+    JSBigInt* decode(Decoder& decoder) const
+    {
+        JSBigInt* bigInt = JSBigInt::createWithLengthUnchecked(decoder.vm(), m_length);
+        bigInt->setSign(m_sign);
+        memcpy(bigInt->dataStorage(), this->buffer(), sizeof(JSBigInt::Digit) * m_length);
+        return bigInt;
+    }
+
+private:
+    unsigned m_length;
+    bool m_sign;
+};
+
+class CachedJSValue : public VariableLengthObject<WriteBarrier<Unknown>> {
+public:
+    void encode(Encoder& encoder, const WriteBarrier<Unknown> value)
+    {
+        JSValue v = value.get();
+
+        if (!v.isCell() || v.isEmpty()) {
+            m_type = EncodedType::JSValue;
+            *this->allocate<EncodedJSValue>(encoder) = JSValue::encode(v);
+            return;
+        }
+
+        JSCell* cell = v.asCell();
+        VM& vm = encoder.vm();
+
+        if (auto* symbolTable = jsDynamicCast<SymbolTable*>(vm, cell)) {
+            m_type = EncodedType::SymbolTable;
+            this->allocate<CachedSymbolTable>(encoder)->encode(encoder, *symbolTable);
+            return;
+        }
+
+        if (auto* string = jsDynamicCast<JSString*>(vm, cell)) {
+            m_type = EncodedType::String;
+            StringImpl* impl = string->tryGetValue().impl();
+            this->allocate<CachedUniquedStringImpl>(encoder)->encode(encoder, *impl);
+            return;
+        }
+
+        if (auto* immutableButterfly = jsDynamicCast<JSImmutableButterfly*>(vm, cell)) {
+            m_type = EncodedType::ImmutableButterfly;
+            this->allocate<CachedImmutableButterfly>(encoder)->encode(encoder, *immutableButterfly);
+            return;
+        }
+
+        if (auto* regexp = jsDynamicCast<RegExp*>(vm, cell)) {
+            m_type = EncodedType::RegExp;
+            this->allocate<CachedRegExp>(encoder)->encode(encoder, *regexp);
+            return;
+        }
+
+        if (auto* templateObjectDescriptor = jsDynamicCast<JSTemplateObjectDescriptor*>(vm, cell)) {
+            m_type = EncodedType::TemplateObjectDescriptor;
+            this->allocate<CachedTemplateObjectDescriptor>(encoder)->encode(encoder, templateObjectDescriptor->descriptor());
+            return;
+        }
+
+        if (auto* bigInt = jsDynamicCast<JSBigInt*>(vm, cell)) {
+            m_type = EncodedType::BigInt;
+            this->allocate<CachedBigInt>(encoder)->encode(encoder, *bigInt);
+            return;
+        }
+
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    void decode(Decoder& decoder, WriteBarrier<Unknown>& value, const JSCell* owner) const
+    {
+        JSValue v;
+        switch (m_type) {
+        case EncodedType::JSValue:
+            v = JSValue::decode(*this->buffer<EncodedJSValue>());
+            break;
+        case EncodedType::SymbolTable:
+            v = this->buffer<CachedSymbolTable>()->decode(decoder);
+            break;
+        case EncodedType::String: {
+            UniquedStringImpl* impl = this->buffer<CachedUniquedStringImpl>()->decode(decoder);
+            v = JSString::create(decoder.vm(), adoptRef(*impl));
+            break;
+        }
+        case EncodedType::ImmutableButterfly:
+            v = this->buffer<CachedImmutableButterfly>()->decode(decoder);
+            break;
+        case EncodedType::RegExp:
+            v = this->buffer<CachedRegExp>()->decode(decoder);
+            break;
+        case EncodedType::TemplateObjectDescriptor:
+            v = JSTemplateObjectDescriptor::create(decoder.vm(), this->buffer<CachedTemplateObjectDescriptor>()->decode(decoder));
+            break;
+        case EncodedType::BigInt:
+            v = this->buffer<CachedBigInt>()->decode(decoder);
+            break;
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+        value.set(decoder.vm(), owner, v);
+    }
+
+private:
+    enum class EncodedType : uint8_t {
+        JSValue,
+        SymbolTable,
+        String,
+        ImmutableButterfly,
+        RegExp,
+        TemplateObjectDescriptor,
+        BigInt,
+    };
+
+    EncodedType m_type;
+};
+
+class CachedInstructionStream : public CachedObject<InstructionStream> {
+public:
+    void encode(Encoder& encoder, const InstructionStream& stream)
+    {
+        m_instructions.encode(encoder, stream.m_instructions);
+    }
+
+    InstructionStream* decode(Decoder& decoder) const
+    {
+        Vector<uint8_t, 0, UnsafeVectorOverflow> instructionsVector;
+        m_instructions.decode(decoder, instructionsVector);
+        return new InstructionStream(WTFMove(instructionsVector));
+    }
+
+private:
+    CachedVector<uint8_t, 0, UnsafeVectorOverflow> m_instructions;
+};
+
+class CachedMetadataTable : public CachedObject<UnlinkedMetadataTable> {
+public:
+    void encode(Encoder&, const UnlinkedMetadataTable& metadataTable)
+    {
+        ASSERT(metadataTable.m_isFinalized);
+        m_hasMetadata = metadataTable.m_hasMetadata;
+        if (!m_hasMetadata)
+            return;
+        for (unsigned i = UnlinkedMetadataTable::s_offsetTableEntries; i--;)
+            m_metadata[i] = metadataTable.buffer()[i];
+    }
+
+    void decode(Decoder&, UnlinkedMetadataTable& metadataTable) const
+    {
+        metadataTable.m_isFinalized = true;
+        metadataTable.m_isLinked = false;
+        metadataTable.m_hasMetadata = m_hasMetadata;
+        for (unsigned i = UnlinkedMetadataTable::s_offsetTableEntries; i--;)
+            metadataTable.buffer()[i] = m_metadata[i];
+    }
+
+private:
+    bool m_hasMetadata;
+    std::array<unsigned, UnlinkedMetadataTable::s_offsetTableEntries> m_metadata;
+};
+
+class CachedSourceOrigin : public CachedObject<SourceOrigin> {
+public:
+    void encode(Encoder& encoder, const SourceOrigin& sourceOrigin)
+    {
+        m_string.encode(encoder, sourceOrigin.string());
+    }
+
+    SourceOrigin decode(Decoder& decoder) const
+    {
+        return SourceOrigin { m_string.decode(decoder) };
+    }
+
+private:
+    CachedString m_string;
+};
+
+class CachedTextPosition : public CachedObject<TextPosition> {
+public:
+    void encode(Encoder&, TextPosition textPosition)
+    {
+        m_line = textPosition.m_line.zeroBasedInt();
+        m_column = textPosition.m_column.zeroBasedInt();
+    }
+
+    TextPosition decode(Decoder&) const
+    {
+        return TextPosition { OrdinalNumber::fromZeroBasedInt(m_line), OrdinalNumber::fromZeroBasedInt(m_column) };
+    }
+
+private:
+    int m_line;
+    int m_column;
+};
+
+template <typename Source, typename CachedType>
+class CachedSourceProviderShape : public CachedObject<Source> {
+public:
+    void encode(Encoder& encoder, const SourceProvider& sourceProvider)
+    {
+        m_sourceOrigin.encode(encoder, sourceProvider.sourceOrigin());
+        m_url.encode(encoder, sourceProvider.url());
+        m_sourceURLDirective.encode(encoder, sourceProvider.sourceURL());
+        m_sourceMappingURLDirective.encode(encoder, sourceProvider.sourceMappingURL());
+        m_startPosition.encode(encoder, sourceProvider.startPosition());
+    }
+
+    void decode(Decoder& decoder, SourceProvider& sourceProvider) const
+    {
+        sourceProvider.setSourceURLDirective(m_sourceURLDirective.decode(decoder));
+        sourceProvider.setSourceMappingURLDirective(m_sourceMappingURLDirective.decode(decoder));
+    }
+
+protected:
+    CachedSourceOrigin m_sourceOrigin;
+    CachedString m_url;
+    CachedString m_sourceURLDirective;
+    CachedString m_sourceMappingURLDirective;
+    CachedTextPosition m_startPosition;
+};
+
+class CachedStringSourceProvider : public CachedSourceProviderShape<StringSourceProvider, CachedStringSourceProvider> {
+    using Base = CachedSourceProviderShape<StringSourceProvider, CachedStringSourceProvider>;
+
+public:
+    void encode(Encoder& encoder, const StringSourceProvider& sourceProvider)
+    {
+        Base::encode(encoder, sourceProvider);
+        m_source.encode(encoder, sourceProvider.source().toString());
+    }
+
+    StringSourceProvider* decode(Decoder& decoder, SourceProviderSourceType sourceType) const
+    {
+        String decodedSource = m_source.decode(decoder);
+        SourceOrigin decodedSourceOrigin = m_sourceOrigin.decode(decoder);
+        String decodedURL = m_url.decode(decoder);
+        TextPosition decodedStartPosition = m_startPosition.decode(decoder);
+
+        Ref<StringSourceProvider> sourceProvider = StringSourceProvider::create(decodedSource, decodedSourceOrigin, URL(URL(), decodedURL), decodedStartPosition, sourceType);
+        Base::decode(decoder, sourceProvider.get());
+        return &sourceProvider.leakRef();
+    }
+
+private:
+    CachedString m_source;
+};
+
+#if ENABLE(WEBASSEMBLY)
+class CachedWebAssemblySourceProvider : public CachedSourceProviderShape<WebAssemblySourceProvider, CachedWebAssemblySourceProvider> {
+    using Base = CachedSourceProviderShape<WebAssemblySourceProvider, CachedWebAssemblySourceProvider>;
+
+public:
+    void encode(Encoder& encoder, const WebAssemblySourceProvider& sourceProvider)
+    {
+        Base::encode(encoder, sourceProvider);
+        m_data.encode(encoder, sourceProvider.data());
+    }
+
+    WebAssemblySourceProvider* decode(Decoder& decoder) const
+    {
+        Vector<uint8_t> decodedData;
+        SourceOrigin decodedSourceOrigin = m_sourceOrigin.decode(decoder);
+        String decodedURL = m_url.decode(decoder);
+
+        m_data.decode(decoder, decodedData);
+
+        Ref<WebAssemblySourceProvider> sourceProvider = WebAssemblySourceProvider::create(WTFMove(decodedData), decodedSourceOrigin, URL(URL(), decodedURL));
+        Base::decode(decoder, sourceProvider.get());
+
+        return &sourceProvider.leakRef();
+    }
+
+private:
+    CachedVector<uint8_t> m_data;
+};
+#endif
+
+class CachedSourceProvider : public VariableLengthObject<SourceProvider> {
+public:
+    void encode(Encoder& encoder, const SourceProvider& sourceProvider)
+    {
+        m_sourceType = sourceProvider.sourceType();
+        switch (m_sourceType) {
+        case SourceProviderSourceType::Program:
+        case SourceProviderSourceType::Module:
+            this->allocate<CachedStringSourceProvider>(encoder)->encode(encoder, reinterpret_cast<const StringSourceProvider&>(sourceProvider));
+            break;
+#if ENABLE(WEBASSEMBLY)
+        case SourceProviderSourceType::WebAssembly:
+            this->allocate<CachedWebAssemblySourceProvider>(encoder)->encode(encoder, reinterpret_cast<const WebAssemblySourceProvider&>(sourceProvider));
+            break;
+#endif
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+    }
+
+    SourceProvider* decode(Decoder& decoder) const
+    {
+        switch (m_sourceType) {
+        case SourceProviderSourceType::Program:
+        case SourceProviderSourceType::Module:
+            return this->buffer<CachedStringSourceProvider>()->decode(decoder, m_sourceType);
+#if ENABLE(WEBASSEMBLY)
+        case SourceProviderSourceType::WebAssembly:
+            return this->buffer<CachedWebAssemblySourceProvider>()->decode(decoder);
+#endif
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+        }
+    }
+
+private:
+    SourceProviderSourceType m_sourceType;
+};
+
+template<typename Source>
+class CachedUnlinkedSourceCodeShape : public CachedObject<Source> {
+public:
+    void encode(Encoder& encoder, const UnlinkedSourceCode& sourceCode)
+    {
+        m_provider.encode(encoder, sourceCode.m_provider.get());
+        m_startOffset = sourceCode.startOffset();
+        m_endOffset = sourceCode.endOffset();
+    }
+
+    void decode(Decoder& decoder, UnlinkedSourceCode& sourceCode) const
+    {
+        sourceCode.m_provider = m_provider.decode(decoder);
+        sourceCode.m_startOffset = m_startOffset;
+        sourceCode.m_endOffset = m_endOffset;
+    }
+
+private:
+    CachedPtr<CachedSourceProvider> m_provider;
+    int m_startOffset;
+    int m_endOffset;
+};
+
+
+class CachedUnlinkedSourceCode : public CachedUnlinkedSourceCodeShape<UnlinkedSourceCode> { };
+
+class CachedSourceCode : public CachedUnlinkedSourceCodeShape<SourceCode> {
+    using Base = CachedUnlinkedSourceCodeShape<SourceCode>;
+
+public:
+    void encode(Encoder& encoder, const SourceCode& sourceCode)
+    {
+        Base::encode(encoder, sourceCode);
+        m_firstLine = sourceCode.firstLine().zeroBasedInt();
+        m_startColumn = sourceCode.startColumn().zeroBasedInt();
+    }
+
+    void decode(Decoder& decoder, SourceCode& sourceCode) const
+    {
+        Base::decode(decoder, sourceCode);
+        sourceCode.m_firstLine = OrdinalNumber::fromZeroBasedInt(m_firstLine);
+        sourceCode.m_startColumn = OrdinalNumber::fromZeroBasedInt(m_startColumn);
+    }
+
+private:
+    int m_firstLine;
+    int m_startColumn;
+};
+
+class CachedFunctionExecutable : public CachedObject<UnlinkedFunctionExecutable> {
+public:
+    void encode(Encoder&, const UnlinkedFunctionExecutable&);
+    UnlinkedFunctionExecutable* decode(Decoder&) const;
+
+    unsigned firstLineOffset() const { return m_firstLineOffset; }
+    unsigned lineCount() const { return m_lineCount; }
+    unsigned unlinkedFunctionNameStart() const { return m_unlinkedFunctionNameStart; }
+    unsigned unlinkedBodyStartColumn() const { return m_unlinkedBodyStartColumn; }
+    unsigned unlinkedBodyEndColumn() const { return m_unlinkedBodyEndColumn; }
+    unsigned startOffset() const { return m_startOffset; }
+    unsigned sourceLength() const { return m_sourceLength; }
+    unsigned parametersStartOffset() const { return m_parametersStartOffset; }
+    unsigned typeProfilingStartOffset() const { return m_typeProfilingStartOffset; }
+    unsigned typeProfilingEndOffset() const { return m_typeProfilingEndOffset; }
+    unsigned parameterCount() const { return m_parameterCount; }
+
+    CodeFeatures features() const { return m_features; }
+    SourceParseMode sourceParseMode() const { return m_sourceParseMode; }
+
+    unsigned isInStrictContext() const { return m_isInStrictContext; }
+    unsigned hasCapturedVariables() const { return m_hasCapturedVariables; }
+    unsigned isBuiltinFunction() const { return m_isBuiltinFunction; }
+    unsigned isBuiltinDefaultClassConstructor() const { return m_isBuiltinDefaultClassConstructor; }
+    unsigned constructAbility() const { return m_constructAbility; }
+    unsigned constructorKind() const { return m_constructorKind; }
+    unsigned functionMode() const { return m_functionMode; }
+    unsigned scriptMode() const { return m_scriptMode; }
+    unsigned superBinding() const { return m_superBinding; }
+    unsigned derivedContextType() const { return m_derivedContextType; }
+
+    Identifier name(Decoder& decoder) const { return m_name.decode(decoder); }
+    Identifier ecmaName(Decoder& decoder) const { return m_ecmaName.decode(decoder); }
+    Identifier inferredName(Decoder& decoder) const { return m_inferredName.decode(decoder); }
+
+private:
+    unsigned m_firstLineOffset;
+    unsigned m_lineCount;
+    unsigned m_unlinkedFunctionNameStart;
+    unsigned m_unlinkedBodyStartColumn;
+    unsigned m_unlinkedBodyEndColumn;
+    unsigned m_startOffset;
+    unsigned m_sourceLength;
+    unsigned m_parametersStartOffset;
+    unsigned m_typeProfilingStartOffset;
+    unsigned m_typeProfilingEndOffset;
+    unsigned m_parameterCount;
+    CodeFeatures m_features;
+    SourceParseMode m_sourceParseMode;
+    unsigned m_isInStrictContext : 1;
+    unsigned m_hasCapturedVariables : 1;
+    unsigned m_isBuiltinFunction : 1;
+    unsigned m_isBuiltinDefaultClassConstructor : 1;
+    unsigned m_constructAbility: 1;
+    unsigned m_constructorKind : 2;
+    unsigned m_functionMode : 2; // FunctionMode
+    unsigned m_scriptMode: 1; // JSParserScriptMode
+    unsigned m_superBinding : 1;
+    unsigned m_derivedContextType: 2;
+
+    CachedSourceCode m_classSource;
+
+    CachedIdentifier m_name;
+    CachedIdentifier m_ecmaName;
+    CachedIdentifier m_inferredName;
+
+    CachedVariableEnvironment m_parentScopeTDZVariables;
+
+    CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock> m_unlinkedCodeBlockForCall;
+    CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock> m_unlinkedCodeBlockForConstruct;
+};
+
+template<typename CodeBlockType>
+class CachedCodeBlock : public CachedObject<CodeBlockType> {
+public:
+    void encode(Encoder&, const UnlinkedCodeBlock&);
+    void decode(Decoder&, UnlinkedCodeBlock&) const;
+
+    InstructionStream* instructions(Decoder& decoder) const { return m_instructions.decode(decoder); }
+
+    VirtualRegister thisRegister() const { return m_thisRegister; }
+    VirtualRegister scopeRegister() const { return m_scopeRegister; }
+    VirtualRegister globalObjectRegister() const { return m_globalObjectRegister; }
+
+    String sourceURLDirective(Decoder& decoder) const { return m_sourceURLDirective.decode(decoder); }
+    String sourceMappingURLDirective(Decoder& decoder) const { return m_sourceMappingURLDirective.decode(decoder); }
+
+    unsigned usesEval() const { return m_usesEval; }
+    unsigned isStrictMode() const { return m_isStrictMode; }
+    unsigned isConstructor() const { return m_isConstructor; }
+    unsigned hasCapturedVariables() const { return m_hasCapturedVariables; }
+    unsigned isBuiltinFunction() const { return m_isBuiltinFunction; }
+    unsigned superBinding() const { return m_superBinding; }
+    unsigned scriptMode() const { return m_scriptMode; }
+    unsigned isArrowFunctionContext() const { return m_isArrowFunctionContext; }
+    unsigned isClassContext() const { return m_isClassContext; }
+    unsigned wasCompiledWithDebuggingOpcodes() const { return m_wasCompiledWithDebuggingOpcodes; }
+    unsigned constructorKind() const { return m_constructorKind; }
+    unsigned derivedContextType() const { return m_derivedContextType; }
+    unsigned evalContextType() const { return m_evalContextType; }
+    unsigned hasTailCalls() const { return m_hasTailCalls; }
+    unsigned lineCount() const { return m_lineCount; }
+    unsigned endColumn() const { return m_endColumn; }
+
+    int numVars() const { return m_numVars; }
+    int numCalleeLocals() const { return m_numCalleeLocals; }
+    int numParameters() const { return m_numParameters; }
+
+    CodeFeatures features() const { return m_features; }
+    SourceParseMode parseMode() const { return m_parseMode; }
+    CodeType codeType() const { return m_codeType; }
+
+    UnlinkedCodeBlock::RareData* rareData(Decoder& decoder) const { return m_rareData.decodeAsPtr(decoder); }
+
+private:
+    VirtualRegister m_thisRegister;
+    VirtualRegister m_scopeRegister;
+    VirtualRegister m_globalObjectRegister;
+
+    unsigned m_usesEval : 1;
+    unsigned m_isStrictMode : 1;
+    unsigned m_isConstructor : 1;
+    unsigned m_hasCapturedVariables : 1;
+    unsigned m_isBuiltinFunction : 1;
+    unsigned m_superBinding : 1;
+    unsigned m_scriptMode: 1;
+    unsigned m_isArrowFunctionContext : 1;
+    unsigned m_isClassContext : 1;
+    unsigned m_wasCompiledWithDebuggingOpcodes : 1;
+    unsigned m_constructorKind : 2;
+    unsigned m_derivedContextType : 2;
+    unsigned m_evalContextType : 2;
+    unsigned m_hasTailCalls : 1;
+
+    unsigned m_lineCount;
+    unsigned m_endColumn;
+
+    int m_numVars;
+    int m_numCalleeLocals;
+    int m_numParameters;
+
+    CodeFeatures m_features;
+    SourceParseMode m_parseMode;
+    CodeType m_codeType;
+
+    std::array<unsigned, LinkTimeConstantCount> m_linkTimeConstants;
+    CachedMetadataTable m_metadata;
+
+    CachedOptional<CachedCodeBlockRareData> m_rareData;
+
+    CachedString m_sourceURLDirective;
+    CachedString m_sourceMappingURLDirective;
+
+    CachedPtr<CachedInstructionStream> m_instructions;
+    CachedVector<InstructionStream::Offset> m_jumpTargets;
+    CachedVector<InstructionStream::Offset> m_propertyAccessInstructions;
+    CachedVector<CachedJSValue> m_constantRegisters;
+    CachedVector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation;
+    CachedVector<ExpressionRangeInfo> m_expressionInfo;
+    CachedHashMap<InstructionStream::Offset, int> m_outOfLineJumpTargets;
+
+    CachedVector<CachedConstantIdentifierSetEntry> m_constantIdentifierSets;
+    CachedVector<CachedIdentifier> m_identifiers;
+    CachedVector<CachedBitVector> m_bitVectors;
+    CachedVector<CachedWriteBarrier<CachedFunctionExecutable>> m_functionDecls;
+    CachedVector<CachedWriteBarrier<CachedFunctionExecutable>> m_functionExprs;
+};
+
+class CachedProgramCodeBlock : public CachedCodeBlock<UnlinkedProgramCodeBlock> {
+    using Base = CachedCodeBlock<UnlinkedProgramCodeBlock>;
+
+public:
+    void encode(Encoder& encoder, const UnlinkedProgramCodeBlock& codeBlock)
+    {
+        Base::encode(encoder, codeBlock);
+        m_varDeclarations.encode(encoder, codeBlock.m_varDeclarations);
+        m_lexicalDeclarations.encode(encoder, codeBlock.m_lexicalDeclarations);
+    }
+
+    UnlinkedProgramCodeBlock* decode(Decoder& decoder) const
+    {
+        UnlinkedProgramCodeBlock* codeBlock = new (NotNull, allocateCell<UnlinkedProgramCodeBlock>(decoder.vm().heap)) UnlinkedProgramCodeBlock(decoder, *this);
+        codeBlock->finishCreation(decoder.vm());
+        Base::decode(decoder, *codeBlock);
+        m_varDeclarations.decode(decoder, codeBlock->m_varDeclarations);
+        m_lexicalDeclarations.decode(decoder, codeBlock->m_lexicalDeclarations);
+        return codeBlock;
+    }
+
+private:
+    CachedVariableEnvironment m_varDeclarations;
+    CachedVariableEnvironment m_lexicalDeclarations;
+};
+
+class CachedModuleCodeBlock : public CachedCodeBlock<UnlinkedModuleProgramCodeBlock> {
+    using Base = CachedCodeBlock<UnlinkedModuleProgramCodeBlock>;
+
+public:
+    void encode(Encoder& encoder, const UnlinkedModuleProgramCodeBlock& codeBlock)
+    {
+        Base::encode(encoder, codeBlock);
+        m_moduleEnvironmentSymbolTableConstantRegisterOffset = codeBlock.m_moduleEnvironmentSymbolTableConstantRegisterOffset;
+    }
+
+    UnlinkedModuleProgramCodeBlock* decode(Decoder& decoder) const
+    {
+        UnlinkedModuleProgramCodeBlock* codeBlock = new (NotNull, allocateCell<UnlinkedModuleProgramCodeBlock>(decoder.vm().heap)) UnlinkedModuleProgramCodeBlock(decoder, *this);
+        codeBlock->finishCreation(decoder.vm());
+        Base::decode(decoder, *codeBlock);
+        codeBlock->m_moduleEnvironmentSymbolTableConstantRegisterOffset = m_moduleEnvironmentSymbolTableConstantRegisterOffset;
+        return codeBlock;
+    }
+
+private:
+    int m_moduleEnvironmentSymbolTableConstantRegisterOffset;
+};
+
+class CachedEvalCodeBlock : public CachedCodeBlock<UnlinkedEvalCodeBlock> {
+    using Base = CachedCodeBlock<UnlinkedEvalCodeBlock>;
+
+public:
+    void encode(Encoder& encoder, const UnlinkedEvalCodeBlock& codeBlock)
+    {
+        Base::encode(encoder, codeBlock);
+        m_variables.encode(encoder, codeBlock.m_variables);
+        m_functionHoistingCandidates.encode(encoder, codeBlock.m_functionHoistingCandidates);
+    }
+
+    UnlinkedEvalCodeBlock* decode(Decoder& decoder) const
+    {
+        UnlinkedEvalCodeBlock* codeBlock = new (NotNull, allocateCell<UnlinkedEvalCodeBlock>(decoder.vm().heap)) UnlinkedEvalCodeBlock(decoder, *this);
+        codeBlock->finishCreation(decoder.vm());
+        Base::decode(decoder, *codeBlock);
+        m_variables.decode(decoder, codeBlock->m_variables);
+        m_functionHoistingCandidates.decode(decoder, codeBlock->m_functionHoistingCandidates);
+        return codeBlock;
+    }
+
+private:
+    CachedVector<CachedIdentifier, 0, UnsafeVectorOverflow> m_variables;
+    CachedVector<CachedIdentifier, 0, UnsafeVectorOverflow> m_functionHoistingCandidates;
+};
+
+class CachedFunctionCodeBlock : public CachedCodeBlock<UnlinkedFunctionCodeBlock> {
+    using Base = CachedCodeBlock<UnlinkedFunctionCodeBlock>;
+
+public:
+    void encode(Encoder& encoder, const UnlinkedFunctionCodeBlock& codeBlock)
+    {
+        Base::encode(encoder, codeBlock);
+    }
+
+    UnlinkedFunctionCodeBlock* decode(Decoder& decoder) const
+    {
+        UnlinkedFunctionCodeBlock* codeBlock = new (NotNull, allocateCell<UnlinkedFunctionCodeBlock>(decoder.vm().heap)) UnlinkedFunctionCodeBlock(decoder, *this);
+        codeBlock->finishCreation(decoder.vm());
+        Base::decode(decoder, *codeBlock);
+        return codeBlock;
+    }
+};
+
+ALWAYS_INLINE UnlinkedFunctionCodeBlock::UnlinkedFunctionCodeBlock(Decoder& decoder, const CachedFunctionCodeBlock& cachedCodeBlock)
+    : Base(decoder, decoder.vm().unlinkedFunctionCodeBlockStructure.get(), cachedCodeBlock)
+{
+}
+
+template<typename T>
+struct CachedCodeBlockTypeImpl;
+
+enum CachedCodeBlockTag {
+    CachedProgramCodeBlockTag,
+    CachedModuleCodeBlockTag,
+    CachedEvalCodeBlockTag,
+};
+
+template<>
+struct CachedCodeBlockTypeImpl<UnlinkedProgramCodeBlock> {
+    using type = CachedProgramCodeBlock;
+    static constexpr CachedCodeBlockTag tag = CachedProgramCodeBlockTag;
+};
+
+template<>
+struct CachedCodeBlockTypeImpl<UnlinkedModuleProgramCodeBlock> {
+    using type = CachedModuleCodeBlock;
+    static constexpr CachedCodeBlockTag tag = CachedModuleCodeBlockTag;
+};
+
+template<>
+struct CachedCodeBlockTypeImpl<UnlinkedEvalCodeBlock> {
+    using type = CachedEvalCodeBlock;
+    static constexpr CachedCodeBlockTag tag = CachedEvalCodeBlockTag;
+};
+
+template<typename T>
+using CachedCodeBlockType = typename CachedCodeBlockTypeImpl<T>::type;
+
+template<typename CodeBlockType>
+ALWAYS_INLINE UnlinkedCodeBlock::UnlinkedCodeBlock(Decoder& decoder, Structure* structure, const CachedCodeBlock<CodeBlockType>& cachedCodeBlock)
+    : Base(decoder.vm(), structure)
+    , m_instructions(cachedCodeBlock.instructions(decoder))
+    , m_liveness(nullptr)
+    , m_thisRegister(cachedCodeBlock.thisRegister())
+    , m_scopeRegister(cachedCodeBlock.scopeRegister())
+    , m_globalObjectRegister(cachedCodeBlock.globalObjectRegister())
+
+    , m_sourceURLDirective(cachedCodeBlock.sourceURLDirective(decoder))
+    , m_sourceMappingURLDirective(cachedCodeBlock.sourceMappingURLDirective(decoder))
+
+    , m_usesEval(cachedCodeBlock.usesEval())
+    , m_isStrictMode(cachedCodeBlock.isStrictMode())
+    , m_isConstructor(cachedCodeBlock.isConstructor())
+    , m_hasCapturedVariables(cachedCodeBlock.hasCapturedVariables())
+    , m_isBuiltinFunction(cachedCodeBlock.isBuiltinFunction())
+    , m_superBinding(cachedCodeBlock.superBinding())
+    , m_scriptMode(cachedCodeBlock.scriptMode())
+    , m_isArrowFunctionContext(cachedCodeBlock.isArrowFunctionContext())
+    , m_isClassContext(cachedCodeBlock.isClassContext())
+    , m_wasCompiledWithDebuggingOpcodes(cachedCodeBlock.wasCompiledWithDebuggingOpcodes())
+    , m_constructorKind(cachedCodeBlock.constructorKind())
+    , m_derivedContextType(cachedCodeBlock.derivedContextType())
+    , m_evalContextType(cachedCodeBlock.evalContextType())
+    , m_hasTailCalls(cachedCodeBlock.hasTailCalls())
+    , m_lineCount(cachedCodeBlock.lineCount())
+    , m_endColumn(cachedCodeBlock.endColumn())
+    , m_numVars(cachedCodeBlock.numVars())
+    , m_numCalleeLocals(cachedCodeBlock.numCalleeLocals())
+    , m_numParameters(cachedCodeBlock.numParameters())
+    , m_features(cachedCodeBlock.features())
+    , m_parseMode(cachedCodeBlock.parseMode())
+    , m_codeType(cachedCodeBlock.codeType())
+    , m_rareData(cachedCodeBlock.rareData(decoder))
+{
+}
+
+template<typename CodeBlockType>
+ALWAYS_INLINE void CachedCodeBlock<CodeBlockType>::decode(Decoder& decoder, UnlinkedCodeBlock& codeBlock) const
+{
+    for (unsigned i = LinkTimeConstantCount; i--;)
+        codeBlock.m_linkTimeConstants[i] = m_linkTimeConstants[i];
+
+    m_metadata.decode(decoder, codeBlock.m_metadata);
+    m_propertyAccessInstructions.decode(decoder, codeBlock.m_propertyAccessInstructions);
+    m_constantRegisters.decode(decoder, codeBlock.m_constantRegisters, &codeBlock);
+    m_constantsSourceCodeRepresentation.decode(decoder, codeBlock.m_constantsSourceCodeRepresentation);
+    m_expressionInfo.decode(decoder, codeBlock.m_expressionInfo);
+    m_outOfLineJumpTargets.decode(decoder, codeBlock.m_outOfLineJumpTargets);
+    m_jumpTargets.decode(decoder, codeBlock.m_jumpTargets);
+    m_constantIdentifierSets.decode(decoder, codeBlock.m_constantIdentifierSets);
+    m_identifiers.decode(decoder, codeBlock.m_identifiers);
+    m_bitVectors.decode(decoder, codeBlock.m_bitVectors);
+    m_functionDecls.decode(decoder, codeBlock.m_functionDecls, &codeBlock);
+    m_functionExprs.decode(decoder, codeBlock.m_functionExprs, &codeBlock);
+}
+
+ALWAYS_INLINE UnlinkedProgramCodeBlock::UnlinkedProgramCodeBlock(Decoder& decoder, const CachedProgramCodeBlock& cachedCodeBlock)
+    : Base(decoder, decoder.vm().unlinkedProgramCodeBlockStructure.get(), cachedCodeBlock)
+{
+}
+
+ALWAYS_INLINE UnlinkedModuleProgramCodeBlock::UnlinkedModuleProgramCodeBlock(Decoder& decoder, const CachedModuleCodeBlock& cachedCodeBlock)
+    : Base(decoder, decoder.vm().unlinkedModuleProgramCodeBlockStructure.get(), cachedCodeBlock)
+{
+}
+
+ALWAYS_INLINE UnlinkedEvalCodeBlock::UnlinkedEvalCodeBlock(Decoder& decoder, const CachedEvalCodeBlock& cachedCodeBlock)
+    : Base(decoder, decoder.vm().unlinkedEvalCodeBlockStructure.get(), cachedCodeBlock)
+{
+}
+
+ALWAYS_INLINE void CachedFunctionExecutable::encode(Encoder& encoder, const UnlinkedFunctionExecutable& executable)
+{
+    m_firstLineOffset = executable.m_firstLineOffset;
+    m_lineCount = executable.m_lineCount;
+    m_unlinkedFunctionNameStart = executable.m_unlinkedFunctionNameStart;
+    m_unlinkedBodyStartColumn = executable.m_unlinkedBodyStartColumn;
+    m_unlinkedBodyEndColumn = executable.m_unlinkedBodyEndColumn;
+    m_startOffset = executable.m_startOffset;
+    m_sourceLength = executable.m_sourceLength;
+    m_parametersStartOffset = executable.m_parametersStartOffset;
+    m_typeProfilingStartOffset = executable.m_typeProfilingStartOffset;
+    m_typeProfilingEndOffset = executable.m_typeProfilingEndOffset;
+    m_parameterCount = executable.m_parameterCount;
+
+    m_features = executable.m_features;
+    m_sourceParseMode = executable.m_sourceParseMode;
+
+    m_isInStrictContext = executable.m_isInStrictContext;
+    m_hasCapturedVariables = executable.m_hasCapturedVariables;
+    m_isBuiltinFunction = executable.m_isBuiltinFunction;
+    m_isBuiltinDefaultClassConstructor = executable.m_isBuiltinDefaultClassConstructor;
+    m_constructAbility = executable.m_constructAbility;
+    m_constructorKind = executable.m_constructorKind;
+    m_functionMode = executable.m_functionMode;
+    m_scriptMode = executable.m_scriptMode;
+    m_superBinding = executable.m_superBinding;
+    m_derivedContextType = executable.m_derivedContextType;
+
+    m_classSource.encode(encoder, executable.m_classSource);
+
+    m_name.encode(encoder, executable.name());
+    m_ecmaName.encode(encoder, executable.ecmaName());
+    m_inferredName.encode(encoder, executable.inferredName());
+
+    m_parentScopeTDZVariables.encode(encoder, executable.parentScopeTDZVariables());
+
+    m_unlinkedCodeBlockForCall.encode(encoder, executable.m_unlinkedCodeBlockForCall);
+    m_unlinkedCodeBlockForConstruct.encode(encoder, executable.m_unlinkedCodeBlockForConstruct);
+}
+
+ALWAYS_INLINE UnlinkedFunctionExecutable* CachedFunctionExecutable::decode(Decoder& decoder) const
+{
+    VariableEnvironment env;
+    m_parentScopeTDZVariables.decode(decoder, env);
+
+    UnlinkedFunctionExecutable* executable = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(decoder.vm().heap)) UnlinkedFunctionExecutable(decoder, env, *this);
+    executable->finishCreation(decoder.vm());
+
+    m_classSource.decode(decoder, executable->m_classSource);
+    m_unlinkedCodeBlockForCall.decode(decoder, executable->m_unlinkedCodeBlockForCall, executable);
+    m_unlinkedCodeBlockForConstruct.decode(decoder, executable->m_unlinkedCodeBlockForConstruct, executable);
+
+    return executable;
+}
+
+ALWAYS_INLINE UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(Decoder& decoder, VariableEnvironment& parentScopeTDZVariables, const CachedFunctionExecutable& cachedExecutable)
+    : Base(decoder.vm(), decoder.vm().unlinkedFunctionExecutableStructure.get())
+    , m_firstLineOffset(cachedExecutable.firstLineOffset())
+    , m_lineCount(cachedExecutable.lineCount())
+    , m_unlinkedFunctionNameStart(cachedExecutable.unlinkedFunctionNameStart())
+    , m_unlinkedBodyStartColumn(cachedExecutable.unlinkedBodyStartColumn())
+    , m_unlinkedBodyEndColumn(cachedExecutable.unlinkedBodyEndColumn())
+    , m_startOffset(cachedExecutable.startOffset())
+    , m_sourceLength(cachedExecutable.sourceLength())
+    , m_parametersStartOffset(cachedExecutable.parametersStartOffset())
+    , m_typeProfilingStartOffset(cachedExecutable.typeProfilingStartOffset())
+    , m_typeProfilingEndOffset(cachedExecutable.typeProfilingEndOffset())
+    , m_parameterCount(cachedExecutable.parameterCount())
+    , m_features(cachedExecutable.features())
+    , m_sourceParseMode(cachedExecutable.sourceParseMode())
+    , m_isInStrictContext(cachedExecutable.isInStrictContext())
+    , m_hasCapturedVariables(cachedExecutable.hasCapturedVariables())
+    , m_isBuiltinFunction(cachedExecutable.isBuiltinFunction())
+    , m_isBuiltinDefaultClassConstructor(cachedExecutable.isBuiltinDefaultClassConstructor())
+    , m_constructAbility(cachedExecutable.constructAbility())
+    , m_constructorKind(cachedExecutable.constructorKind())
+    , m_functionMode(cachedExecutable.functionMode())
+    , m_scriptMode(cachedExecutable.scriptMode())
+    , m_superBinding(cachedExecutable.superBinding())
+    , m_derivedContextType(cachedExecutable.derivedContextType())
+
+    , m_name(cachedExecutable.name(decoder))
+    , m_ecmaName(cachedExecutable.ecmaName(decoder))
+    , m_inferredName(cachedExecutable.inferredName(decoder))
+
+    , m_parentScopeTDZVariables(decoder.vm().m_compactVariableMap->get(parentScopeTDZVariables))
+{
+}
+
+template<typename CodeBlockType>
+ALWAYS_INLINE void CachedCodeBlock<CodeBlockType>::encode(Encoder& encoder, const UnlinkedCodeBlock& codeBlock)
+{
+    m_thisRegister = codeBlock.m_thisRegister;
+    m_scopeRegister = codeBlock.m_scopeRegister;
+    m_globalObjectRegister = codeBlock.m_globalObjectRegister;
+    m_usesEval = codeBlock.m_usesEval;
+    m_isStrictMode = codeBlock.m_isStrictMode;
+    m_isConstructor = codeBlock.m_isConstructor;
+    m_hasCapturedVariables = codeBlock.m_hasCapturedVariables;
+    m_isBuiltinFunction = codeBlock.m_isBuiltinFunction;
+    m_superBinding = codeBlock.m_superBinding;
+    m_scriptMode = codeBlock.m_scriptMode;
+    m_isArrowFunctionContext = codeBlock.m_isArrowFunctionContext;
+    m_isClassContext = codeBlock.m_isClassContext;
+    m_wasCompiledWithDebuggingOpcodes = codeBlock.m_wasCompiledWithDebuggingOpcodes;
+    m_constructorKind = codeBlock.m_constructorKind;
+    m_derivedContextType = codeBlock.m_derivedContextType;
+    m_evalContextType = codeBlock.m_evalContextType;
+    m_hasTailCalls = codeBlock.m_hasTailCalls;
+    m_lineCount = codeBlock.m_lineCount;
+    m_endColumn = codeBlock.m_endColumn;
+    m_numVars = codeBlock.m_numVars;
+    m_numCalleeLocals = codeBlock.m_numCalleeLocals;
+    m_numParameters = codeBlock.m_numParameters;
+    m_features = codeBlock.m_features;
+    m_parseMode = codeBlock.m_parseMode;
+    m_codeType = codeBlock.m_codeType;
+
+    for (unsigned i = LinkTimeConstantCount; i--;)
+        m_linkTimeConstants[i] = codeBlock.m_linkTimeConstants[i];
+
+    m_metadata.encode(encoder, codeBlock.m_metadata);
+    m_rareData.encode(encoder, codeBlock.m_rareData);
+
+    m_sourceURLDirective.encode(encoder, codeBlock.m_sourceURLDirective.impl());
+    m_sourceMappingURLDirective.encode(encoder, codeBlock.m_sourceURLDirective.impl());
+
+    m_instructions.encode(encoder, codeBlock.m_instructions.get());
+    m_propertyAccessInstructions.encode(encoder, codeBlock.m_propertyAccessInstructions);
+    m_constantRegisters.encode(encoder, codeBlock.m_constantRegisters);
+    m_constantsSourceCodeRepresentation.encode(encoder, codeBlock.m_constantsSourceCodeRepresentation);
+    m_expressionInfo.encode(encoder, codeBlock.m_expressionInfo);
+    m_jumpTargets.encode(encoder, codeBlock.m_jumpTargets);
+    m_outOfLineJumpTargets.encode(encoder, codeBlock.m_outOfLineJumpTargets);
+
+    m_constantIdentifierSets.encode(encoder, codeBlock.m_constantIdentifierSets);
+    m_identifiers.encode(encoder, codeBlock.m_identifiers);
+    m_bitVectors.encode(encoder, codeBlock.m_bitVectors);
+    m_functionDecls.encode(encoder, codeBlock.m_functionDecls);
+    m_functionExprs.encode(encoder, codeBlock.m_functionExprs);
+}
+
+class CachedSourceCodeKey : public CachedObject<SourceCodeKey> {
+public:
+    void encode(Encoder& encoder, const SourceCodeKey& key)
+    {
+        m_sourceCode.encode(encoder, key.m_sourceCode);
+        m_name.encode(encoder, key.m_name);
+        m_flags = key.m_flags.m_flags;
+        m_hash = key.hash();
+        m_functionConstructorParametersEndPosition = key.m_functionConstructorParametersEndPosition;
+    }
+
+    void decode(Decoder& decoder, SourceCodeKey& key) const
+    {
+        m_sourceCode.decode(decoder, key.m_sourceCode);
+        m_name.decode(decoder, key.m_name);
+        key.m_flags.m_flags = m_flags;
+        key.m_hash = m_hash;
+        key.m_functionConstructorParametersEndPosition = m_functionConstructorParametersEndPosition;
+    }
+
+private:
+    CachedUnlinkedSourceCode m_sourceCode;
+    CachedString m_name;
+    unsigned m_flags;
+    unsigned m_hash;
+    int m_functionConstructorParametersEndPosition;
+};
+
+class GenericCacheEntry {
+public:
+    std::pair<SourceCodeKey, UnlinkedCodeBlock*> decode(Decoder&) const;
+
+protected:
+    CachedCodeBlockTag m_tag;
+};
+
+template<typename UnlinkedCodeBlockType>
+class CacheEntry : public GenericCacheEntry {
+public:
+    void encode(Encoder& encoder, std::pair<SourceCodeKey, const UnlinkedCodeBlockType*> pair)
+    {
+        m_tag = CachedCodeBlockTypeImpl<UnlinkedCodeBlockType>::tag;
+        m_key.encode(encoder, pair.first);
+        m_codeBlock.encode(encoder, pair.second);
+    }
+
+private:
+    friend GenericCacheEntry;
+
+    std::pair<SourceCodeKey, UnlinkedCodeBlockType*> decode(Decoder& decoder) const
+    {
+        ASSERT(m_tag == CachedCodeBlockTypeImpl<UnlinkedCodeBlockType>::tag);
+        SourceCodeKey decodedKey;
+        m_key.decode(decoder, decodedKey);
+        return { WTFMove(decodedKey), m_codeBlock.decode(decoder) };
+    }
+
+    CachedSourceCodeKey m_key;
+    CachedPtr<CachedCodeBlockType<UnlinkedCodeBlockType>> m_codeBlock;
+};
+
+std::pair<SourceCodeKey, UnlinkedCodeBlock*> GenericCacheEntry::decode(Decoder& decoder) const
+{
+    switch (m_tag) {
+    case CachedProgramCodeBlockTag:
+        return reinterpret_cast<const CacheEntry<UnlinkedProgramCodeBlock>*>(this)->decode(decoder);
+    case CachedModuleCodeBlockTag:
+        return reinterpret_cast<const CacheEntry<UnlinkedModuleProgramCodeBlock>*>(this)->decode(decoder);
+    case CachedEvalCodeBlockTag:
+        // We do not cache eval code blocks
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+#if COMPILER(MSVC)
+    // Without this, MSVC will complain that this path does not return a value.
+    return reinterpret_cast<const CacheEntry<UnlinkedEvalCodeBlock>*>(this)->decode(decoder);
+#endif
+}
+
+template<typename UnlinkedCodeBlockType>
+void encodeCodeBlock(Encoder& encoder, const SourceCodeKey& key, const UnlinkedCodeBlock* codeBlock)
+{
+    auto* entry = encoder.template malloc<CacheEntry<UnlinkedCodeBlockType>>();
+    entry->encode(encoder,  { key, jsCast<const UnlinkedCodeBlockType*>(codeBlock) });
+}
+
+std::pair<MallocPtr<uint8_t>, size_t> encodeCodeBlock(VM& vm, const SourceCodeKey& key, const UnlinkedCodeBlock* codeBlock)
+{
+    const ClassInfo* classInfo = codeBlock->classInfo(vm);
+
+    Encoder encoder(vm);
+    if (classInfo == UnlinkedProgramCodeBlock::info())
+        encodeCodeBlock<UnlinkedProgramCodeBlock>(encoder, key, codeBlock);
+    else if (classInfo == UnlinkedModuleProgramCodeBlock::info())
+        encodeCodeBlock<UnlinkedModuleProgramCodeBlock>(encoder, key, codeBlock);
+    else
+        ASSERT(classInfo == UnlinkedEvalCodeBlock::info());
+
+    return encoder.release();
+}
+
+UnlinkedCodeBlock* decodeCodeBlockImpl(VM& vm, const SourceCodeKey& key, const void* buffer, size_t size)
+{
+    const auto* cachedEntry = reinterpret_cast<const GenericCacheEntry*>(buffer);
+    Decoder decoder(vm, buffer, size);
+    std::pair<SourceCodeKey, UnlinkedCodeBlock*> entry;
+    {
+        DeferGC deferGC(vm.heap);
+        entry = cachedEntry->decode(decoder);
+    }
+
+    if (entry.first != key)
+        return nullptr;
+    return entry.second;
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/CachedTypes.h b/Source/JavaScriptCore/runtime/CachedTypes.h
new file mode 100644 (file)
index 0000000..bcb8acb
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "JSCast.h"
+#include <wtf/MallocPtr.h>
+
+namespace JSC {
+
+class SourceCodeKey;
+class UnlinkedCodeBlock;
+
+std::pair<MallocPtr<uint8_t>, size_t> encodeCodeBlock(VM&, const SourceCodeKey&, const UnlinkedCodeBlock*);
+UnlinkedCodeBlock* decodeCodeBlockImpl(VM&, const SourceCodeKey&, const void*, size_t);
+
+
+template<typename UnlinkedCodeBlockType>
+UnlinkedCodeBlockType* decodeCodeBlock(VM& vm, const SourceCodeKey& key, const void* buffer, size_t size)
+{
+    return jsCast<UnlinkedCodeBlockType*>(decodeCodeBlockImpl(vm, key, buffer, size));
+}
+
+} // namespace JSC
index 4601792..c32d204 100644 (file)
@@ -43,6 +43,9 @@ void CodeCacheMap::pruneSlowCase()
 
     while (m_size > m_capacity || !canPruneQuickly()) {
         MapType::iterator it = m_map.begin();
+
+        writeCodeBlock(*it->value.cell->vm(), it->key, it->value);
+
         m_size -= it->key.length();
         m_map.remove(it);
     }
@@ -59,9 +62,8 @@ UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableT
         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
         WTF::nullopt);
-    SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
-    if (cache && Options::useCodeCache()) {
-        UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get());
+    UnlinkedCodeBlockType* unlinkedCodeBlock = m_sourceCode.findCacheAndUpdateAge<UnlinkedCodeBlockType>(vm, key);
+    if (unlinkedCodeBlock && Options::useCodeCache()) {
         unsigned lineCount = unlinkedCodeBlock->lineCount();
         unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn().oneBasedInt();
         bool endColumnIsOnStartLine = !lineCount;
@@ -71,9 +73,9 @@ UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableT
         source.provider()->setSourceMappingURLDirective(unlinkedCodeBlock->sourceMappingURLDirective());
         return unlinkedCodeBlock;
     }
-    
+
     VariableEnvironment variablesUnderTDZ;
-    UnlinkedCodeBlockType* unlinkedCodeBlock = generateUnlinkedCodeBlock<UnlinkedCodeBlockType, ExecutableType>(vm, executable, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
+    unlinkedCodeBlock = generateUnlinkedCodeBlock<UnlinkedCodeBlockType, ExecutableType>(vm, executable, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
 
     if (unlinkedCodeBlock && Options::useCodeCache())
         m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
@@ -110,9 +112,8 @@ UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& v
         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
         functionConstructorParametersEndPosition);
-    SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
-    if (cache && Options::useCodeCache()) {
-        UnlinkedFunctionExecutable* executable = jsCast<UnlinkedFunctionExecutable*>(cache->cell.get());
+    UnlinkedFunctionExecutable* executable = m_sourceCode.findCacheAndUpdateAge<UnlinkedFunctionExecutable>(vm, key);
+    if (executable && Options::useCodeCache()) {
         source.provider()->setSourceURLDirective(executable->sourceURLDirective());
         source.provider()->setSourceMappingURLDirective(executable->sourceMappingURLDirective());
         return executable;
@@ -155,4 +156,10 @@ UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& v
     return functionExecutable;
 }
 
+void CodeCache::write(VM& vm)
+{
+    for (const auto& it : m_sourceCode)
+        writeCodeBlock(vm, it.key, it.value);
+}
+
 }
index 8a4419f..99395f2 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "BytecodeGenerator.h"
+#include "CachedTypes.h"
 #include "ExecutableInfo.h"
 #include "JSCInlines.h"
 #include "Parser.h"
@@ -37,6 +38,7 @@
 #include "UnlinkedEvalCodeBlock.h"
 #include "UnlinkedModuleProgramCodeBlock.h"
 #include "UnlinkedProgramCodeBlock.h"
+#include <sys/stat.h>
 #include <wtf/Forward.h>
 #include <wtf/text/WTFString.h>
 
@@ -89,13 +91,76 @@ public:
     {
     }
 
-    SourceCodeValue* findCacheAndUpdateAge(const SourceCodeKey& key)
+    iterator begin() { return m_map.begin(); }
+    iterator end() { return m_map.end(); }
+
+    template<typename UnlinkedCodeBlockType>
+    UnlinkedCodeBlockType* fetchFromDiskImpl(VM& vm, const SourceCodeKey& key)
+    {
+#if OS(DARWIN)
+        const char* cachePath = Options::diskCachePath();
+        if (!cachePath)
+            return nullptr;
+
+        unsigned hash = key.hash();
+        char filename[512];
+        int count = snprintf(filename, 512, "%s/%u.cache", cachePath, hash);
+        if (count < 0 || count > 512)
+            return nullptr;
+
+        int fd = open(filename, O_RDONLY);
+        if (fd == -1)
+            return nullptr;
+
+        int rc = flock(fd, LOCK_SH | LOCK_NB);
+        if (rc) {
+            close(fd);
+            return nullptr;
+        }
+
+        struct stat sb;
+        int res = fstat(fd, &sb);
+        size_t size = static_cast<size_t>(sb.st_size);
+        if (res || !size)
+            return nullptr;
+
+        const void* buffer = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
+        UnlinkedCodeBlockType* unlinkedCodeBlock = decodeCodeBlock<UnlinkedCodeBlockType>(vm, key, buffer, size);
+
+        if (!unlinkedCodeBlock)
+            return nullptr;
+
+        addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_age));
+        return unlinkedCodeBlock;
+#else
+        UNUSED_PARAM(vm);
+        UNUSED_PARAM(key);
+        return nullptr;
+#endif
+    }
+
+    template<typename UnlinkedCodeBlockType>
+    std::enable_if_t<std::is_base_of<UnlinkedCodeBlock, UnlinkedCodeBlockType>::value && !std::is_same<UnlinkedCodeBlockType, UnlinkedEvalCodeBlock>::value, UnlinkedCodeBlockType*>
+    fetchFromDisk(VM& vm, const SourceCodeKey& key)
+    {
+        UnlinkedCodeBlockType* codeBlock = fetchFromDiskImpl<UnlinkedCodeBlockType>(vm, key);
+        if (UNLIKELY(Options::forceDiskCache()))
+            RELEASE_ASSERT(codeBlock);
+        return codeBlock;
+    }
+
+    template<typename T>
+    std::enable_if_t<!std::is_base_of<UnlinkedCodeBlock, T>::value || std::is_same<T, UnlinkedEvalCodeBlock>::value, T*>
+    fetchFromDisk(VM&, const SourceCodeKey&) { return nullptr; }
+
+    template<typename UnlinkedCodeBlockType>
+    UnlinkedCodeBlockType* findCacheAndUpdateAge(VM& vm, const SourceCodeKey& key)
     {
         prune();
 
         iterator findResult = m_map.find(key);
         if (findResult == m_map.end())
-            return nullptr;
+            return fetchFromDisk<UnlinkedCodeBlockType>(vm, key);
 
         int64_t age = m_age - findResult->value.age;
         if (age > m_capacity) {
@@ -115,7 +180,7 @@ public:
         findResult->value.age = m_age;
         m_age += key.length();
 
-        return &findResult->value;
+        return jsCast<UnlinkedCodeBlockType*>(findResult->value.cell.get());
     }
 
     AddResult addCache(const SourceCodeKey& key, const SourceCodeValue& value)
@@ -197,6 +262,7 @@ public:
     UnlinkedFunctionExecutable* getUnlinkedGlobalFunctionExecutable(VM&, const Identifier&, const SourceCode&, DebuggerMode, Optional<int> functionConstructorParametersEndPosition, ParserError&);
 
     void clear() { m_sourceCode.clear(); }
+    JS_EXPORT_PRIVATE void write(VM&);
 
 private:
     template <class UnlinkedCodeBlockType, class ExecutableType> 
@@ -256,4 +322,36 @@ UnlinkedCodeBlockType* generateUnlinkedCodeBlock(VM& vm, ExecutableType* executa
     return unlinkedCodeBlock;
 }
 
+ALWAYS_INLINE static void writeCodeBlock(VM& vm, const SourceCodeKey& key, const SourceCodeValue& value)
+{
+#if OS(DARWIN)
+    const char* cachePath = Options::diskCachePath();
+    if (LIKELY(!cachePath))
+        return;
+
+    UnlinkedCodeBlock* codeBlock = jsDynamicCast<UnlinkedCodeBlock*>(vm, value.cell.get());
+    if (!codeBlock)
+        return;
+
+    unsigned hash = key.hash();
+    char filename[512];
+    int count = snprintf(filename, 512, "%s/%u.cache", cachePath, hash);
+    if (count < 0 || count > 512)
+        return;
+
+    std::pair<MallocPtr<uint8_t>, size_t> result = encodeCodeBlock(vm, key, codeBlock);
+
+    int fd = open(filename, O_CREAT | O_WRONLY, 0666);
+    int rc = flock(fd, LOCK_EX | LOCK_NB);
+    if (!rc)
+        ::write(fd, result.first.get(), result.second);
+    close(fd);
+#else
+    UNUSED_PARAM(vm);
+    UNUSED_PARAM(key);
+    UNUSED_PARAM(value);
+#endif
+}
+
+
 } // namespace JSC
index ca53163..2bcaa9b 100644 (file)
@@ -1688,11 +1688,6 @@ bool JSBigInt::getPrimitiveNumber(ExecState* exec, double& number, JSValue& resu
     return true;
 }
 
-inline size_t JSBigInt::offsetOfData()
-{
-    return WTF::roundUpToMultipleOf<sizeof(Digit)>(sizeof(JSBigInt));
-}
-
 template <typename CharType>
 JSBigInt* JSBigInt::parseInt(ExecState* exec, CharType*  data, unsigned length, ErrorParseMode errorParseMode)
 {
@@ -1795,11 +1790,6 @@ JSBigInt* JSBigInt::parseInt(ExecState* exec, VM& vm, CharType* data, unsigned l
     return nullptr;
 }
 
-inline JSBigInt::Digit* JSBigInt::dataStorage()
-{
-    return reinterpret_cast<Digit*>(reinterpret_cast<char*>(this) + offsetOfData());
-}
-
 inline JSBigInt::Digit JSBigInt::digit(unsigned n)
 {
     ASSERT(n < length());
index 2b3bf93..dbbf63d 100644 (file)
@@ -38,6 +38,7 @@ namespace JSC {
 class JSBigInt final : public JSCell {
     using Base = JSCell;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal | OverridesToThis;
+    friend class CachedBigInt;
 
 public:
 
@@ -228,8 +229,15 @@ private:
     static Optional<Digit> toShiftAmount(JSBigInt* x);
 
     static size_t allocationSize(unsigned length);
-    static size_t offsetOfData();
-    Digit* dataStorage();
+    inline static size_t offsetOfData()
+    {
+        return WTF::roundUpToMultipleOf<sizeof(Digit)>(sizeof(JSBigInt));
+    }
+
+    inline Digit* dataStorage()
+    {
+        return reinterpret_cast<Digit*>(reinterpret_cast<char*>(this) + offsetOfData());
+    }
 
     Digit digit(unsigned);
     void setDigit(unsigned, Digit);
index 9fc34fb..8b05ca8 100644 (file)
@@ -524,6 +524,12 @@ static void recomputeDependentOptions()
     // https://bugs.webkit.org/show_bug.cgi?id=177956
     Options::useProbeOSRExit() = false;
 #endif
+
+    if (!Options::useCodeCache())
+        Options::diskCachePath() = nullptr;
+
+    if (!Options::diskCachePath())
+        Options::forceDiskCache() = false;
 }
 
 void Options::initialize()
index 83aad35..03add45 100644 (file)
@@ -41,6 +41,8 @@ class VM;
 JS_EXPORT_PRIVATE RegExpFlags regExpFlags(const String&);
 
 class RegExp final : public JSCell {
+    friend class CachedRegExp;
+
 public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
index 40e7c7e..e812342 100644 (file)
@@ -39,6 +39,8 @@ namespace JSC {
 // makes sense because such modifications are so uncommon. You'd have to do something crazy like
 // "delete arguments[i]" or some variant of defineOwnProperty.
 class ScopedArgumentsTable final : public JSCell {
+    friend class CachedScopedArgumentsTable;
+
 public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
index ed982e3..5eedee6 100644 (file)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "Heap.h"
 #include "WasmIndexOrName.h"
 #include "WriteBarrier.h"
 #include <limits.h>
index 3826442..4a30f37 100644 (file)
@@ -31,6 +31,7 @@
 #include "PropertyMapHashTable.h"
 #include "Structure.h"
 #include "StructureChain.h"
+#include "StructureRareDataInlines.h"
 
 namespace JSC {
 
index 9157d5b..59691b2 100644 (file)
@@ -73,6 +73,8 @@ static ALWAYS_INLINE int missingSymbolMarker() { return std::numeric_limits<int>
 // copy:     SymbolTableEntry --> FatEntry -----^
 
 struct SymbolTableEntry {
+    friend class CachedSymbolTableEntry;
+
 private:
     static VarOffset varOffsetFromBits(intptr_t bits)
     {
@@ -436,6 +438,8 @@ struct SymbolTableIndexHashTraits : HashTraits<SymbolTableEntry> {
 };
 
 class SymbolTable final : public JSCell {
+    friend class CachedSymbolTable;
+
 public:
     typedef JSCell Base;
     static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
index 57714e1..17b2f9d 100644 (file)
@@ -1,3 +1,16 @@
+2019-01-22  Tadeu Zagallo  <tzagallo@apple.com>
+
+        Cache bytecode to disk
+        https://bugs.webkit.org/show_bug.cgi?id=192782
+        <rdar://problem/46084932>
+
+        Reviewed by Keith Miller.
+
+        BitVectors have to be friends with JSC::CacheBitVector to allow
+        serializing its buffer as part of the bytecode cache encoding.
+
+        * wtf/BitVector.h:
+
 2019-01-21  Claudio Saavedra  <csaavedra@igalia.com>
 
         [GTK] Speculative build fix for Ubuntu LTS
index 755cd7a..b8bb365 100644 (file)
 #include <wtf/PrintStream.h>
 #include <wtf/StdLibExtras.h>
 
+namespace JSC {
+class CachedBitVector;
+}
+
 namespace WTF {
 
 // This is a space-efficient, resizeable bitvector class. In the common case it
@@ -338,6 +342,8 @@ public:
     iterator end() const { return iterator(*this, size()); }
         
 private:
+    friend class JSC::CachedBitVector;
+
     static unsigned bitsInPointer()
     {
         return sizeof(void*) << 3;
index 304767c..eba7e9f 100644 (file)
@@ -1,3 +1,20 @@
+2019-01-22  Tadeu Zagallo  <tzagallo@apple.com>
+
+        Cache bytecode to disk
+        https://bugs.webkit.org/show_bug.cgi?id=192782
+        <rdar://problem/46084932>
+
+        Reviewed by Keith Miller.
+
+        Add test helper to execute bytecode-cache tests: it executes each test
+        twice, the first with JSC_diskCachePath set to a temporary directory
+        and second with JSC_forceDiskCache=true (in addition to the cache path)
+        to guarantee that only the disk cache is being used and no new
+        UnlinkedCodeBlocks are being created.
+
+        * Scripts/jsc-stress-test-helpers/bytecode-cache-test-helper.sh: Added.
+        * Scripts/run-jsc-stress-tests:
+
 2019-01-22  Oriol Brufau  <obrufau@igalia.com>
 
         [css-logical] Implement flow-relative margin, padding and border shorthands
diff --git a/Tools/Scripts/jsc-stress-test-helpers/bytecode-cache-test-helper.sh b/Tools/Scripts/jsc-stress-test-helpers/bytecode-cache-test-helper.sh
new file mode 100644 (file)
index 0000000..83e2d59
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright (C) 2019 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+set -e
+
+mysys() {
+    set +e
+    eval "$*"
+    exitCode=$?
+    if [ $exitCode != 0 ]; then
+        echo "Command '$*' failed"
+        return $exitCode
+    fi
+}
+
+pathToVM=$1
+shift
+inputFile=$1
+shift
+extraOptions=( "$@" )
+diskCachePath=$(mktemp -d -t "bytecode-cache")
+
+_trap_exit() { rm -rf "$diskCachePath"; }
+trap _trap_exit EXIT
+
+export JSC_diskCachePath=$diskCachePath
+mysys "$pathToVM" "$inputFile" "${extraOptions[@]}"
+export JSC_forceDiskCache=true
+mysys "$pathToVM" "$inputFile" "${extraOptions[@]}"
+
index 949e3f5..10b76e1 100755 (executable)
@@ -655,6 +655,15 @@ def runDefault(*optionalTestSpecificOptions)
     run("default", *(FTL_OPTIONS + optionalTestSpecificOptions))
 end
 
+def runBytecodeCache(*optionalTestSpecificOptions)
+    unless $hostOS == "darwin"
+        skip
+        return
+    end
+    options = BASE_OPTIONS + $testSpecificRequiredOptions + FTL_OPTIONS + optionalTestSpecificOptions
+    addRunCommand("bytecode-cache", ["sh", (pathToHelpers + "bytecode-cache-test-helper.sh").to_s, pathToVM.to_s, $benchmark.to_s] + options, silentOutputHandler, simpleErrorHandler)
+end
+
 def runBigIntEnabled(*optionalTestSpecificOptions)
     # FIXME: <rdar://problem/40331121>
     if $remote or ($architecture !~ /x86/i and $hostOS == "darwin")
@@ -751,6 +760,7 @@ def defaultRun
         defaultQuickRun
     else
         runDefault
+        runBytecodeCache
         if $jitTests
             runNoLLInt
             runNoCJITValidatePhases