Skip to content

Commit

Permalink
Merge pull request #66 from oven-sh/jarred/bytecode
Browse files Browse the repository at this point in the history
Improve support for bytecode serialization
  • Loading branch information
Jarred-Sumner committed Sep 29, 2024
2 parents a303e09 + 0f1b0ee commit cbde2c5
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 29 deletions.
20 changes: 17 additions & 3 deletions Source/JavaScriptCore/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -652,12 +652,20 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
bytecode/SuperSampler.h
bytecode/ToThisStatus.h
bytecode/TypeLocation.h
bytecode/UnlinkedCodeBlock.h
bytecode/UnlinkedEvalCodeBlock.h
bytecode/UnlinkedModuleProgramCodeBlockInlines.h
bytecode/UnlinkedModuleProgramCodeBlock.h
bytecode/UnlinkedProgramCodeBlock.h
bytecode/UnlinkedProgramCodeBlockInlines.h
bytecode/UnlinkedMetadataTable.h
bytecode/UnlinkedFunctionExecutable.h
bytecode/UnlinkedFunctionExecutableInlines.h
bytecode/UnlinkedFunctionCodeBlockInlines.h
bytecode/UnlinkedFunctionCodeBlock.h
bytecode/UnlinkedEvalCodeBlockInlines.h
bytecode/UnlinkedEvalCodeBlock.h
bytecode/UnlinkedCodeBlockGenerator.h
bytecode/UnlinkedCodeBlock.h
bytecode/UnlinkedGlobalCodeBlock.h
bytecode/UnlinkedMetadataTable.h
bytecode/ValueProfile.h
bytecode/ValueRecovery.h
bytecode/VariableWriteFireDetail.h
Expand Down Expand Up @@ -888,19 +896,25 @@ set(JavaScriptCore_PRIVATE_FRAMEWORK_HEADERS
llint/LLIntThunks.h

parser/Lexer.h
parser/Nodes.h
parser/ParserArena.h
parser/ParserError.h
parser/ParserFunctionInfo.h
parser/ParserModes.h
parser/ParserTokens.h
parser/ResultType.h
parser/SourceCode.h
parser/SourceCodeKey.h
parser/SourceProvider.h
parser/SourceProviderCache.h
parser/SourceProviderCacheItem.h
parser/SourceTaintedOrigin.h
parser/UnlinkedSourceCode.h
parser/VariableEnvironment.h

bytecode/ParseHash.h
bytecompiler/Label.h

profiler/ProfilerBytecode.h
profiler/ProfilerBytecodeSequence.h
profiler/ProfilerBytecodes.h
Expand Down
6 changes: 6 additions & 0 deletions Source/JavaScriptCore/builtins/BuiltinExecutables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const S
}
} else {
RELEASE_ASSERT(error.isValid());
#if ASSERT_ENABLED
if (error.type() != ParserError::StackOverflow) {
WTFLogAlways("Error parsing builtin: %s\n", error.message().utf8().data());
CRASH();
}
#endif
RELEASE_ASSERT(error.type() == ParserError::StackOverflow);
}
}
Expand Down
10 changes: 8 additions & 2 deletions Source/JavaScriptCore/parser/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,12 +974,18 @@ template <bool shouldCreateIdentifier> ALWAYS_INLINE JSTokenType Lexer<LChar>::p
const Identifier* ident = nullptr;

