blob: eb9d993b2514c6729e102f65e720a728af2c2439 [file] [log] [blame]
/*
* 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 "BytecodeCacheError.h"
#include "BytecodeCacheVersion.h"
#include "BytecodeLivenessAnalysis.h"
#include "JSCInlines.h"
#include "JSImmutableButterfly.h"
#include "JSTemplateObjectDescriptor.h"
#include "ScopedArgumentsTable.h"
#include "SourceCodeKey.h"
#include "SourceProvider.h"
#include "UnlinkedEvalCodeBlock.h"
#include "UnlinkedFunctionCodeBlock.h"
#include "UnlinkedMetadataTableInlines.h"
#include "UnlinkedModuleProgramCodeBlock.h"
#include "UnlinkedProgramCodeBlock.h"
#include <wtf/FastMalloc.h>
#include <wtf/Optional.h>
#include <wtf/UUID.h>
#include <wtf/text/AtomStringImpl.h>
namespace JSC {
namespace Yarr {
enum class Flags : uint8_t;
}
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, FileSystem::PlatformFileHandle fd = FileSystem::invalidPlatformFileHandle)
: m_vm(vm)
, m_fd(fd)
, m_baseOffset(0)
, m_currentPage(nullptr)
{
allocateNewPage();
}
VM& vm() { return m_vm; }
Allocation malloc(unsigned size)
{
RELEASE_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, typename... Args>
T* malloc(Args&&... args)
{
return new (malloc(sizeof(T)).buffer()) T(std::forward<Args>(args)...);
}
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);
}
Optional<ptrdiff_t> cachedOffsetForPtr(const void* ptr)
{
auto it = m_ptrToOffsetMap.find(ptr);
if (it == m_ptrToOffsetMap.end())
return WTF::nullopt;
return { it->value };
}
void addLeafExecutable(const UnlinkedFunctionExecutable* executable, ptrdiff_t offset)
{
m_leafExecutables.add(executable, offset);
}
RefPtr<CachedBytecode> release(BytecodeCacheError& error)
{
if (!m_currentPage)
return nullptr;
m_currentPage->alignEnd();
if (FileSystem::isHandleValid(m_fd)) {
return releaseMapped(error);
}
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 CachedBytecode::create(WTFMove(buffer), size, WTFMove(m_leafExecutables));
}
private:
RefPtr<CachedBytecode> releaseMapped(BytecodeCacheError& error)
{
size_t size = m_baseOffset + m_currentPage->size();
if (!FileSystem::truncateFile(m_fd, size)) {
error = BytecodeCacheError::StandardError(errno);
return nullptr;
}
for (const auto& page : m_pages) {
int bytesWritten = FileSystem::writeToFile(m_fd, reinterpret_cast<char*>(page.buffer()), page.size());
if (bytesWritten == -1) {
error = BytecodeCacheError::StandardError(errno);
return nullptr;
}
if (static_cast<size_t>(bytesWritten) != page.size()) {
error = BytecodeCacheError::WriteError(bytesWritten, page.size());
return nullptr;
}
}
bool success;
FileSystem::MappedFileData mappedFileData(m_fd, FileSystem::MappedFileMode::Private, success);
if (!success) {
error = BytecodeCacheError::StandardError(errno);
return nullptr;
}
return CachedBytecode::create(WTFMove(mappedFileData), WTFMove(m_leafExecutables));
}
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 = roundUpToMultipleOf(alignment, m_offset);
size = 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;
}
void alignEnd()
{
ptrdiff_t size = roundUpToMultipleOf(alignof(std::max_align_t), m_offset);
if (size == m_offset)
return;
RELEASE_ASSERT(static_cast<size_t>(size) <= m_capacity);
m_offset = size;
}
private:
MallocPtr<uint8_t> m_buffer;
ptrdiff_t m_offset;
size_t m_capacity;
};
void allocateNewPage(size_t size = 0)
{
static size_t minPageSize = pageSize();
if (m_currentPage) {
m_currentPage->alignEnd();
m_baseOffset += m_currentPage->size();
}
if (size < minPageSize)
size = minPageSize;
else
size = roundUpToMultipleOf(minPageSize, size);
m_pages.append(Page { size });
m_currentPage = &m_pages.last();
}
VM& m_vm;
FileSystem::PlatformFileHandle m_fd;
ptrdiff_t m_baseOffset;
Page* m_currentPage;
Vector<Page> m_pages;
HashMap<const void*, ptrdiff_t> m_ptrToOffsetMap;
LeafExecutableMap m_leafExecutables;
};
Decoder::Decoder(VM& vm, Ref<CachedBytecode> cachedBytecode, RefPtr<SourceProvider> provider)
: m_vm(vm)
, m_cachedBytecode(WTFMove(cachedBytecode))
, m_provider(provider)
{
}
Decoder::~Decoder()
{
for (auto& finalizer : m_finalizers)
finalizer();
}
Ref<Decoder> Decoder::create(VM& vm, Ref<CachedBytecode> cachedBytecode, RefPtr<SourceProvider> provider)
{
return adoptRef(*new Decoder(vm, WTFMove(cachedBytecode), WTFMove(provider)));
}
size_t Decoder::size() const
{
return m_cachedBytecode->size();
}
ptrdiff_t Decoder::offsetOf(const void* ptr)
{
const uint8_t* addr = static_cast<const uint8_t*>(ptr);
ASSERT(addr >= m_cachedBytecode->data() && addr < m_cachedBytecode->data() + m_cachedBytecode->size());
return addr - m_cachedBytecode->data();
}
void Decoder::cacheOffset(ptrdiff_t offset, void* ptr)
{
m_offsetToPtrMap.add(offset, ptr);
}
WTF::Optional<void*> Decoder::cachedPtrForOffset(ptrdiff_t offset)
{
auto it = m_offsetToPtrMap.find(offset);
if (it == m_offsetToPtrMap.end())
return WTF::nullopt;
return { it->value };
}
const void* Decoder::ptrForOffsetFromBase(ptrdiff_t offset)
{
ASSERT(offset > 0 && static_cast<size_t>(offset) < m_cachedBytecode->size());
return m_cachedBytecode->data() + offset;
}
CompactVariableMap::Handle Decoder::handleForEnvironment(CompactVariableEnvironment* environment) const
{
auto it = m_environmentToHandleMap.find(environment);
RELEASE_ASSERT(it != m_environmentToHandleMap.end());
return it->value;
}
void Decoder::setHandleForEnvironment(CompactVariableEnvironment* environment, const CompactVariableMap::Handle& handle)
{
auto addResult = m_environmentToHandleMap.add(environment, handle);
RELEASE_ASSERT(addResult.isNewEntry);
}
void Decoder::addLeafExecutable(const UnlinkedFunctionExecutable* executable, ptrdiff_t offset)
{
m_cachedBytecode->leafExecutables().add(executable, offset);
}
template<typename Functor>
void Decoder::addFinalizer(const Functor& fn)
{
m_finalizers.append(fn);
}
RefPtr<SourceProvider> Decoder::provider() const
{
return m_provider;
}
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>);
public:
using SourceType_ = Source;
CachedObject() = default;
inline void* operator new(size_t, void* where) { return where; }
void* operator new[](size_t, void* where) { return where; }
// Copied from WTF_FORBID_HEAP_ALLOCATION, since we only want to allow placement new
void* operator new(size_t) = delete;
void operator delete(void*) = delete;
void* operator new[](size_t size) = delete;
void operator delete[](void*) = delete;
void* operator new(size_t, NotNullTag, void* location) = delete;
};
template<typename Source>
class VariableLengthObject : public CachedObject<Source>, VariableLengthObjectBase {
template<typename, typename>
friend class CachedPtr;
friend struct CachedPtrOffsets;
public:
VariableLengthObject()
: VariableLengthObjectBase(s_invalidOffset)
{
}
bool isEmpty() const
{
return m_offset == s_invalidOffset;
}
protected:
const uint8_t* buffer() const
{
ASSERT(!isEmpty());
return bitwise_cast<const uint8_t*>(this) + m_offset;
}
template<typename T>
const T* buffer() const
{
ASSERT(!(bitwise_cast<uintptr_t>(buffer()) % alignof(T)));
return bitwise_cast<const T*>(buffer());
}
uint8_t* allocate(Encoder& encoder, size_t size)
{
ptrdiff_t offsetOffset = encoder.offsetOf(&m_offset);
auto result = encoder.malloc(size);
m_offset = result.offset() - offsetOffset;
return result.buffer();
}
template<typename T>
#if CPU(ARM64) && CPU(ADDRESS32)
// FIXME: Remove this once it's no longer needed and LLVM doesn't miscompile us:
// <rdar://problem/49792205>
__attribute__((optnone))
#endif
T* allocate(Encoder& encoder, unsigned size = 1)
{
uint8_t* result = allocate(encoder, sizeof(T) * size);
ASSERT(!(bitwise_cast<uintptr_t>(result) % alignof(T)));
return new (result) T[size];
}
private:
constexpr static ptrdiff_t s_invalidOffset = std::numeric_limits<ptrdiff_t>::max();
};
template<typename T, typename Source = SourceType<T>>
class CachedPtr : public VariableLengthObject<Source*> {
template<typename, typename>
friend class CachedRefPtr;
friend struct CachedPtrOffsets;
public:
void encode(Encoder& encoder, const Source* src)
{
if (!src)
return;
if (Optional<ptrdiff_t> offset = encoder.cachedOffsetForPtr(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, bool& isNewAllocation, Args&&... args) const
{
if (this->isEmpty()) {
isNewAllocation = false;
return nullptr;
}
ptrdiff_t bufferOffset = decoder.offsetOf(this->buffer());
if (Optional<void*> ptr = decoder.cachedPtrForOffset(bufferOffset)) {
isNewAllocation = false;
return static_cast<Source*>(*ptr);
}
isNewAllocation = true;
Source* ptr = get()->decode(decoder, std::forward<Args>(args)...);
decoder.cacheOffset(bufferOffset, ptr);
return ptr;
}
template<typename... Args>
Source* decode(Decoder& decoder, Args&&... args) const
{
bool unusedIsNewAllocation;
return decode(decoder, unusedIsNewAllocation, std::forward<Args>(args)...);
}
const T* operator->() const { return get(); }
private:
const T* get() const
{
RELEASE_ASSERT(!this->isEmpty());
return this->template buffer<T>();
}
};
ptrdiff_t CachedPtrOffsets::offsetOffset()
{
return OBJECT_OFFSETOF(CachedPtr<void>, m_offset);
}
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
{
bool isNewAllocation;
Source* decodedPtr = m_ptr.decode(decoder, isNewAllocation);
if (!decodedPtr)
return nullptr;
if (isNewAllocation) {
decoder.addFinalizer([=] {
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>> {
friend struct CachedWriteBarrierOffsets;
public:
bool isEmpty() const { return m_ptr.isEmpty(); }
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;
};
ptrdiff_t CachedWriteBarrierOffsets::ptrOffset()
{
return OBJECT_OFFSETOF(CachedWriteBarrier<void>, 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();
if (!m_size)
return;
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
{
if (!m_size)
return;
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;
};
template<typename T>
class CachedUniquedStringImplBase : public VariableLengthObject<T> {
public:
void encode(Encoder& encoder, const StringImpl& string)
{
m_isAtomic = string.isAtom();
m_isSymbol = string.isSymbol();
RefPtr<StringImpl> impl = const_cast<StringImpl*>(&string);
if (m_isSymbol) {
SymbolImpl* symbol = static_cast<SymbolImpl*>(impl.get());
if (!symbol->isNullSymbol()) {
// We have special handling for well-known symbols.
if (!symbol->isPrivate())
impl = encoder.vm().propertyNames->getPublicName(encoder.vm(), symbol).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 AtomStringImpl::add(buffer, m_length).leakRef();
Identifier ident = Identifier::fromString(decoder.vm(), buffer, m_length);
String str = decoder.vm().propertyNames->lookUpPrivateName(ident);
StringImpl* impl = str.releaseImpl().get();
ASSERT(impl->isSymbol());
return static_cast<UniquedStringImpl*>(impl);
};
if (!m_length) {
if (m_isSymbol)
return &SymbolImpl::createNullSymbol().leakRef();
return AtomStringImpl::add("").leakRef();
}
if (m_is8Bit)
return create(this->template buffer<LChar>());
return create(this->template buffer<UChar>());
}
private:
bool m_is8Bit : 1;
bool m_isSymbol : 1;
bool m_isAtomic : 1;
unsigned m_length;
};
class CachedUniquedStringImpl : public CachedUniquedStringImplBase<UniquedStringImpl> { };
class CachedStringImpl : public CachedUniquedStringImplBase<StringImpl> { };
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<Optional<SourceType<T>>> {
public:
void encode(Encoder& encoder, const Optional<SourceType<T>>& source)
{
if (!source)
return;
this->template allocate<T>(encoder)->encode(encoder, *source);
}
Optional<SourceType<T>> decode(Decoder& decoder) const
{
if (this->isEmpty())
return WTF::nullopt;
return { this->template buffer<T>()->decode(decoder) };
}
void decode(Decoder& decoder, 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 });
}
};
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 CachedBitVector : public VariableLengthObject<BitVector> {
public:
void encode(Encoder& encoder, const BitVector& bitVector)
{
m_numBits = bitVector.size();
if (!m_numBits)
return;
size_t sizeInBytes = BitVector::byteCount(m_numBits);
uint8_t* buffer = this->allocate(encoder, sizeInBytes);
memcpy(buffer, bitVector.bits(), sizeInBytes);
}
void decode(Decoder&, BitVector& bitVector) const
{
if (!m_numBits)
return;
bitVector.ensureSize(m_numBits);
size_t sizeInBytes = BitVector::byteCount(m_numBits);
memcpy(bitVector.bits(), this->buffer(), sizeInBytes);
}
private:
size_t m_numBits;
};
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 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);
m_bitVectors.encode(encoder, rareData.m_bitVectors);
m_constantIdentifierSets.encode(encoder, rareData.m_constantIdentifierSets);
}
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);
m_bitVectors.decode(decoder, rareData->m_bitVectors);
m_constantIdentifierSets.decode(decoder, rareData->m_constantIdentifierSets);
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;
CachedVector<CachedBitVector> m_bitVectors;
CachedVector<CachedConstantIdentifierSetEntry> m_constantIdentifierSets;
};
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;
};
class CachedCompactVariableEnvironment : public CachedObject<CompactVariableEnvironment> {
public:
void encode(Encoder& encoder, const CompactVariableEnvironment& env)
{
m_variables.encode(encoder, env.m_variables);
m_variableMetadata.encode(encoder, env.m_variableMetadata);
m_hash = env.m_hash;
m_isEverythingCaptured = env.m_isEverythingCaptured;
}
void decode(Decoder& decoder, CompactVariableEnvironment& env) const
{
m_variables.decode(decoder, env.m_variables);
m_variableMetadata.decode(decoder, env.m_variableMetadata);
env.m_hash = m_hash;
env.m_isEverythingCaptured = m_isEverythingCaptured;
}
CompactVariableEnvironment* decode(Decoder& decoder) const
{
CompactVariableEnvironment* env = new CompactVariableEnvironment;
decode(decoder, *env);
return env;
}
private:
CachedVector<CachedRefPtr<CachedUniquedStringImpl>> m_variables;
CachedVector<VariableEnvironmentEntry> m_variableMetadata;
unsigned m_hash;
bool m_isEverythingCaptured;
};
class CachedCompactVariableMapHandle : public CachedObject<CompactVariableMap::Handle> {
public:
void encode(Encoder& encoder, const CompactVariableMap::Handle& handle)
{
m_environment.encode(encoder, handle.m_environment);
}
CompactVariableMap::Handle decode(Decoder& decoder) const
{
bool isNewAllocation;
CompactVariableEnvironment* environment = m_environment.decode(decoder, isNewAllocation);
if (!environment) {
ASSERT(!isNewAllocation);
return CompactVariableMap::Handle();
}
if (!isNewAllocation)
return decoder.handleForEnvironment(environment);
bool isNewEntry;
CompactVariableMap::Handle handle = decoder.vm().m_compactVariableMap->get(environment, isNewEntry);
if (!isNewEntry) {
decoder.addFinalizer([=] {
delete environment;
});
}
decoder.setHandleForEnvironment(environment, handle);
return handle;
}
private:
CachedPtr<CachedCompactVariableEnvironment> m_environment;
};
template<typename T, typename Source = SourceType<T>>
class CachedArray : public VariableLengthObject<Source*> {
public:
void encode(Encoder& encoder, const Source* array, unsigned size)
{
if (!size)
return;
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
{
if (!size)
return;
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), 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), 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)
{
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;
CachedPtr<CachedScopedArgumentsTable> m_arguments;
};
class CachedJSValue;
class CachedImmutableButterfly : public CachedObject<JSImmutableButterfly> {
public:
CachedImmutableButterfly()
: m_cachedDoubles()
{
}
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;
OptionSet<Yarr::Flags> m_flags;
};
class CachedTemplateObjectDescriptor : public CachedObject<TemplateObjectDescriptor> {
public:
void encode(Encoder& encoder, const JSTemplateObjectDescriptor& descriptor)
{
m_rawStrings.encode(encoder, descriptor.descriptor().rawStrings());
m_cookedStrings.encode(encoder, descriptor.descriptor().cookedStrings());
m_endOffset = descriptor.endOffset();
}
JSTemplateObjectDescriptor* decode(Decoder& decoder) const
{
TemplateObjectDescriptor::StringVector decodedRawStrings;
TemplateObjectDescriptor::OptionalStringVector decodedCookedStrings;
m_rawStrings.decode(decoder, decodedRawStrings);
m_cookedStrings.decode(decoder, decodedCookedStrings);
return JSTemplateObjectDescriptor::create(decoder.vm(), TemplateObjectDescriptor::create(WTFMove(decodedRawStrings), WTFMove(decodedCookedStrings)), m_endOffset);
}
private:
CachedVector<CachedString, 4> m_rawStrings;
CachedVector<CachedOptional<CachedString>, 4> m_cookedStrings;
int m_endOffset;
};
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);
if (m_length)
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);
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: {
StringImpl* impl = this->buffer<CachedUniquedStringImpl>()->decode(decoder);
v = jsString(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 = 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;
m_is32Bit = metadataTable.m_is32Bit;
if (m_is32Bit) {
for (unsigned i = UnlinkedMetadataTable::s_offsetTableEntries; i--;)
m_metadata[i] = metadataTable.offsetTable32()[i];
} else {
for (unsigned i = UnlinkedMetadataTable::s_offsetTableEntries; i--;)
m_metadata[i] = metadataTable.offsetTable16()[i];
}
}
Ref<UnlinkedMetadataTable> decode(Decoder&) const
{
if (!m_hasMetadata)
return UnlinkedMetadataTable::empty();
Ref<UnlinkedMetadataTable> metadataTable = UnlinkedMetadataTable::create(m_is32Bit);
metadataTable->m_isFinalized = true;
metadataTable->m_isLinked = false;
metadataTable->m_hasMetadata = m_hasMetadata;
if (m_is32Bit) {
for (unsigned i = UnlinkedMetadataTable::s_offsetTableEntries; i--;)
metadataTable->offsetTable32()[i] = m_metadata[i];
} else {
for (unsigned i = UnlinkedMetadataTable::s_offsetTableEntries; i--;)
metadataTable->offsetTable16()[i] = m_metadata[i];
}
return metadataTable;
}
private:
bool m_hasMetadata;
bool m_is32Bit;
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.sourceURLDirective());
m_sourceMappingURLDirective.encode(encoder, sourceProvider.sourceMappingURLDirective());
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);
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:
CachedRefPtr<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 CachedSourceCodeWithoutProvider : public CachedObject<SourceCode> {
public:
void encode(Encoder&, const SourceCode& sourceCode)
{
m_hasProvider = !!sourceCode.provider();
m_startOffset = sourceCode.startOffset();
m_endOffset = sourceCode.endOffset();
m_firstLine = sourceCode.firstLine().zeroBasedInt();
m_startColumn = sourceCode.startColumn().zeroBasedInt();
}
void decode(Decoder& decoder, SourceCode& sourceCode) const
{
if (m_hasProvider)
sourceCode.m_provider = decoder.provider();
sourceCode.m_startOffset = m_startOffset;
sourceCode.m_endOffset = m_endOffset;
sourceCode.m_firstLine = OrdinalNumber::fromZeroBasedInt(m_firstLine);
sourceCode.m_startColumn = OrdinalNumber::fromZeroBasedInt(m_startColumn);
}
private:
bool m_hasProvider;
int m_startOffset;
int m_endOffset;
int m_firstLine;
int m_startColumn;
};
class CachedFunctionExecutableRareData : public CachedObject<UnlinkedFunctionExecutable::RareData> {
public:
void encode(Encoder& encoder, const UnlinkedFunctionExecutable::RareData& rareData)
{
m_classSource.encode(encoder, rareData.m_classSource);
m_parentScopeTDZVariables.encode(encoder, rareData.m_parentScopeTDZVariables);
}
UnlinkedFunctionExecutable::RareData* decode(Decoder& decoder) const
{
UnlinkedFunctionExecutable::RareData* rareData = new UnlinkedFunctionExecutable::RareData { };
m_classSource.decode(decoder, rareData->m_classSource);
auto parentScopeTDZVariables = m_parentScopeTDZVariables.decode(decoder);
rareData->m_parentScopeTDZVariables = WTFMove(parentScopeTDZVariables);
return rareData;
}
private:
CachedSourceCodeWithoutProvider m_classSource;
CachedCompactVariableMapHandle m_parentScopeTDZVariables;
};
class CachedFunctionExecutable : public CachedObject<UnlinkedFunctionExecutable> {
friend struct CachedFunctionExecutableOffsets;
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_mutableMetadata.m_features; }
SourceParseMode sourceParseMode() const { return m_sourceParseMode; }
unsigned isInStrictContext() const { return m_isInStrictContext; }
unsigned hasCapturedVariables() const { return m_mutableMetadata.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); }
UnlinkedFunctionExecutable::RareData* rareData(Decoder& decoder) const { return m_rareData.decode(decoder); }
const CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock>& unlinkedCodeBlockForCall() const { return m_unlinkedCodeBlockForCall; }
const CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock>& unlinkedCodeBlockForConstruct() const { return m_unlinkedCodeBlockForConstruct; }
private:
CachedFunctionExecutableMetadata m_mutableMetadata;
unsigned m_firstLineOffset : 31;
unsigned m_isInStrictContext : 1;
unsigned m_lineCount : 31;
unsigned m_isBuiltinFunction : 1;
unsigned m_unlinkedFunctionNameStart : 31;
unsigned m_isBuiltinDefaultClassConstructor : 1;
unsigned m_unlinkedBodyStartColumn : 31;
unsigned m_constructAbility: 1;
unsigned m_unlinkedBodyEndColumn : 31;
unsigned m_startOffset : 31;
unsigned m_scriptMode: 1; // JSParserScriptMode
unsigned m_sourceLength : 31;
unsigned m_superBinding : 1;
unsigned m_parametersStartOffset : 31;
unsigned m_typeProfilingStartOffset;
unsigned m_typeProfilingEndOffset;
unsigned m_parameterCount;
SourceParseMode m_sourceParseMode;
unsigned m_constructorKind : 2;
unsigned m_functionMode : 2; // FunctionMode
unsigned m_derivedContextType: 2;
CachedPtr<CachedFunctionExecutableRareData> m_rareData;
CachedIdentifier m_name;
CachedIdentifier m_ecmaName;
CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock> m_unlinkedCodeBlockForCall;
CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock> m_unlinkedCodeBlockForConstruct;
};
ptrdiff_t CachedFunctionExecutableOffsets::codeBlockForCallOffset()
{
return OBJECT_OFFSETOF(CachedFunctionExecutable, m_unlinkedCodeBlockForCall);
}
ptrdiff_t CachedFunctionExecutableOffsets::codeBlockForConstructOffset()
{
return OBJECT_OFFSETOF(CachedFunctionExecutable, m_unlinkedCodeBlockForConstruct);
}
ptrdiff_t CachedFunctionExecutableOffsets::metadataOffset()
{
return OBJECT_OFFSETOF(CachedFunctionExecutable, m_mutableMetadata);
}
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; }
String sourceURLDirective(Decoder& decoder) const { return m_sourceURLDirective.decode(decoder); }
String sourceMappingURLDirective(Decoder& decoder) const { return m_sourceMappingURLDirective.decode(decoder); }
Ref<UnlinkedMetadataTable> metadata(Decoder& decoder) const { return m_metadata.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 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; }
OptionSet<CodeGenerationMode> codeGenerationMode() const { return m_codeGenerationMode; }
unsigned codeType() const { return m_codeType; }
UnlinkedCodeBlock::RareData* rareData(Decoder& decoder) const { return m_rareData.decode(decoder); }
private:
VirtualRegister m_thisRegister;
VirtualRegister m_scopeRegister;
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_constructorKind : 2;
unsigned m_derivedContextType : 2;
unsigned m_evalContextType : 2;
unsigned m_hasTailCalls : 1;
unsigned m_codeType : 2;
CodeFeatures m_features;
SourceParseMode m_parseMode;
OptionSet<CodeGenerationMode> m_codeGenerationMode;
unsigned m_lineCount;
unsigned m_endColumn;
int m_numVars;
int m_numCalleeLocals;
int m_numParameters;
CachedMetadataTable m_metadata;
CachedPtr<CachedCodeBlockRareData> m_rareData;
CachedString m_sourceURLDirective;
CachedString m_sourceMappingURLDirective;
CachedPtr<CachedInstructionStream> m_instructions;
CachedVector<InstructionStream::Offset> m_jumpTargets;
CachedVector<CachedJSValue> m_constantRegisters;
CachedVector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation;
CachedVector<ExpressionRangeInfo> m_expressionInfo;
CachedHashMap<InstructionStream::Offset, int> m_outOfLineJumpTargets;
CachedVector<CachedIdentifier> m_identifiers;
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,
};
static CachedCodeBlockTag tagFromSourceCodeType(SourceCodeType type)
{
switch (type) {
case SourceCodeType::ProgramType:
return CachedProgramCodeBlockTag;
case SourceCodeType::EvalType:
return CachedEvalCodeBlockTag;
case SourceCodeType::ModuleType:
return CachedModuleCodeBlockTag;
case SourceCodeType::FunctionType:
break;
}
ASSERT_NOT_REACHED();
return static_cast<CachedCodeBlockTag>(-1);
}
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_thisRegister(cachedCodeBlock.thisRegister())
, m_scopeRegister(cachedCodeBlock.scopeRegister())
, 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_hasTailCalls(cachedCodeBlock.hasTailCalls())
, m_constructorKind(cachedCodeBlock.constructorKind())
, m_derivedContextType(cachedCodeBlock.derivedContextType())
, m_evalContextType(cachedCodeBlock.evalContextType())
, m_codeType(cachedCodeBlock.codeType())
, m_didOptimize(static_cast<unsigned>(MixedTriState))
, m_age(0)
, m_features(cachedCodeBlock.features())
, m_parseMode(cachedCodeBlock.parseMode())
, m_codeGenerationMode(cachedCodeBlock.codeGenerationMode())
, m_lineCount(cachedCodeBlock.lineCount())
, m_endColumn(cachedCodeBlock.endColumn())
, m_numVars(cachedCodeBlock.numVars())
, m_numCalleeLocals(cachedCodeBlock.numCalleeLocals())
, m_numParameters(cachedCodeBlock.numParameters())
, m_sourceURLDirective(cachedCodeBlock.sourceURLDirective(decoder))
, m_sourceMappingURLDirective(cachedCodeBlock.sourceMappingURLDirective(decoder))
, m_metadata(cachedCodeBlock.metadata(decoder))
, m_instructions(cachedCodeBlock.instructions(decoder))
, m_rareData(cachedCodeBlock.rareData(decoder))
{
}
template<typename CodeBlockType>
ALWAYS_INLINE void CachedCodeBlock<CodeBlockType>::decode(Decoder& decoder, UnlinkedCodeBlock& codeBlock) const
{
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_identifiers.decode(decoder, codeBlock.m_identifiers);
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_mutableMetadata.m_features = executable.m_features;
m_mutableMetadata.m_hasCapturedVariables = executable.m_hasCapturedVariables;
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_sourceParseMode = executable.m_sourceParseMode;
m_isInStrictContext = executable.m_isInStrictContext;
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_rareData.encode(encoder, executable.m_rareData.get());
m_name.encode(encoder, executable.name());
m_ecmaName.encode(encoder, executable.ecmaName());
m_unlinkedCodeBlockForCall.encode(encoder, executable.m_unlinkedCodeBlockForCall);
m_unlinkedCodeBlockForConstruct.encode(encoder, executable.m_unlinkedCodeBlockForConstruct);
if (!executable.m_unlinkedCodeBlockForCall || !executable.m_unlinkedCodeBlockForConstruct)
encoder.addLeafExecutable(&executable, encoder.offsetOf(this));
}
ALWAYS_INLINE UnlinkedFunctionExecutable* CachedFunctionExecutable::decode(Decoder& decoder) const
{
UnlinkedFunctionExecutable* executable = new (NotNull, allocateCell<UnlinkedFunctionExecutable>(decoder.vm().heap)) UnlinkedFunctionExecutable(decoder, *this);
executable->finishCreation(decoder.vm());
return executable;
}
ALWAYS_INLINE UnlinkedFunctionExecutable::UnlinkedFunctionExecutable(Decoder& decoder, const CachedFunctionExecutable& cachedExecutable)
: Base(decoder.vm(), decoder.vm().unlinkedFunctionExecutableStructure.get())
, m_firstLineOffset(cachedExecutable.firstLineOffset())
, m_isInStrictContext(cachedExecutable.isInStrictContext())
, m_lineCount(cachedExecutable.lineCount())
, m_hasCapturedVariables(cachedExecutable.hasCapturedVariables())
, m_unlinkedFunctionNameStart(cachedExecutable.unlinkedFunctionNameStart())
, m_isBuiltinFunction(cachedExecutable.isBuiltinFunction())
, m_unlinkedBodyStartColumn(cachedExecutable.unlinkedBodyStartColumn())
, m_isBuiltinDefaultClassConstructor(cachedExecutable.isBuiltinDefaultClassConstructor())
, m_unlinkedBodyEndColumn(cachedExecutable.unlinkedBodyEndColumn())
, m_constructAbility(cachedExecutable.constructAbility())
, m_startOffset(cachedExecutable.startOffset())
, m_scriptMode(cachedExecutable.scriptMode())
, m_sourceLength(cachedExecutable.sourceLength())
, m_superBinding(cachedExecutable.superBinding())
, m_parametersStartOffset(cachedExecutable.parametersStartOffset())
, m_isCached(false)
, m_typeProfilingStartOffset(cachedExecutable.typeProfilingStartOffset())
, m_typeProfilingEndOffset(cachedExecutable.typeProfilingEndOffset())
, m_parameterCount(cachedExecutable.parameterCount())
, m_features(cachedExecutable.features())
, m_sourceParseMode(cachedExecutable.sourceParseMode())
, m_constructorKind(cachedExecutable.constructorKind())
, m_functionMode(cachedExecutable.functionMode())
, m_derivedContextType(cachedExecutable.derivedContextType())
, m_isGeneratedFromCache(true)
, m_unlinkedCodeBlockForCall()
, m_unlinkedCodeBlockForConstruct()
, m_name(cachedExecutable.name(decoder))
, m_ecmaName(cachedExecutable.ecmaName(decoder))
, m_rareData(cachedExecutable.rareData(decoder))
{
uint32_t leafExecutables = 2;
auto checkBounds = [&](int32_t& codeBlockOffset, auto& cachedPtr) {
if (!cachedPtr.isEmpty()) {
ptrdiff_t offset = decoder.offsetOf(&cachedPtr);
if (static_cast<size_t>(offset) < decoder.size()) {
codeBlockOffset = offset;
m_isCached = true;
leafExecutables--;
return;
}
}
codeBlockOffset = 0;
};
if (!cachedExecutable.unlinkedCodeBlockForCall().isEmpty() || !cachedExecutable.unlinkedCodeBlockForConstruct().isEmpty()) {
checkBounds(m_cachedCodeBlockForCallOffset, cachedExecutable.unlinkedCodeBlockForCall());
checkBounds(m_cachedCodeBlockForConstructOffset, cachedExecutable.unlinkedCodeBlockForConstruct());
if (m_isCached)
m_decoder = &decoder;
else
m_decoder = nullptr;
}
if (leafExecutables)
decoder.addLeafExecutable(this, decoder.offsetOf(&cachedExecutable));
}
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_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_hasTailCalls = codeBlock.m_hasTailCalls;
m_constructorKind = codeBlock.m_constructorKind;
m_derivedContextType = codeBlock.m_derivedContextType;
m_evalContextType = codeBlock.m_evalContextType;
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_codeGenerationMode = codeBlock.m_codeGenerationMode;
m_codeType = codeBlock.m_codeType;
m_metadata.encode(encoder, codeBlock.m_metadata.get());
m_rareData.encode(encoder, codeBlock.m_rareData.get());
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_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_identifiers.encode(encoder, codeBlock.m_identifiers);
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:
bool decode(Decoder&, std::pair<SourceCodeKey, UnlinkedCodeBlock*>&) const;
bool isStillValid(Decoder&, const SourceCodeKey&, CachedCodeBlockTag) const;
protected:
GenericCacheEntry(Encoder& encoder, CachedCodeBlockTag tag)
: m_tag(tag)
{
m_bootSessionUUID.encode(encoder, bootSessionUUIDString());
}
CachedCodeBlockTag tag() const { return m_tag; }
bool isUpToDate(Decoder& decoder) const
{
if (m_cacheVersion != JSC_BYTECODE_CACHE_VERSION)
return false;
if (m_bootSessionUUID.decode(decoder) != bootSessionUUIDString())
return false;
return true;
}
private:
uint32_t m_cacheVersion { JSC_BYTECODE_CACHE_VERSION };
CachedString m_bootSessionUUID;
CachedCodeBlockTag m_tag;
};
template<typename UnlinkedCodeBlockType>
class CacheEntry : public GenericCacheEntry {
public:
CacheEntry(Encoder& encoder)
: GenericCacheEntry(encoder, CachedCodeBlockTypeImpl<UnlinkedCodeBlockType>::tag)
{
}
void encode(Encoder& encoder, std::pair<SourceCodeKey, const UnlinkedCodeBlockType*> pair)
{
m_key.encode(encoder, pair.first);
m_codeBlock.encode(encoder, pair.second);
}
private:
friend GenericCacheEntry;
bool isStillValid(Decoder& decoder, const SourceCodeKey& key) const
{
SourceCodeKey decodedKey;
m_key.decode(decoder, decodedKey);
return decodedKey == key;
}
bool decode(Decoder& decoder, std::pair<SourceCodeKey, UnlinkedCodeBlockType*>& result) const
{
ASSERT(tag() == CachedCodeBlockTypeImpl<UnlinkedCodeBlockType>::tag);
SourceCodeKey decodedKey;
m_key.decode(decoder, decodedKey);
result = { WTFMove(decodedKey), m_codeBlock.decode(decoder) };
return true;
}
CachedSourceCodeKey m_key;
CachedPtr<CachedCodeBlockType<UnlinkedCodeBlockType>> m_codeBlock;
};
bool GenericCacheEntry::decode(Decoder& decoder, std::pair<SourceCodeKey, UnlinkedCodeBlock*>& result) const
{
if (!isUpToDate(decoder))
return false;
switch (m_tag) {
case CachedProgramCodeBlockTag:
return bitwise_cast<const CacheEntry<UnlinkedProgramCodeBlock>*>(this)->decode(decoder, reinterpret_cast<std::pair<SourceCodeKey, UnlinkedProgramCodeBlock*>&>(result));
case CachedModuleCodeBlockTag:
return bitwise_cast<const CacheEntry<UnlinkedModuleProgramCodeBlock>*>(this)->decode(decoder, reinterpret_cast<std::pair<SourceCodeKey, UnlinkedModuleProgramCodeBlock*>&>(result));
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 false;
#endif
}
bool GenericCacheEntry::isStillValid(Decoder& decoder, const SourceCodeKey& key, CachedCodeBlockTag tag) const
{
if (!isUpToDate(decoder))
return false;
switch (tag) {
case CachedProgramCodeBlockTag:
return bitwise_cast<const CacheEntry<UnlinkedProgramCodeBlock>*>(this)->isStillValid(decoder, key);
case CachedModuleCodeBlockTag:
return bitwise_cast<const CacheEntry<UnlinkedModuleProgramCodeBlock>*>(this)->isStillValid(decoder, key);
case CachedEvalCodeBlockTag:
// We do not cache eval code blocks
RELEASE_ASSERT_NOT_REACHED();
}
RELEASE_ASSERT_NOT_REACHED();
return false;
}
template<typename UnlinkedCodeBlockType>
void encodeCodeBlock(Encoder& encoder, const SourceCodeKey& key, const UnlinkedCodeBlock* codeBlock)
{
auto* entry = encoder.template malloc<CacheEntry<UnlinkedCodeBlockType>>(encoder);
entry->encode(encoder, { key, jsCast<const UnlinkedCodeBlockType*>(codeBlock) });
}
RefPtr<CachedBytecode> encodeCodeBlock(VM& vm, const SourceCodeKey& key, const UnlinkedCodeBlock* codeBlock, FileSystem::PlatformFileHandle fd, BytecodeCacheError& error)
{
const ClassInfo* classInfo = codeBlock->classInfo(vm);
Encoder encoder(vm, fd);
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(error);
}
RefPtr<CachedBytecode> encodeCodeBlock(VM& vm, const SourceCodeKey& key, const UnlinkedCodeBlock* codeBlock)
{
BytecodeCacheError error;
return encodeCodeBlock(vm, key, codeBlock, FileSystem::invalidPlatformFileHandle, error);
}
RefPtr<CachedBytecode> encodeFunctionCodeBlock(VM& vm, const UnlinkedFunctionCodeBlock* codeBlock, BytecodeCacheError& error)
{
Encoder encoder(vm);
encoder.malloc<CachedFunctionCodeBlock>()->encode(encoder, *codeBlock);
return encoder.release(error);
}
UnlinkedCodeBlock* decodeCodeBlockImpl(VM& vm, const SourceCodeKey& key, Ref<CachedBytecode> cachedBytecode)
{
const auto* cachedEntry = bitwise_cast<const GenericCacheEntry*>(cachedBytecode->data());
Ref<Decoder> decoder = Decoder::create(vm, WTFMove(cachedBytecode), &key.source().provider());
std::pair<SourceCodeKey, UnlinkedCodeBlock*> entry;
{
DeferGC deferGC(vm.heap);
if (!cachedEntry->decode(decoder.get(), entry))
return nullptr;
}
if (entry.first != key)
return nullptr;
return entry.second;
}
bool isCachedBytecodeStillValid(VM& vm, Ref<CachedBytecode> cachedBytecode, const SourceCodeKey& key, SourceCodeType type)
{
const void* buffer = cachedBytecode->data();
size_t size = cachedBytecode->size();
if (!size)
return false;
const auto* cachedEntry = bitwise_cast<const GenericCacheEntry*>(buffer);
Ref<Decoder> decoder = Decoder::create(vm, WTFMove(cachedBytecode));
return cachedEntry->isStillValid(decoder.get(), key, tagFromSourceCodeType(type));
}
void decodeFunctionCodeBlock(Decoder& decoder, int32_t cachedFunctionCodeBlockOffset, WriteBarrier<UnlinkedFunctionCodeBlock>& codeBlock, const JSCell* owner)
{
ASSERT(decoder.vm().heap.isDeferred());
auto* cachedCodeBlock = static_cast<const CachedWriteBarrier<CachedFunctionCodeBlock, UnlinkedFunctionCodeBlock>*>(decoder.ptrForOffsetFromBase(cachedFunctionCodeBlockOffset));
cachedCodeBlock->decode(decoder, codeBlock, owner);
}
} // namespace JSC