Reland "[offthread] Add SFI support to OffThreadFactory"

This is a reland of 453e1a3b03

Added canonical "empty" arrays to ScannerStream::ForTesting, for the
zero-length nullptr data case.

Original change's description:
> [offthread] Add SFI support to OffThreadFactory
>
> Add support for off-thread SharedFunctionInfo allocation, which
> includes UncompiledData and PreparseData allocation.
>
> Bug: chromium:1011762
> Change-Id: Ia10f9ce762c7d7eb1108b9e71da75131dce919b7
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2050393
> Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#66246}

TBR=ulan@chromium.org

Bug: chromium:1011762
Change-Id: I37d2c6b9317548922913887940a0164cc2067efb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2054085
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66253}
This commit is contained in:
Leszek Swirski 2020-02-13 10:39:04 +01:00 committed by Commit Bot
parent a44b20abe3
commit a046c5fcf9
23 changed files with 571 additions and 226 deletions

View File

@ -15,6 +15,7 @@
#include "src/codegen/label.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/execution/off-thread-isolate.h"
#include "src/heap/factory.h"
#include "src/objects/elements-kind.h"
#include "src/objects/function-syntax-kind.h"
@ -2208,8 +2209,10 @@ class FunctionLiteral final : public Expression {
// Empty handle means that the function does not have a shared name (i.e.
// the name will be set dynamically after creation of the function closure).
MaybeHandle<String> GetName(Isolate* isolate) const {
return raw_name_ ? raw_name_->AllocateFlat(isolate) : MaybeHandle<String>();
template <typename Isolate>
MaybeHandleFor<Isolate, String> GetName(Isolate* isolate) const {
return raw_name_ ? raw_name_->AllocateFlat(isolate)
: MaybeHandleFor<Isolate, String>();
}
bool has_shared_name() const { return raw_name_ != nullptr; }
const AstConsString* raw_name() const { return raw_name_; }
@ -2277,6 +2280,11 @@ class FunctionLiteral final : public Expression {
}
UNREACHABLE();
}
OffThreadHandle<String> GetInferredName(OffThreadIsolate* isolate) const {
DCHECK(inferred_name_.is_null());
DCHECK_NOT_NULL(raw_inferred_name_);
return raw_inferred_name_->GetString(isolate);
}
const AstConsString* raw_inferred_name() { return raw_inferred_name_; }
// Only one of {set_inferred_name, set_raw_inferred_name} should be called.

View File

@ -32,6 +32,7 @@
#include "src/objects/js-array-inl.h"
#include "src/objects/layout-descriptor.h"
#include "src/objects/objects-inl.h"
#include "src/roots/roots.h"
#ifdef V8_INTL_SUPPORT
#include "src/objects/js-break-iterator-inl.h"
#include "src/objects/js-collator-inl.h"
@ -816,12 +817,20 @@ void JSFunction::JSFunctionVerify(Isolate* isolate) {
}
void SharedFunctionInfo::SharedFunctionInfoVerify(Isolate* isolate) {
// TODO(leszeks): Add a TorqueGeneratedClassVerifier for OffThreadIsolate.
TorqueGeneratedClassVerifiers::SharedFunctionInfoVerify(*this, isolate);
this->SharedFunctionInfoVerify(ReadOnlyRoots(isolate));
}
void SharedFunctionInfo::SharedFunctionInfoVerify(OffThreadIsolate* isolate) {
this->SharedFunctionInfoVerify(ReadOnlyRoots(isolate));
}
void SharedFunctionInfo::SharedFunctionInfoVerify(ReadOnlyRoots roots) {
Object value = name_or_scope_info();
if (value.IsScopeInfo()) {
CHECK_LT(0, ScopeInfo::cast(value).length());
CHECK_NE(value, ReadOnlyRoots(isolate).empty_scope_info());
CHECK_NE(value, roots.empty_scope_info());
}
CHECK(HasWasmExportedFunctionData() || IsApiFunction() ||
@ -830,13 +839,13 @@ void SharedFunctionInfo::SharedFunctionInfoVerify(Isolate* isolate) {
HasUncompiledDataWithoutPreparseData() || HasWasmJSFunctionData() ||
HasWasmCapiFunctionData());
CHECK(script_or_debug_info().IsUndefined(isolate) ||
CHECK(script_or_debug_info().IsUndefined(roots) ||
script_or_debug_info().IsScript() || HasDebugInfo());
if (!is_compiled()) {
CHECK(!HasFeedbackMetadata());
CHECK(outer_scope_info().IsScopeInfo() ||
outer_scope_info().IsTheHole(isolate));
outer_scope_info().IsTheHole(roots));
} else if (HasBytecodeArray() && HasFeedbackMetadata()) {
CHECK(feedback_metadata().IsFeedbackMetadata());
}

View File

@ -1290,7 +1290,19 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
int GetNextScriptId();
#if V8_SFI_HAS_UNIQUE_ID
int GetNextUniqueSharedFunctionInfoId() { return next_unique_sfi_id_++; }
int GetNextUniqueSharedFunctionInfoId() {
int current_id = next_unique_sfi_id_.load(std::memory_order_relaxed);
int next_id;
do {
if (current_id >= Smi::kMaxValue) {
next_id = 0;
} else {
next_id = current_id + 1;
}
} while (!next_unique_sfi_id_.compare_exchange_weak(
current_id, next_id, std::memory_order_relaxed));
return current_id;
}
#endif
Address promise_hook_address() {
@ -1765,7 +1777,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
int next_optimization_id_ = 0;
#if V8_SFI_HAS_UNIQUE_ID
int next_unique_sfi_id_ = 0;
std::atomic<int> next_unique_sfi_id_;
#endif
// Vector of callbacks before a Call starts execution.

View File

@ -18,6 +18,12 @@ OffThreadIsolate::~OffThreadIsolate() { delete logger_; }
int OffThreadIsolate::GetNextScriptId() { return isolate_->GetNextScriptId(); }
#if V8_SFI_HAS_UNIQUE_ID
int OffThreadIsolate::GetNextUniqueSharedFunctionInfoId() {
return isolate_->GetNextUniqueSharedFunctionInfoId();
}
#endif // V8_SFI_HAS_UNIQUE_ID
bool OffThreadIsolate::NeedsSourcePositionsForProfiling() {
// TODO(leszeks): Figure out if it makes sense to check this asynchronously.
return isolate_->NeedsSourcePositionsForProfiling();

View File

@ -61,6 +61,9 @@ class V8_EXPORT_PRIVATE OffThreadIsolate final
}
int GetNextScriptId();
#if V8_SFI_HAS_UNIQUE_ID
int GetNextUniqueSharedFunctionInfoId();
#endif // V8_SFI_HAS_UNIQUE_ID
bool NeedsSourcePositionsForProfiling();
bool is_collecting_type_profile();

View File

@ -233,7 +233,7 @@ class OffThreadHandle {
}
private:
Address address_;
Address address_ = 0;
};
// A helper class which wraps an normal or off-thread handle, and returns one

View File

@ -114,6 +114,35 @@ HandleFor<Impl, FixedArrayBase> FactoryBase<Impl>::NewFixedDoubleArray(
return array;
}
template <typename Impl>
HandleFor<Impl, WeakFixedArray> FactoryBase<Impl>::NewWeakFixedArrayWithMap(
Map map, int length, AllocationType allocation) {
// Zero-length case must be handled outside.
DCHECK_LT(0, length);
DCHECK(ReadOnlyHeap::Contains(map));
HeapObject result =
AllocateRawArray(WeakFixedArray::SizeFor(length), allocation);
result.set_map_after_allocation(map, SKIP_WRITE_BARRIER);
HandleFor<Impl, WeakFixedArray> array =
handle(WeakFixedArray::cast(result), isolate());
array->set_length(length);
MemsetTagged(ObjectSlot(array->data_start()),
read_only_roots().undefined_value(), length);
return array;
}
template <typename Impl>
HandleFor<Impl, WeakFixedArray> FactoryBase<Impl>::NewWeakFixedArray(
int length, AllocationType allocation) {
DCHECK_LE(0, length);
if (length == 0) return impl()->empty_weak_fixed_array();
return NewWeakFixedArrayWithMap(read_only_roots().weak_fixed_array_map(),
length, allocation);
}
template <typename Impl>
HandleFor<Impl, Script> FactoryBase<Impl>::NewScript(
HandleFor<Impl, String> source) {
@ -148,6 +177,111 @@ HandleFor<Impl, Script> FactoryBase<Impl>::NewScriptWithId(
return script;
}
template <typename Impl>
HandleFor<Impl, SharedFunctionInfo>
FactoryBase<Impl>::NewSharedFunctionInfoForLiteral(
FunctionLiteral* literal, HandleFor<Impl, Script> script,
bool is_toplevel) {
FunctionKind kind = literal->kind();
HandleFor<Impl, SharedFunctionInfo> shared = NewSharedFunctionInfo(
literal->GetName(isolate()), MaybeHandleFor<Impl, Code>(),
Builtins::kCompileLazy, kind);
SharedFunctionInfo::InitFromFunctionLiteral(isolate(), shared, literal,
is_toplevel);
shared->SetScript(read_only_roots(), *script, literal->function_literal_id(),
false);
return shared;
}
template <typename Impl>
HandleFor<Impl, PreparseData> FactoryBase<Impl>::NewPreparseData(
int data_length, int children_length) {
int size = PreparseData::SizeFor(data_length, children_length);
HandleFor<Impl, PreparseData> result = handle(
PreparseData::cast(AllocateRawWithImmortalMap(
size, AllocationType::kOld, read_only_roots().preparse_data_map())),
isolate());
result->set_data_length(data_length);
result->set_children_length(children_length);
MemsetTagged(result->inner_data_start(), read_only_roots().null_value(),
children_length);
result->clear_padding();
return result;
}
template <typename Impl>
HandleFor<Impl, UncompiledDataWithoutPreparseData>
FactoryBase<Impl>::NewUncompiledDataWithoutPreparseData(
HandleFor<Impl, String> inferred_name, int32_t start_position,
int32_t end_position) {
HandleFor<Impl, UncompiledDataWithoutPreparseData> result = handle(
UncompiledDataWithoutPreparseData::cast(NewWithImmortalMap(
impl()->read_only_roots().uncompiled_data_without_preparse_data_map(),
AllocationType::kOld)),
isolate());
result->Init(impl(), *inferred_name, start_position, end_position);
return result;
}
template <typename Impl>
HandleFor<Impl, UncompiledDataWithPreparseData>
FactoryBase<Impl>::NewUncompiledDataWithPreparseData(
HandleFor<Impl, String> inferred_name, int32_t start_position,
int32_t end_position, HandleFor<Impl, PreparseData> preparse_data) {
HandleFor<Impl, UncompiledDataWithPreparseData> result = handle(
UncompiledDataWithPreparseData::cast(NewWithImmortalMap(
impl()->read_only_roots().uncompiled_data_with_preparse_data_map(),
AllocationType::kOld)),
isolate());
result->Init(impl(), *inferred_name, start_position, end_position,
*preparse_data);
return result;
}
template <typename Impl>
HandleFor<Impl, SharedFunctionInfo> FactoryBase<Impl>::NewSharedFunctionInfo(
MaybeHandleFor<Impl, String> maybe_name,
MaybeHandleFor<Impl, HeapObject> maybe_function_data,
int maybe_builtin_index, FunctionKind kind) {
HandleFor<Impl, SharedFunctionInfo> shared = NewSharedFunctionInfo();
// Function names are assumed to be flat elsewhere.
HandleFor<Impl, String> shared_name;
bool has_shared_name = maybe_name.ToHandle(&shared_name);
if (has_shared_name) {
DCHECK(shared_name->IsFlat());
shared->set_name_or_scope_info(*shared_name);
} else {
DCHECK_EQ(shared->name_or_scope_info(),
SharedFunctionInfo::kNoSharedNameSentinel);
}
HandleFor<Impl, HeapObject> function_data;
if (maybe_function_data.ToHandle(&function_data)) {
// If we pass function_data then we shouldn't pass a builtin index, and
// the function_data should not be code with a builtin.
DCHECK(!Builtins::IsBuiltinId(maybe_builtin_index));
DCHECK_IMPLIES(function_data->IsCode(),
!Code::cast(*function_data).is_builtin());
shared->set_function_data(*function_data);
} else if (Builtins::IsBuiltinId(maybe_builtin_index)) {
shared->set_builtin_id(maybe_builtin_index);
} else {
shared->set_builtin_id(Builtins::kIllegal);
}
shared->CalculateConstructAsBuiltin();
shared->set_kind(kind);
#ifdef VERIFY_HEAP
shared->SharedFunctionInfoVerify(isolate());
#endif // VERIFY_HEAP
return shared;
}
template <typename Impl>
HandleFor<Impl, ObjectBoilerplateDescription>
FactoryBase<Impl>::NewObjectBoilerplateDescription(int boilerplate,
@ -403,6 +537,26 @@ FactoryBase<Impl>::NewSourceTextModuleInfo() {
AllocationType::kOld));
}
template <typename Impl>
HandleFor<Impl, SharedFunctionInfo> FactoryBase<Impl>::NewSharedFunctionInfo() {
Map map = read_only_roots().shared_function_info_map();
HandleFor<Impl, SharedFunctionInfo> shared = handle(
SharedFunctionInfo::cast(NewWithImmortalMap(map, AllocationType::kOld)),
isolate());
int unique_id = -1;
#if V8_SFI_HAS_UNIQUE_ID
unique_id = isolate()->GetNextUniqueSharedFunctionInfoId();
#endif // V8_SFI_HAS_UNIQUE_ID
shared->Init(read_only_roots(), unique_id);
#ifdef VERIFY_HEAP
shared->SharedFunctionInfoVerify(isolate());
#endif // VERIFY_HEAP
return shared;
}
template <typename Impl>
HandleFor<Impl, SeqOneByteString>
FactoryBase<Impl>::AllocateRawOneByteInternalizedString(int length,

View File

@ -8,6 +8,7 @@
#include "src/base/export-template.h"
#include "src/common/globals.h"
#include "src/handles/handle-for.h"
#include "src/objects/function-kind.h"
#include "src/objects/instance-type.h"
#include "src/roots/roots.h"
@ -15,6 +16,8 @@ namespace v8 {
namespace internal {
class HeapObject;
class SharedFunctionInfo;
class FunctionLiteral;
class SeqOneByteString;
class SeqTwoByteString;
class FreshlyAllocatedBigInt;
@ -22,6 +25,9 @@ class ObjectBoilerplateDescription;
class ArrayBoilerplateDescription;
class TemplateObjectDescription;
class SourceTextModuleInfo;
class PreparseData;
class UncompiledDataWithoutPreparseData;
class UncompiledDataWithPreparseData;
template <typename Impl>
class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FactoryBase {
@ -73,6 +79,16 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FactoryBase {
HandleFor<Impl, FixedArrayBase> NewFixedDoubleArray(
int length, AllocationType allocation = AllocationType::kYoung);
// Allocates a weak fixed array-like object with given map and initialized
// with undefined values.
HandleFor<Impl, WeakFixedArray> NewWeakFixedArrayWithMap(
Map map, int length, AllocationType allocation = AllocationType::kYoung);
// Allocates a fixed array which may contain in-place weak references. The
// array is initialized with undefined values
HandleFor<Impl, WeakFixedArray> NewWeakFixedArray(
int length, AllocationType allocation = AllocationType::kYoung);
// Allocates a fixed array for name-value pairs of boilerplate properties and
// calculates the number of properties we need to store in the backing store.
HandleFor<Impl, ObjectBoilerplateDescription> NewObjectBoilerplateDescription(
@ -92,6 +108,24 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FactoryBase {
HandleFor<Impl, Script> NewScriptWithId(HandleFor<Impl, String> source,
int script_id);
HandleFor<Impl, SharedFunctionInfo> NewSharedFunctionInfoForLiteral(
FunctionLiteral* literal, HandleFor<Impl, Script> script,
bool is_toplevel);
HandleFor<Impl, PreparseData> NewPreparseData(int data_length,
int children_length);
HandleFor<Impl, UncompiledDataWithoutPreparseData>
NewUncompiledDataWithoutPreparseData(HandleFor<Impl, String> inferred_name,
int32_t start_position,
int32_t end_position);
HandleFor<Impl, UncompiledDataWithPreparseData>
NewUncompiledDataWithPreparseData(HandleFor<Impl, String> inferred_name,
int32_t start_position,
int32_t end_position,
HandleFor<Impl, PreparseData>);
HandleFor<Impl, SeqOneByteString> NewOneByteInternalizedString(
const Vector<const uint8_t>& str, uint32_t hash_field);
HandleFor<Impl, SeqTwoByteString> NewTwoByteInternalizedString(
@ -145,6 +179,12 @@ class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) FactoryBase {
HandleFor<Impl, FixedArray> NewFixedArrayWithFiller(
Map map, int length, Oddball filler, AllocationType allocation);
HandleFor<Impl, SharedFunctionInfo> NewSharedFunctionInfo();
HandleFor<Impl, SharedFunctionInfo> NewSharedFunctionInfo(
MaybeHandleFor<Impl, String> maybe_name,
MaybeHandleFor<Impl, HeapObject> maybe_function_data,
int maybe_builtin_index, FunctionKind kind = kNormalFunction);
private:
Impl* impl() { return static_cast<Impl*>(this); }
auto isolate() { return impl()->isolate(); }

View File

@ -10,7 +10,6 @@
#include <utility> // For move
#include "src/ast/ast-source-ranges.h"
#include "src/ast/ast.h"
#include "src/base/bits.h"
#include "src/builtins/accessors.h"
#include "src/builtins/constants-table-builder.h"
@ -341,40 +340,6 @@ Handle<FixedArray> Factory::NewFixedArrayWithMapRootIndex(
length, allocation);
}
template <typename T>
Handle<T> Factory::NewWeakFixedArrayWithMap(Map map, int length,
AllocationType allocation) {
static_assert(std::is_base_of<WeakFixedArray, T>::value,
"T must be a descendant of WeakFixedArray");
// Zero-length case must be handled outside.
DCHECK_LT(0, length);
HeapObject result =
AllocateRawArray(WeakFixedArray::SizeFor(length), AllocationType::kOld);
result.set_map_after_allocation(map, SKIP_WRITE_BARRIER);
Handle<WeakFixedArray> array(WeakFixedArray::cast(result), isolate());
array->set_length(length);
MemsetTagged(ObjectSlot(array->data_start()), *undefined_value(), length);
return Handle<T>::cast(array);
}
Handle<WeakFixedArray> Factory::NewWeakFixedArray(int length,
AllocationType allocation) {
DCHECK_LE(0, length);
if (length == 0) return empty_weak_fixed_array();
HeapObject result =
AllocateRawArray(WeakFixedArray::SizeFor(length), allocation);
result.set_map_after_allocation(read_only_roots().weak_fixed_array_map(),
SKIP_WRITE_BARRIER);
Handle<WeakFixedArray> array(WeakFixedArray::cast(result), isolate());
array->set_length(length);
MemsetTagged(ObjectSlot(array->data_start()), *undefined_value(), length);
return array;
}
MaybeHandle<FixedArray> Factory::TryNewFixedArray(
int length, AllocationType allocation_type) {
DCHECK_LE(0, length);
@ -1482,8 +1447,9 @@ Handle<DescriptorArray> Factory::NewDescriptorArray(int number_of_descriptors,
Handle<TransitionArray> Factory::NewTransitionArray(int number_of_transitions,
int slack) {
int capacity = TransitionArray::LengthFor(number_of_transitions + slack);
Handle<TransitionArray> array = NewWeakFixedArrayWithMap<TransitionArray>(
read_only_roots().transition_array_map(), capacity, AllocationType::kOld);
Handle<TransitionArray> array = Handle<TransitionArray>::cast(
NewWeakFixedArrayWithMap(read_only_roots().transition_array_map(),
capacity, AllocationType::kOld));
// Transition arrays are AllocationType::kOld. When black allocation is on we
// have to add the transition array to the list of
// encountered_transition_arrays.
@ -2130,48 +2096,6 @@ Handle<JSFunction> Factory::NewFunctionFromSharedFunctionInfo(
return result;
}
Handle<PreparseData> Factory::NewPreparseData(int data_length,
int children_length) {
int size = PreparseData::SizeFor(data_length, children_length);
Handle<PreparseData> result(
PreparseData::cast(AllocateRawWithImmortalMap(size, AllocationType::kOld,
*preparse_data_map())),
isolate());
result->set_data_length(data_length);
result->set_children_length(children_length);
MemsetTagged(result->inner_data_start(), *null_value(), children_length);
result->clear_padding();
return result;
}
Handle<UncompiledDataWithoutPreparseData>
Factory::NewUncompiledDataWithoutPreparseData(Handle<String> inferred_name,
int32_t start_position,
int32_t end_position) {
Handle<UncompiledDataWithoutPreparseData> result(
UncompiledDataWithoutPreparseData::cast(New(
uncompiled_data_without_preparse_data_map(), AllocationType::kOld)),
isolate());
result->Init(*inferred_name, start_position, end_position);
return result;
}
Handle<UncompiledDataWithPreparseData>
Factory::NewUncompiledDataWithPreparseData(Handle<String> inferred_name,
int32_t start_position,
int32_t end_position,
Handle<PreparseData> preparse_data) {
Handle<UncompiledDataWithPreparseData> result(
UncompiledDataWithPreparseData::cast(
New(uncompiled_data_with_preparse_data_map(), AllocationType::kOld)),
isolate());
result->Init(*inferred_name, start_position, end_position, *preparse_data);
return result;
}
Handle<JSObject> Factory::NewExternal(void* value) {
Handle<Foreign> foreign = NewForeign(reinterpret_cast<Address>(value));
Handle<JSObject> external = NewJSObjectFromMap(external_map());
@ -2965,18 +2889,6 @@ void Factory::ReinitializeJSGlobalProxy(Handle<JSGlobalProxy> object,
InitializeJSObjectFromMap(object, raw_properties_or_hash, map);
}
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfoForLiteral(
FunctionLiteral* literal, Handle<Script> script, bool is_toplevel) {
FunctionKind kind = literal->kind();
Handle<SharedFunctionInfo> shared = NewSharedFunctionInfoForBuiltin(
literal->GetName(isolate()), Builtins::kCompileLazy, kind);
SharedFunctionInfo::InitFromFunctionLiteral(isolate(), shared, literal,
is_toplevel);
shared->SetScript(ReadOnlyRoots(isolate()), *script,
literal->function_literal_id(), false);
return shared;
}
Handle<JSMessageObject> Factory::NewJSMessageObject(
MessageTemplate message, Handle<Object> argument, int start_position,
int end_position, Handle<SharedFunctionInfo> shared_info,
@ -3036,64 +2948,6 @@ Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfoForBuiltin(
return shared;
}
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
MaybeHandle<String> maybe_name, MaybeHandle<HeapObject> maybe_function_data,
int maybe_builtin_index, FunctionKind kind) {
Handle<SharedFunctionInfo> shared = NewSharedFunctionInfo();
// Function names are assumed to be flat elsewhere. Must flatten before
// allocating SharedFunctionInfo to avoid GC seeing the uninitialized SFI.
Handle<String> shared_name;
bool has_shared_name = maybe_name.ToHandle(&shared_name);
if (has_shared_name) {
DCHECK(shared_name->IsFlat());
shared->set_name_or_scope_info(*shared_name);
} else {
DCHECK_EQ(shared->name_or_scope_info(),
SharedFunctionInfo::kNoSharedNameSentinel);
}
Handle<HeapObject> function_data;
if (maybe_function_data.ToHandle(&function_data)) {
// If we pass function_data then we shouldn't pass a builtin index, and
// the function_data should not be code with a builtin.
DCHECK(!Builtins::IsBuiltinId(maybe_builtin_index));
DCHECK_IMPLIES(function_data->IsCode(),
!Code::cast(*function_data).is_builtin());
shared->set_function_data(*function_data);
} else if (Builtins::IsBuiltinId(maybe_builtin_index)) {
shared->set_builtin_id(maybe_builtin_index);
} else {
shared->set_builtin_id(Builtins::kIllegal);
}
shared->CalculateConstructAsBuiltin();
shared->set_kind(kind);
#ifdef VERIFY_HEAP
shared->SharedFunctionInfoVerify(isolate());
#endif // VERIFY_HEAP
return shared;
}
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo() {
Handle<Map> map = shared_function_info_map();
Handle<SharedFunctionInfo> shared(
SharedFunctionInfo::cast(New(map, AllocationType::kOld)), isolate());
int unique_id = -1;
#if V8_SFI_HAS_UNIQUE_ID
unique_id = isolate()->GetNextUniqueSharedFunctionInfoId();
#endif // V8_SFI_HAS_UNIQUE_ID
shared->Init(ReadOnlyRoots(isolate()), unique_id);
#ifdef VERIFY_HEAP
shared->SharedFunctionInfoVerify(isolate());
#endif // VERIFY_HEAP
return shared;
}
namespace {
inline int NumberToStringCacheHash(Handle<FixedArray> cache, Smi number) {
int mask = (cache->length() >> 1) - 1;

View File

@ -16,7 +16,6 @@
#include "src/heap/heap.h"
#include "src/objects/code.h"
#include "src/objects/dictionary.h"
#include "src/objects/function-kind.h"
#include "src/objects/js-array.h"
#include "src/objects/js-regexp.h"
#include "src/objects/string.h"
@ -55,7 +54,6 @@ class JSWeakMap;
class LoadHandler;
class NativeContext;
class NewFunctionArgs;
class PreparseData;
class PromiseResolveThenableJobTask;
class RegExpMatchInfo;
class ScriptContextTable;
@ -65,8 +63,6 @@ class StackTraceFrame;
class StoreHandler;
class SyntheticModule;
class TemplateObjectDescription;
class UncompiledDataWithoutPreparseData;
class UncompiledDataWithPreparseData;
class WasmCapiFunctionData;
class WasmExportedFunctionData;
class WasmJSFunctionData;
@ -134,17 +130,6 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
RootIndex map_root_index, int length,
AllocationType allocation = AllocationType::kYoung);
// Allocates a weak fixed array-like object with given map and initialized
// with undefined values.
template <typename T = WeakFixedArray>
Handle<T> NewWeakFixedArrayWithMap(
Map map, int length, AllocationType allocation = AllocationType::kYoung);
// Allocates a fixed array which may contain in-place weak references. The
// array is initialized with undefined values
Handle<WeakFixedArray> NewWeakFixedArray(
int length, AllocationType allocation = AllocationType::kYoung);
// Allocates a property array initialized with undefined values.
Handle<PropertyArray> NewPropertyArray(int length);
// Tries allocating a fixed array initialized with undefined values.
@ -674,17 +659,6 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
Handle<Map> map, Handle<SharedFunctionInfo> info, Handle<Context> context,
AllocationType allocation = AllocationType::kOld);
Handle<PreparseData> NewPreparseData(int data_length, int children_length);
Handle<UncompiledDataWithoutPreparseData>
NewUncompiledDataWithoutPreparseData(Handle<String> inferred_name,
int32_t start_position,
int32_t end_position);
Handle<UncompiledDataWithPreparseData> NewUncompiledDataWithPreparseData(
Handle<String> inferred_name, int32_t start_position,
int32_t end_position, Handle<PreparseData>);
// Create an External object for V8's external API.
Handle<JSObject> NewExternal(void* value);
@ -758,9 +732,6 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
MaybeHandle<String> name, int builtin_index,
FunctionKind kind = kNormalFunction);
Handle<SharedFunctionInfo> NewSharedFunctionInfoForLiteral(
FunctionLiteral* literal, Handle<Script> script, bool is_toplevel);
static bool IsFunctionModeWithPrototype(FunctionMode function_mode) {
return (function_mode & kWithPrototypeBits) != 0;
}
@ -1009,11 +980,6 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
ElementsKind elements_kind, int capacity,
ArrayStorageAllocationMode mode = DONT_INITIALIZE_ARRAY_ELEMENTS);
Handle<SharedFunctionInfo> NewSharedFunctionInfo();
Handle<SharedFunctionInfo> NewSharedFunctionInfo(
MaybeHandle<String> name, MaybeHandle<HeapObject> maybe_function_data,
int maybe_builtin_index, FunctionKind kind = kNormalFunction);
void InitializeAllocationMemento(AllocationMemento memento,
AllocationSite allocation_site);

View File

@ -2217,7 +2217,7 @@ void MarkCompactCollector::FlushBytecodeFromSFI(
// Initialize the uncompiled data.
UncompiledData uncompiled_data = UncompiledData::cast(compiled_data);
uncompiled_data.Init(
uncompiled_data.InitAfterBytecodeFlush(
inferred_name, start_position, end_position,
[](HeapObject object, ObjectSlot slot, HeapObject target) {
RecordSlot(object, slot, target);

View File

@ -16,6 +16,7 @@
#include "src/objects/heap-object.h"
#include "src/objects/map-inl.h"
#include "src/objects/objects-body-descriptors-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/string.h"
#include "src/objects/visitors.h"
#include "src/roots/roots-inl.h"

View File

@ -17,6 +17,7 @@
#include "src/objects/heap-object.h"
#include "src/objects/map.h"
#include "src/objects/objects.h"
#include "src/objects/shared-function-info.h"
#include "src/roots/roots.h"
namespace v8 {

View File

@ -37,7 +37,8 @@ void FreeSpace::set_next(FreeSpace next) {
}
FreeSpace FreeSpace::cast(HeapObject o) {
SLOW_DCHECK(!GetHeapFromWritableObject(o)->deserialization_complete() ||
SLOW_DCHECK((!Heap::InOffThreadSpace(o) &&
!GetHeapFromWritableObject(o)->deserialization_complete()) ||
o.IsFreeSpace());
return bit_cast<FreeSpace>(o);
}

View File

@ -33,6 +33,7 @@
#include "src/execution/frames-inl.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/off-thread-isolate.h"
#include "src/execution/protectors-inl.h"
#include "src/heap/factory-inl.h"
#include "src/heap/heap-inl.h"
@ -5332,8 +5333,9 @@ void SharedFunctionInfo::DisableOptimization(BailoutReason reason) {
}
// static
template <typename Isolate>
void SharedFunctionInfo::InitFromFunctionLiteral(
Isolate* isolate, Handle<SharedFunctionInfo> shared_info,
Isolate* isolate, HandleFor<Isolate, SharedFunctionInfo> shared_info,
FunctionLiteral* lit, bool is_toplevel) {
DCHECK(!shared_info->name_or_scope_info().IsScopeInfo());
@ -5387,12 +5389,12 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
shared_info->set_is_safe_to_skip_arguments_adaptor(false);
shared_info->UpdateExpectedNofPropertiesFromEstimate(lit);
Handle<UncompiledData> data;
HandleFor<Isolate, UncompiledData> data;
ProducedPreparseData* scope_data = lit->produced_preparse_data();
if (scope_data != nullptr) {
Handle<PreparseData> preparse_data =
scope_data->Serialize(shared_info->GetIsolate());
HandleFor<Isolate, PreparseData> preparse_data =
scope_data->Serialize(isolate);
data = isolate->factory()->NewUncompiledDataWithPreparseData(
lit->GetInferredName(isolate), lit->start_position(),
@ -5406,6 +5408,16 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
shared_info->set_uncompiled_data(*data);
}
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void SharedFunctionInfo::
InitFromFunctionLiteral<Isolate>(Isolate* isolate,
Handle<SharedFunctionInfo> shared_info,
FunctionLiteral* lit, bool is_toplevel);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void SharedFunctionInfo::
InitFromFunctionLiteral<OffThreadIsolate>(
OffThreadIsolate* isolate,
OffThreadHandle<SharedFunctionInfo> shared_info, FunctionLiteral* lit,
bool is_toplevel);
uint16_t SharedFunctionInfo::get_property_estimate_from_literal(
FunctionLiteral* literal) {
int estimate = literal->expected_property_count();

View File

@ -329,7 +329,7 @@ ScopeInfo SharedFunctionInfo::scope_info() const {
if (maybe_scope_info.IsScopeInfo()) {
return ScopeInfo::cast(maybe_scope_info);
}
return ScopeInfo::Empty(GetIsolate());
return GetReadOnlyRoots().empty_scope_info();
}
void SharedFunctionInfo::set_scope_info(ScopeInfo scope_info,
@ -619,7 +619,15 @@ void SharedFunctionInfo::ClearPreparseData() {
DCHECK(HasUncompiledDataWithoutPreparseData());
}
void UncompiledData::Init(
template <typename Isolate>
void UncompiledData::Init(Isolate* isolate, String inferred_name,
int start_position, int end_position) {
set_inferred_name(inferred_name);
set_start_position(start_position);
set_end_position(end_position);
}
void UncompiledData::InitAfterBytecodeFlush(
String inferred_name, int start_position, int end_position,
std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)>
gc_notify_updated_slot) {
@ -630,17 +638,14 @@ void UncompiledData::Init(
set_end_position(end_position);
}
void UncompiledDataWithPreparseData::Init(
String inferred_name, int start_position, int end_position,
PreparseData scope_data,
std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)>
gc_notify_updated_slot) {
this->UncompiledData::Init(inferred_name, start_position, end_position,
gc_notify_updated_slot);
template <typename Isolate>
void UncompiledDataWithPreparseData::Init(Isolate* isolate,
String inferred_name,
int start_position, int end_position,
PreparseData scope_data) {
this->UncompiledData::Init(isolate, inferred_name, start_position,
end_position);
set_preparse_data(scope_data);
gc_notify_updated_slot(
*this, RawField(UncompiledDataWithPreparseData::kPreparseDataOffset),
scope_data);
}
bool SharedFunctionInfo::HasWasmExportedFunctionData() const {

View File

@ -9,6 +9,7 @@
#include "src/base/bit-field.h"
#include "src/codegen/bailout-reason.h"
#include "src/handles/handle-for.h"
#include "src/objects/compressed-slots.h"
#include "src/objects/function-kind.h"
#include "src/objects/function-syntax-kind.h"
@ -99,11 +100,14 @@ class PreparseData
class UncompiledData
: public TorqueGeneratedUncompiledData<UncompiledData, HeapObject> {
public:
inline void Init(
template <typename Isolate>
inline void Init(Isolate* isolate, String inferred_name, int start_position,
int end_position);
inline void InitAfterBytecodeFlush(
String inferred_name, int start_position, int end_position,
std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)>
gc_notify_updated_slot =
[](HeapObject object, ObjectSlot slot, HeapObject target) {});
gc_notify_updated_slot);
using BodyDescriptor =
FixedBodyDescriptor<kStartOfStrongFieldsOffset, kEndOfStrongFieldsOffset,
@ -135,12 +139,9 @@ class UncompiledDataWithPreparseData
public:
DECL_PRINTER(UncompiledDataWithPreparseData)
inline void Init(
String inferred_name, int start_position, int end_position,
PreparseData scope_data,
std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)>
gc_notify_updated_slot =
[](HeapObject object, ObjectSlot slot, HeapObject target) {});
template <typename Isolate>
inline void Init(Isolate* isolate, String inferred_name, int start_position,
int end_position, PreparseData scope_data);
using BodyDescriptor = SubclassBodyDescriptor<
UncompiledData::BodyDescriptor,
@ -553,8 +554,9 @@ class SharedFunctionInfo : public HeapObject {
inline bool has_simple_parameters();
// Initialize a SharedFunctionInfo from a parsed function literal.
static void InitFromFunctionLiteral(Isolate* isolate,
Handle<SharedFunctionInfo> shared_info,
template <typename Isolate>
static void InitFromFunctionLiteral(
Isolate* isolate, HandleFor<Isolate, SharedFunctionInfo> shared_info,
FunctionLiteral* lit, bool is_toplevel);
// Updates the expected number of properties based on estimate from parser.
@ -587,6 +589,7 @@ class SharedFunctionInfo : public HeapObject {
// Dispatched behavior.
DECL_PRINTER(SharedFunctionInfo)
DECL_VERIFIER(SharedFunctionInfo)
void SharedFunctionInfoVerify(OffThreadIsolate* isolate);
#ifdef OBJECT_PRINT
void PrintSourceCode(std::ostream& os);
#endif
@ -638,6 +641,8 @@ class SharedFunctionInfo : public HeapObject {
inline bool needs_home_object() const;
private:
void SharedFunctionInfoVerify(ReadOnlyRoots roots);
// [name_or_scope_info]: Function name string, kNoSharedNameSentinel or
// ScopeInfo.
DECL_ACCESSORS(name_or_scope_info, Object)
@ -660,7 +665,8 @@ class SharedFunctionInfo : public HeapObject {
inline uint16_t get_property_estimate_from_literal(FunctionLiteral* literal);
friend class Factory;
template <typename Impl>
friend class FactoryBase;
friend class V8HeapExplorer;
FRIEND_TEST(PreParserTest, LazyFunctionLength);

View File

@ -199,6 +199,7 @@ class ZonePreparseData : public ZoneObject {
int child_length);
Handle<PreparseData> Serialize(Isolate* isolate);
OffThreadHandle<PreparseData> Serialize(OffThreadIsolate* isolate);
int children_length() const { return static_cast<int>(children_.size()); }

View File

@ -9,11 +9,13 @@
#include "src/ast/scopes.h"
#include "src/ast/variables.h"
#include "src/handles/handles.h"
#include "src/heap/off-thread-factory.h"
#include "src/objects/objects-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/parsing/parser.h"
#include "src/parsing/preparse-data-impl.h"
#include "src/parsing/preparser.h"
#include "src/roots/roots.h"
#include "src/zone/zone-list-inl.h" // crbug.com/v8/8816
namespace v8 {
@ -431,6 +433,17 @@ Handle<PreparseData> PreparseDataBuilder::ByteData::CopyToHeap(
return data;
}
OffThreadHandle<PreparseData>
PreparseDataBuilder::ByteData::CopyToOffThreadHeap(OffThreadIsolate* isolate,
int children_length) {
DCHECK(is_finalized_);
int data_length = zone_byte_data_.length();
OffThreadHandle<PreparseData> data =
isolate->factory()->NewPreparseData(data_length, children_length);
data->copy_in(0, zone_byte_data_.begin(), data_length);
return data;
}
Handle<PreparseData> PreparseDataBuilder::Serialize(Isolate* isolate) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
@ -447,6 +460,23 @@ Handle<PreparseData> PreparseDataBuilder::Serialize(Isolate* isolate) {
return data;
}
OffThreadHandle<PreparseData> PreparseDataBuilder::Serialize(
OffThreadIsolate* isolate) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
OffThreadHandle<PreparseData> data =
byte_data_.CopyToOffThreadHeap(isolate, num_inner_with_data_);
int i = 0;
DCHECK(finalized_children_);
for (const auto& builder : children_) {
if (!builder->HasData()) continue;
OffThreadHandle<PreparseData> child_data = builder->Serialize(isolate);
data->set_child(i++, *child_data);
}
DCHECK_EQ(i, data->children_length());
return data;
}
ZonePreparseData* PreparseDataBuilder::Serialize(Zone* zone) {
DCHECK(HasData());
DCHECK(!ThisOrParentBailedOut());
@ -473,6 +503,10 @@ class BuilderProducedPreparseData final : public ProducedPreparseData {
return builder_->Serialize(isolate);
}
OffThreadHandle<PreparseData> Serialize(OffThreadIsolate* isolate) final {
return builder_->Serialize(isolate);
}
ZonePreparseData* Serialize(Zone* zone) final {
return builder_->Serialize(zone);
}
@ -491,6 +525,11 @@ class OnHeapProducedPreparseData final : public ProducedPreparseData {
return data_;
}
OffThreadHandle<PreparseData> Serialize(OffThreadIsolate* isolate) final {
// Not required.
UNREACHABLE();
}
ZonePreparseData* Serialize(Zone* zone) final {
// Not required.
UNREACHABLE();
@ -508,6 +547,10 @@ class ZoneProducedPreparseData final : public ProducedPreparseData {
return data_->Serialize(isolate);
}
OffThreadHandle<PreparseData> Serialize(OffThreadIsolate* isolate) final {
return data_->Serialize(isolate);
}
ZonePreparseData* Serialize(Zone* zone) final { return data_; }
private:
@ -750,6 +793,23 @@ Handle<PreparseData> ZonePreparseData::Serialize(Isolate* isolate) {
return result;
}
OffThreadHandle<PreparseData> ZonePreparseData::Serialize(
OffThreadIsolate* isolate) {
int data_size = static_cast<int>(byte_data()->size());
int child_data_length = children_length();
OffThreadHandle<PreparseData> result =
isolate->factory()->NewPreparseData(data_size, child_data_length);
result->copy_in(0, byte_data()->data(), data_size);
for (int i = 0; i < child_data_length; i++) {
ZonePreparseData* child = get_child(i);
DCHECK_NOT_NULL(child);
OffThreadHandle<PreparseData> child_data = child->Serialize(isolate);
result->set_child(i, *child_data);
}
return result;
}
ZoneConsumedPreparseData::ZoneConsumedPreparseData(Zone* zone,
ZonePreparseData* data)
: data_(data), scope_data_wrapper_(data_->byte_data()) {

View File

@ -140,6 +140,8 @@ class V8_EXPORT_PRIVATE PreparseDataBuilder : public ZoneObject,
void Finalize(Zone* zone);
Handle<PreparseData> CopyToHeap(Isolate* isolate, int children_length);
OffThreadHandle<PreparseData> CopyToOffThreadHeap(OffThreadIsolate* isolate,
int children_length);
inline ZonePreparseData* CopyToZone(Zone* zone, int children_length);
void Reserve(size_t bytes);
@ -208,6 +210,7 @@ class V8_EXPORT_PRIVATE PreparseDataBuilder : public ZoneObject,
friend class BuilderProducedPreparseData;
Handle<PreparseData> Serialize(Isolate* isolate);
OffThreadHandle<PreparseData> Serialize(OffThreadIsolate* isolate);
ZonePreparseData* Serialize(Zone* zone);
void FinalizeChildren(Zone* zone);
@ -250,6 +253,12 @@ class ProducedPreparseData : public ZoneObject {
// MaybeHandle.
virtual Handle<PreparseData> Serialize(Isolate* isolate) = 0;
// If there is data (if the Scope contains skippable inner functions), move
// the data into the heap and return a Handle to it; otherwise return a null
// MaybeHandle.
virtual OffThreadHandle<PreparseData> Serialize(
OffThreadIsolate* isolate) = 0;
// If there is data (if the Scope contains skippable inner functions), return
// an off-heap ZonePreparseData representing the data; otherwise
// return nullptr.

View File

@ -792,10 +792,35 @@ std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
const char* data, size_t length) {
if (data == nullptr) {
DCHECK_EQ(length, 0);
// We don't want to pass in a null pointer into the the character stream,
// because then the one-past-the-end pointer is undefined, so instead pass
// through this static array.
static const char non_null_empty_string[1] = {0};
data = non_null_empty_string;
}
return std::unique_ptr<Utf16CharacterStream>(
new BufferedCharacterStream<TestingStream>(
static_cast<size_t>(0), reinterpret_cast<const uint8_t*>(data),
static_cast<size_t>(length)));
0, reinterpret_cast<const uint8_t*>(data), length));
}
std::unique_ptr<Utf16CharacterStream> ScannerStream::ForTesting(
const uint16_t* data, size_t length) {
if (data == nullptr) {
DCHECK_EQ(length, 0);
// We don't want to pass in a null pointer into the the character stream,
// because then the one-past-the-end pointer is undefined, so instead pass
// through this static array.
static const uint16_t non_null_empty_uint16_t_string[1] = {0};
data = non_null_empty_uint16_t_string;
}
return std::unique_ptr<Utf16CharacterStream>(
new UnbufferedCharacterStream<TestingStream>(0, data, length));
}
Utf16CharacterStream* ScannerStream::For(

View File

@ -31,6 +31,8 @@ class V8_EXPORT_PRIVATE ScannerStream {
static std::unique_ptr<Utf16CharacterStream> ForTesting(const char* data);
static std::unique_ptr<Utf16CharacterStream> ForTesting(const char* data,
size_t length);
static std::unique_ptr<Utf16CharacterStream> ForTesting(const uint16_t* data,
size_t length);
};
} // namespace internal

View File

@ -8,12 +8,25 @@
#include <memory>
#include "src/ast/ast-value-factory.h"
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/common/assert-scope.h"
#include "src/common/globals.h"
#include "src/execution/off-thread-isolate.h"
#include "src/handles/handles-inl.h"
#include "src/handles/handles.h"
#include "src/heap/off-thread-factory.h"
#include "src/heap/off-thread-factory-inl.h"
#include "src/objects/fixed-array.h"
#include "src/objects/script.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/string.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/parser.h"
#include "src/parsing/rewriter.h"
#include "src/parsing/scanner-character-streams.h"
#include "src/parsing/scanner.h"
#include "src/strings/unicode-inl.h"
#include "src/utils/utils.h"
#include "test/unittests/test-utils.h"
namespace v8 {
@ -21,10 +34,74 @@ namespace internal {
class OffThreadIsolate;
namespace {
std::vector<uint16_t> DecodeUtf8(const std::string& string) {
if (string.empty()) return {};
auto utf8_data =
Vector<const uint8_t>::cast(VectorOf(string.data(), string.length()));
Utf8Decoder decoder(utf8_data);
std::vector<uint16_t> utf16(decoder.utf16_length());
decoder.Decode(&utf16[0], utf8_data);
return utf16;
}
} // namespace
class OffThreadFactoryTest : public TestWithIsolateAndZone {
public:
OffThreadFactoryTest()
: TestWithIsolateAndZone(), off_thread_isolate_(isolate()) {}
: TestWithIsolateAndZone(),
off_thread_isolate_(isolate()),
parse_info_(isolate()) {}
FunctionLiteral* ParseProgram(const char* source) {
auto utf16_source = DecodeUtf8(source);
// Normally this would be an external string or whatever, we don't have to
// worry about it for now.
source_string_ =
factory()->NewStringFromUtf8(CStrVector(source)).ToHandleChecked();
parse_info_.set_character_stream(
ScannerStream::ForTesting(utf16_source.data(), utf16_source.size()));
parse_info_.set_toplevel();
parse_info_.set_allow_lazy_parsing();
{
DisallowHeapAllocation no_allocation;
DisallowHandleAllocation no_handles;
DisallowHeapAccess no_heap_access;
Parser parser(parse_info());
parser.InitializeEmptyScopeChain(parse_info());
parser.ParseOnBackground(parse_info());
CHECK(DeclarationScope::Analyze(parse_info()));
}
parse_info()->ast_value_factory()->Internalize(off_thread_isolate());
DeclarationScope::AllocateScopeInfos(parse_info(), off_thread_isolate());
script_ = parse_info_.CreateScript(off_thread_isolate(),
off_thread_factory()->empty_string(),
ScriptOriginOptions());
// Create the SFI list on the script so that SFI SetScript works.
OffThreadHandle<WeakFixedArray> infos =
off_thread_factory()->NewWeakFixedArray(
parse_info()->max_function_literal_id() + 1, AllocationType::kOld);
script_->set_shared_function_infos(*infos);
return parse_info()->literal();
}
ParseInfo* parse_info() { return &parse_info_; }
OffThreadHandle<Script> script() { return script_; }
OffThreadIsolate* off_thread_isolate() { return &off_thread_isolate_; }
OffThreadFactory* off_thread_factory() {
@ -41,6 +118,9 @@ class OffThreadFactoryTest : public TestWithIsolateAndZone {
private:
OffThreadIsolate off_thread_isolate_;
ParseInfo parse_info_;
Handle<String> source_string_;
OffThreadHandle<Script> script_;
};
TEST_F(OffThreadFactoryTest, HandleOrOffThreadHandle_IsNullWhenConstructed) {
@ -173,5 +253,95 @@ TEST_F(OffThreadFactoryTest, AstConsString_CreatesConsString) {
"foobar-plus-padding-for-length")));
}
TEST_F(OffThreadFactoryTest, EmptyScript) {
FunctionLiteral* program = ParseProgram("");
SharedFunctionInfo shared =
*off_thread_factory()->NewSharedFunctionInfoForLiteral(program, script(),
true);
off_thread_factory()->FinishOffThread();
Handle<SharedFunctionInfo> root_sfi = handle(shared, isolate());
off_thread_factory()->Publish(isolate());
EXPECT_EQ(root_sfi->function_literal_id(), 0);
}
TEST_F(OffThreadFactoryTest, LazyFunction) {
FunctionLiteral* program = ParseProgram("function lazy() {}");
FunctionLiteral* lazy = program->scope()
->declarations()
->AtForTest(0)
->AsFunctionDeclaration()
->fun();
SharedFunctionInfo shared =
*off_thread_factory()->NewSharedFunctionInfoForLiteral(lazy, script(),
true);
off_thread_factory()->FinishOffThread();
Handle<SharedFunctionInfo> lazy_sfi = handle(shared, isolate());
off_thread_factory()->Publish(isolate());
EXPECT_EQ(lazy_sfi->function_literal_id(), 1);
EXPECT_TRUE(lazy_sfi->Name().IsOneByteEqualTo(CStrVector("lazy")));
EXPECT_FALSE(lazy_sfi->is_compiled());
EXPECT_TRUE(lazy_sfi->HasUncompiledDataWithoutPreparseData());
}
TEST_F(OffThreadFactoryTest, EagerFunction) {
FunctionLiteral* program = ParseProgram("(function eager() {})");
FunctionLiteral* eager = program->body()
->at(0)
->AsExpressionStatement()
->expression()
->AsFunctionLiteral();
SharedFunctionInfo shared =
*off_thread_factory()->NewSharedFunctionInfoForLiteral(eager, script(),
true);
off_thread_factory()->FinishOffThread();
Handle<SharedFunctionInfo> eager_sfi = handle(shared, isolate());
off_thread_factory()->Publish(isolate());
EXPECT_EQ(eager_sfi->function_literal_id(), 1);
EXPECT_TRUE(eager_sfi->Name().IsOneByteEqualTo(CStrVector("eager")));
EXPECT_FALSE(eager_sfi->HasUncompiledData());
// TODO(leszeks): Allocate bytecode and enable these checks.
// EXPECT_TRUE(eager_sfi->is_compiled());
// EXPECT_TRUE(eager_sfi->HasBytecodeArray());
}
TEST_F(OffThreadFactoryTest, ImplicitNameFunction) {
FunctionLiteral* program = ParseProgram("let implicit_name = function() {}");
FunctionLiteral* implicit_name = program->body()
->at(0)
->AsBlock()
->statements()
->at(0)
->AsExpressionStatement()
->expression()
->AsAssignment()
->value()
->AsFunctionLiteral();
SharedFunctionInfo shared =
*off_thread_factory()->NewSharedFunctionInfoForLiteral(implicit_name,
script(), true);
off_thread_factory()->FinishOffThread();
Handle<SharedFunctionInfo> implicit_name_sfi = handle(shared, isolate());
off_thread_factory()->Publish(isolate());
EXPECT_EQ(implicit_name_sfi->function_literal_id(), 1);
EXPECT_TRUE(
implicit_name_sfi->Name().IsOneByteEqualTo(CStrVector("implicit_name")));
}
} // namespace internal
} // namespace v8