if (shouldCreateIdentifier || m_parsingBuiltinFunction) {
std::span identifierSpan { identifierStart, static_cast<size_t>(currentSourcePtr() - identifierStart) };
const std::span<const LChar> identifierSpan { identifierStart, static_cast<size_t>(currentSourcePtr() - identifierStart) };
if (m_parsingBuiltinFunction && isBuiltinName) {
if (isWellKnownSymbol)
ident = &m_arena->makeIdentifier(m_vm, m_vm.propertyNames->builtinNames().lookUpWellKnownSymbol(identifierSpan));
else
else {
if constexpr (ASSERT_ENABLED) {
auto *symbol = m_vm.propertyNames->builtinNames().lookUpPrivateName(identifierSpan);
ASSERT_WITH_MESSAGE(symbol, "Private symbol not found: %s", identifierSpan.data());
}

ident = &m_arena->makeIdentifier(m_vm, m_vm.propertyNames->builtinNames().lookUpPrivateName(identifierSpan));
}
if (!ident)
return INVALID_PRIVATE_NAME_ERRORTOK;
} else {
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/parser/SourceCodeKey.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ class SourceCodeKey {
&& m_functionConstructorParametersEndPosition == other.m_functionConstructorParametersEndPosition
&& m_name == other.m_name
&& host() == other.host()
#if USE(BUN_JSC_ADDITIONS)
;
#else
&& (m_sourceCode == other.m_sourceCode || string() == other.string());
#endif
}

struct Hash {
Expand Down
21 changes: 18 additions & 3 deletions Source/JavaScriptCore/runtime/CachePayload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

#include "config.h"
#include "CachePayload.h"
#include <span>

namespace JSC {

Expand All @@ -43,18 +44,28 @@ CachePayload CachePayload::makeEmptyPayload()
return CachePayload(std::pair { nullptr, 0 });
}

CachePayload CachePayload::makePayloadWithDestructor(std::span<uint8_t> data, Destructor&& destructor)
{
return CachePayload(data, WTFMove(destructor));
}

CachePayload::CachePayload(CachePayload&& other)
{
m_data = WTFMove(other.m_data);
m_destructor = WTFMove(other.m_destructor);
other.m_data = std::pair { nullptr, 0 };
other.m_destructor = nullptr;
}

CachePayload::CachePayload(std::variant<FileSystem::MappedFileData, std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>>&& data)
: m_data(WTFMove(data))
CachePayload::CachePayload(std::variant<FileSystem::MappedFileData, std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>, std::span<uint8_t>> && data, Destructor&& destructor)
: m_data(WTFMove(data)), m_destructor(WTFMove(destructor))
{
}

CachePayload::~CachePayload() = default;
CachePayload::~CachePayload() {
if (m_destructor)
m_destructor(data());
}

const uint8_t* CachePayload::data() const
{
Expand All @@ -63,6 +74,8 @@ const uint8_t* CachePayload::data() const
return data.span().data();
}, [](const std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>& data) -> const uint8_t* {
return data.first.get();
}, [](const std::span<uint8_t>& data) -> const uint8_t* {
return data.data();
}
);
}
Expand All @@ -74,6 +87,8 @@ size_t CachePayload::size() const
return data.size();
}, [](const std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>& data) -> size_t {
return data.second;
}, [](const std::span<uint8_t>& data) -> size_t {
return data.size();
}
);
}
Expand Down
7 changes: 5 additions & 2 deletions Source/JavaScriptCore/runtime/CachePayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ namespace JSC {

class CachePayload {
public:
using Destructor = WTF::Function<void(const void*)>;
JS_EXPORT_PRIVATE static CachePayload makeMappedPayload(FileSystem::MappedFileData&&);
JS_EXPORT_PRIVATE static CachePayload makeMallocPayload(MallocPtr<uint8_t, VMMalloc>&&, size_t);
JS_EXPORT_PRIVATE static CachePayload makePayloadWithDestructor(std::span<uint8_t>, Destructor&&);
JS_EXPORT_PRIVATE static CachePayload makeEmptyPayload();

JS_EXPORT_PRIVATE CachePayload(CachePayload&&);
Expand All @@ -45,11 +47,12 @@ class CachePayload {
std::span<const uint8_t> span() const { return { data(), size() }; }

private:
CachePayload(std::variant<FileSystem::MappedFileData, std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>>&&);
CachePayload(std::variant<FileSystem::MappedFileData, std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>, std::span<uint8_t>>&&, Destructor&& = {nullptr});

JS_EXPORT_PRIVATE const uint8_t* data() const;

std::variant<FileSystem::MappedFileData, std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>> m_data;
std::variant<FileSystem::MappedFileData, std::pair<MallocPtr<uint8_t, VMMalloc>, size_t>, std::span<uint8_t>> m_data;
Destructor m_destructor { nullptr };
};

} // namespace JSC
5 changes: 5 additions & 0 deletions Source/JavaScriptCore/runtime/CachedBytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class CachedBytecode : public RefCounted<CachedBytecode> {
return adoptRef(*new CachedBytecode(CachePayload::makeMallocPayload(WTFMove(data), size), WTFMove(leafExecutables)));
}

static Ref<CachedBytecode> create(std::span<uint8_t> data, CachePayload::Destructor&& destructor, LeafExecutableMap&& leafExecutables)
{
return adoptRef(*new CachedBytecode(CachePayload::makePayloadWithDestructor(data, WTFMove(destructor)), WTFMove(leafExecutables)));
}

LeafExecutableMap& leafExecutables() { return m_leafExecutables; }

JS_EXPORT_PRIVATE void addGlobalUpdate(Ref<CachedBytecode>);
Expand Down
34 changes: 34 additions & 0 deletions Source/JavaScriptCore/runtime/CachedTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2526,6 +2526,12 @@ class CacheEntry : public GenericCacheEntry {
return true;
}

bool decode(Decoder& decoder, SourceCodeKey& key) const
{
m_key.decode(decoder, key);
return true;
}

CachedSourceCodeKey m_key;
CachedPtr<CachedCodeBlockType<UnlinkedCodeBlockType>> m_codeBlock;
};
Expand All @@ -2551,6 +2557,24 @@ bool GenericCacheEntry::decode(Decoder& decoder, std::pair<SourceCodeKey, Unlink
return false;
}

bool GenericCacheEntry::decode(Decoder& decoder, SourceCodeKey& key) const
{
if (!isUpToDate(decoder))
return false;

switch (m_tag) {
case CachedProgramCodeBlockTag:
return bitwise_cast<const CacheEntry<UnlinkedProgramCodeBlock>*>(this)->decode(decoder, key);
case CachedModuleCodeBlockTag:
return bitwise_cast<const CacheEntry<UnlinkedModuleProgramCodeBlock>*>(this)->decode(decoder, key);
case CachedEvalCodeBlockTag:
// We do not cache eval code blocks
return false;
}

return false;
}

bool GenericCacheEntry::isStillValid(Decoder& decoder, const SourceCodeKey& key, CachedCodeBlockTag tag) const
{
if (!isUpToDate(decoder))
Expand Down Expand Up @@ -2604,6 +2628,16 @@ RefPtr<CachedBytecode> encodeFunctionCodeBlock(VM& vm, const UnlinkedFunctionCod
return encoder.release(error);
}

std::optional<SourceCodeKey> decodeSourceCodeKey(VM& vm, Ref<CachedBytecode> cachedBytecode)
{
const auto* cachedEntry = bitwise_cast<const GenericCacheEntry*>(cachedBytecode->span().data());
Ref<Decoder> decoder = Decoder::create(vm, WTFMove(cachedBytecode));

SourceCodeKey key;
if (!cachedEntry->decode(decoder.get(), key))
return std::nullopt;
return key;
}
UnlinkedCodeBlock* decodeCodeBlockImpl(VM& vm, const SourceCodeKey& key, Ref<CachedBytecode> cachedBytecode)
{
const auto* cachedEntry = bitwise_cast<const GenericCacheEntry*>(cachedBytecode->span().data());
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/runtime/CachedTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ UnlinkedCodeBlockType* decodeCodeBlock(VM& vm, const SourceCodeKey& key, Ref<Cac
return jsCast<UnlinkedCodeBlockType*>(decodeCodeBlockImpl(vm, key, WTFMove(cachedBytecode)));
}

std::optional<SourceCodeKey> decodeSourceCodeKey(VM& vm, Ref<CachedBytecode> cachedBytecode);

JS_EXPORT_PRIVATE RefPtr<CachedBytecode> encodeFunctionCodeBlock(VM&, const UnlinkedFunctionCodeBlock*, BytecodeCacheError&);

JS_EXPORT_PRIVATE void decodeFunctionCodeBlock(Decoder&, int32_t cachedFunctionCodeBlockOffset, WriteBarrier<UnlinkedFunctionCodeBlock>&, const JSCell*);
Expand Down
37 changes: 26 additions & 11 deletions Source/JavaScriptCore/runtime/CodeCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,28 @@ static void generateUnlinkedCodeBlockForFunctions(VM& vm, UnlinkedCodeBlock* unl

// FIXME: We should also generate CodeBlocks for CodeForConstruct
// https://bugs.webkit.org/show_bug.cgi?id=193823
for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionDecls(); i++)
generate(unlinkedCodeBlock->functionDecl(i), CodeForCall);
for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionExprs(); i++)
generate(unlinkedCodeBlock->functionExpr(i), CodeForCall);
//
// NOTE: We changed this in Bun. We check if the function is a constructor
// and if it is, we generate a CodeForConstruct block.
for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionDecls(); i++) {
auto* functionDecl = unlinkedCodeBlock->functionDecl(i);
if (functionDecl->isConstructor()) {
generate(functionDecl, CodeForConstruct);
} else {
generate(functionDecl, CodeForCall);
}
}
for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionExprs(); i++) {
auto* functionExpr = unlinkedCodeBlock->functionExpr(i);
if (functionExpr->isConstructor()) {
generate(functionExpr, CodeForConstruct);
} else {
generate(functionExpr, CodeForCall);
}
}
}

template <class UnlinkedCodeBlockType, class ExecutableType = ScriptExecutable>
template<class UnlinkedCodeBlockType, class ExecutableType = ScriptExecutable>
UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& source, LexicallyScopedFeatures lexicallyScopedFeatures, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType, DerivedContextType derivedContextType, bool isArrowFunctionContext, const TDZEnvironment* variablesUnderTDZ = nullptr, const PrivateNameEnvironment* privateNameEnvironment = nullptr, ExecutableType* executable = nullptr)
{
typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
Expand Down Expand Up @@ -119,7 +134,7 @@ UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& s
return unlinkedCodeBlock;
}

template <class UnlinkedCodeBlockType, class ExecutableType>
template<class UnlinkedCodeBlockType, class ExecutableType>
UnlinkedCodeBlockType* generateUnlinkedCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType, const TDZEnvironment* variablesUnderTDZ = nullptr, const PrivateNameEnvironment* privateNameEnvironment = nullptr)
{
return generateUnlinkedCodeBlockImpl<UnlinkedCodeBlockType, ExecutableType>(vm, source, executable->lexicallyScopedFeatures(), scriptMode, codeGenerationMode, error, evalContextType, executable->derivedContextType(), executable->isArrowFunctionContext(), variablesUnderTDZ, privateNameEnvironment, executable);
Expand All @@ -130,7 +145,7 @@ UnlinkedEvalCodeBlock* generateUnlinkedCodeBlockForDirectEval(VM& vm, DirectEval
return generateUnlinkedCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, scriptMode, codeGenerationMode, error, evalContextType, variablesUnderTDZ, privateNameEnvironment);
}

template <class UnlinkedCodeBlockType>
template<class UnlinkedCodeBlockType>
std::enable_if_t<!std::is_same<UnlinkedCodeBlockType, UnlinkedEvalCodeBlock>::value, UnlinkedCodeBlockType*>
recursivelyGenerateUnlinkedCodeBlock(VM& vm, const SourceCode& source, LexicallyScopedFeatures lexicallyScopedFeatures, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType)
{
Expand All @@ -153,7 +168,7 @@ UnlinkedModuleProgramCodeBlock* recursivelyGenerateUnlinkedCodeBlockForModulePro
return recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, lexicallyScopedFeatures, scriptMode, codeGenerationMode, error, evalContextType);
}

template <class UnlinkedCodeBlockType, class ExecutableType>
template<class UnlinkedCodeBlockType, class ExecutableType>
UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType)
{
DerivedContextType derivedContextType = executable->derivedContextType();
Expand Down Expand Up @@ -245,7 +260,7 @@ UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& v
ASSERT(metadata);
if (!metadata)
return nullptr;

metadata->overrideName(name);
metadata->setEndPosition(positionBeforeLastNewline);
// The Function constructor only has access to global variables, so no variables will be under TDZ unless they're
Expand Down Expand Up @@ -294,13 +309,13 @@ static SourceCodeKey sourceCodeKeyForSerializedBytecode(VM&, const SourceCode& s
SourceCodeKey sourceCodeKeyForSerializedProgram(VM& vm, const SourceCode& sourceCode)
{
JSParserScriptMode scriptMode = JSParserScriptMode::Classic;
return sourceCodeKeyForSerializedBytecode(vm, sourceCode, SourceCodeType::ProgramType, NoLexicallyScopedFeatures, scriptMode, { });
return sourceCodeKeyForSerializedBytecode(vm, sourceCode, SourceCodeType::ProgramType, NoLexicallyScopedFeatures, scriptMode, {});
}

SourceCodeKey sourceCodeKeyForSerializedModule(VM& vm, const SourceCode& sourceCode)
{
JSParserScriptMode scriptMode = JSParserScriptMode::Module;
return sourceCodeKeyForSerializedBytecode(vm, sourceCode, SourceCodeType::ModuleType, StrictModeLexicallyScopedFeature, scriptMode, { });
return sourceCodeKeyForSerializedBytecode(vm, sourceCode, SourceCodeType::ModuleType, StrictModeLexicallyScopedFeature, scriptMode, {});
}

RefPtr<CachedBytecode> serializeBytecode(VM& vm, UnlinkedCodeBlock* codeBlock, const SourceCode& source, SourceCodeType codeType, LexicallyScopedFeatures lexicallyScopedFeatures, JSParserScriptMode scriptMode, FileSystem::PlatformFileHandle fd, BytecodeCacheError& error, OptionSet<CodeGenerationMode> codeGenerationMode)
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/runtime/CodeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ class CodeCacheMap {
if (isMainThread())
RELEASE_ASSERT(codeBlock);
}
dataLogLnIf(codeBlock && Options::verboseDiskCache(), "[Disk Cache] Cache hit for sourceCode");
dataLogLnIf(!codeBlock && Options::verboseDiskCache(), "[Disk Cache] Cache miss for sourceCode");
return codeBlock;
} else {
UNUSED_PARAM(vm);
Expand Down
2 changes: 1 addition & 1 deletion Source/JavaScriptCore/runtime/JSCBytecodeCacheVersion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static constexpr bool verbose = false;

uint32_t computeJSCBytecodeCacheVersion()
{
#if OS(DARWIN)
#if OS(DARWIN) && !USE(BUN_JSC_ADDITIONS)
static LazyNeverDestroyed<uint32_t> cacheVersion;
static std::once_flag onceFlag;
std::call_once(onceFlag, [] {
Expand Down
10 changes: 7 additions & 3 deletions Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,12 +859,16 @@ JSC_DEFINE_HOST_FUNCTION(globalFuncImportModule, (JSGlobalObject* globalObject,
RETURN_IF_EXCEPTION(scope, JSValue::encode(JSPromise::rejectedPromiseWithCaughtException(globalObject, scope)));

scope.release();

auto* promise = JSPromise::create(vm, globalObject->promiseStructure());

if (internalPromise->status(vm) == JSPromise::Status::Fulfilled) {
return JSValue::encode(JSPromise::resolvedPromise(globalObject, internalPromise->result(vm)));
auto result = internalPromise->result(vm);
promise->fulfill(globalObject, result);
} else {
promise->resolve(globalObject, internalPromise);
}

auto* promise = JSPromise::create(vm, globalObject->promiseStructure());
promise->resolve(globalObject, internalPromise);
return JSValue::encode(promise);
}

Expand Down
Loading

0 comments on commit cbde2c5

Please sign in to comment.