[objects] Add shared-function-info.cc
.. and move all SharedFunctionInfo implementations from objects.cc to the dedicated shared-function-info.cc. Drive-by: Also move remaining JSRegExp impls. Bug: v8:8888 Change-Id: I59adc3928f379eeb8b70f26d7e51d01c889c9a47 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2292240 Commit-Queue: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Cr-Commit-Position: refs/heads/master@{#68832}
This commit is contained in:
parent
0bebb1ad02
commit
b342a1203c
2
BUILD.gn
2
BUILD.gn
@ -1035,6 +1035,7 @@ action("postmortem-metadata") {
|
||||
"src/objects/scope-info.h",
|
||||
"src/objects/script.h",
|
||||
"src/objects/script-inl.h",
|
||||
"src/objects/shared-function-info.cc",
|
||||
"src/objects/shared-function-info.h",
|
||||
"src/objects/shared-function-info-inl.h",
|
||||
"src/objects/string.cc",
|
||||
@ -2898,6 +2899,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/objects/script-inl.h",
|
||||
"src/objects/script.h",
|
||||
"src/objects/shared-function-info-inl.h",
|
||||
"src/objects/shared-function-info.cc",
|
||||
"src/objects/shared-function-info.h",
|
||||
"src/objects/slots-atomic-inl.h",
|
||||
"src/objects/slots-inl.h",
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
MaybeHandle<JSArray> JSRegExpResult::GetAndCacheIndices(
|
||||
Isolate* isolate, Handle<JSRegExpResult> regexp_result) {
|
||||
// Check for cached indices. We do a slow lookup and set of
|
||||
@ -167,5 +168,325 @@ uint32_t JSRegExp::BacktrackLimit() const {
|
||||
return static_cast<uint32_t>(Smi::ToInt(DataAt(kIrregexpBacktrackLimit)));
|
||||
}
|
||||
|
||||
// static
|
||||
JSRegExp::Flags JSRegExp::FlagsFromString(Isolate* isolate,
|
||||
Handle<String> flags, bool* success) {
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('g') == JSRegExp::kGlobal);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('i') == JSRegExp::kIgnoreCase);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('m') == JSRegExp::kMultiline);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('s') == JSRegExp::kDotAll);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('u') == JSRegExp::kUnicode);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('y') == JSRegExp::kSticky);
|
||||
|
||||
int length = flags->length();
|
||||
if (length == 0) {
|
||||
*success = true;
|
||||
return JSRegExp::kNone;
|
||||
}
|
||||
// A longer flags string cannot be valid.
|
||||
if (length > JSRegExp::kFlagCount) return JSRegExp::Flags(0);
|
||||
JSRegExp::Flags value(0);
|
||||
if (flags->IsSeqOneByteString()) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
SeqOneByteString seq_flags = SeqOneByteString::cast(*flags);
|
||||
for (int i = 0; i < length; i++) {
|
||||
base::Optional<JSRegExp::Flag> maybe_flag =
|
||||
JSRegExp::FlagFromChar(seq_flags.Get(i));
|
||||
if (!maybe_flag.has_value()) return JSRegExp::Flags(0);
|
||||
JSRegExp::Flag flag = *maybe_flag;
|
||||
// Duplicate flag.
|
||||
if (value & flag) return JSRegExp::Flags(0);
|
||||
value |= flag;
|
||||
}
|
||||
} else {
|
||||
flags = String::Flatten(isolate, flags);
|
||||
DisallowHeapAllocation no_gc;
|
||||
String::FlatContent flags_content = flags->GetFlatContent(no_gc);
|
||||
for (int i = 0; i < length; i++) {
|
||||
base::Optional<JSRegExp::Flag> maybe_flag =
|
||||
JSRegExp::FlagFromChar(flags_content.Get(i));
|
||||
if (!maybe_flag.has_value()) return JSRegExp::Flags(0);
|
||||
JSRegExp::Flag flag = *maybe_flag;
|
||||
// Duplicate flag.
|
||||
if (value & flag) return JSRegExp::Flags(0);
|
||||
value |= flag;
|
||||
}
|
||||
}
|
||||
*success = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
// static
|
||||
MaybeHandle<JSRegExp> JSRegExp::New(Isolate* isolate, Handle<String> pattern,
|
||||
Flags flags, uint32_t backtrack_limit) {
|
||||
Handle<JSFunction> constructor = isolate->regexp_function();
|
||||
Handle<JSRegExp> regexp =
|
||||
Handle<JSRegExp>::cast(isolate->factory()->NewJSObject(constructor));
|
||||
|
||||
return JSRegExp::Initialize(regexp, pattern, flags, backtrack_limit);
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<JSRegExp> JSRegExp::Copy(Handle<JSRegExp> regexp) {
|
||||
Isolate* const isolate = regexp->GetIsolate();
|
||||
return Handle<JSRegExp>::cast(isolate->factory()->CopyJSObject(regexp));
|
||||
}
|
||||
|
||||
Object JSRegExp::Code(bool is_latin1) const {
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
return DataAt(code_index(is_latin1));
|
||||
}
|
||||
|
||||
Object JSRegExp::Bytecode(bool is_latin1) const {
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
return DataAt(bytecode_index(is_latin1));
|
||||
}
|
||||
|
||||
bool JSRegExp::ShouldProduceBytecode() {
|
||||
return FLAG_regexp_interpret_all ||
|
||||
(FLAG_regexp_tier_up && !MarkedForTierUp());
|
||||
}
|
||||
|
||||
// An irregexp is considered to be marked for tier up if the tier-up ticks value
|
||||
// reaches zero. An atom is not subject to tier-up implementation, so the
|
||||
// tier-up ticks value is not set.
|
||||
bool JSRegExp::MarkedForTierUp() {
|
||||
DCHECK(data().IsFixedArray());
|
||||
if (TypeTag() == JSRegExp::ATOM || !FLAG_regexp_tier_up) {
|
||||
return false;
|
||||
}
|
||||
return Smi::ToInt(DataAt(kIrregexpTicksUntilTierUpIndex)) == 0;
|
||||
}
|
||||
|
||||
void JSRegExp::ResetLastTierUpTick() {
|
||||
DCHECK(FLAG_regexp_tier_up);
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
int tier_up_ticks = Smi::ToInt(DataAt(kIrregexpTicksUntilTierUpIndex)) + 1;
|
||||
FixedArray::cast(data()).set(JSRegExp::kIrregexpTicksUntilTierUpIndex,
|
||||
Smi::FromInt(tier_up_ticks));
|
||||
}
|
||||
|
||||
void JSRegExp::TierUpTick() {
|
||||
DCHECK(FLAG_regexp_tier_up);
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
int tier_up_ticks = Smi::ToInt(DataAt(kIrregexpTicksUntilTierUpIndex));
|
||||
if (tier_up_ticks == 0) {
|
||||
return;
|
||||
}
|
||||
FixedArray::cast(data()).set(JSRegExp::kIrregexpTicksUntilTierUpIndex,
|
||||
Smi::FromInt(tier_up_ticks - 1));
|
||||
}
|
||||
|
||||
void JSRegExp::MarkTierUpForNextExec() {
|
||||
DCHECK(FLAG_regexp_tier_up);
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
FixedArray::cast(data()).set(JSRegExp::kIrregexpTicksUntilTierUpIndex,
|
||||
Smi::zero());
|
||||
}
|
||||
|
||||
// static
|
||||
MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp,
|
||||
Handle<String> source,
|
||||
Handle<String> flags_string) {
|
||||
Isolate* isolate = regexp->GetIsolate();
|
||||
bool success = false;
|
||||
Flags flags = JSRegExp::FlagsFromString(isolate, flags_string, &success);
|
||||
if (!success) {
|
||||
THROW_NEW_ERROR(
|
||||
isolate,
|
||||
NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string),
|
||||
JSRegExp);
|
||||
}
|
||||
return Initialize(regexp, source, flags);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsLineTerminator(int c) {
|
||||
// Expected to return true for '\n', '\r', 0x2028, and 0x2029.
|
||||
return unibrow::IsLineTerminator(static_cast<unibrow::uchar>(c));
|
||||
}
|
||||
|
||||
// TODO(jgruber): Consider merging CountAdditionalEscapeChars and
|
||||
// WriteEscapedRegExpSource into a single function to deduplicate dispatch logic
|
||||
// and move related code closer to each other.
|
||||
template <typename Char>
|
||||
int CountAdditionalEscapeChars(Handle<String> source, bool* needs_escapes_out) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
int escapes = 0;
|
||||
bool needs_escapes = false;
|
||||
bool in_char_class = false;
|
||||
Vector<const Char> src = source->GetCharVector<Char>(no_gc);
|
||||
for (int i = 0; i < src.length(); i++) {
|
||||
const Char c = src[i];
|
||||
if (c == '\\') {
|
||||
if (i + 1 < src.length() && IsLineTerminator(src[i + 1])) {
|
||||
// This '\' is ignored since the next character itself will be escaped.
|
||||
escapes--;
|
||||
} else {
|
||||
// Escape. Skip next character, which will be copied verbatim;
|
||||
i++;
|
||||
}
|
||||
} else if (c == '/' && !in_char_class) {
|
||||
// Not escaped forward-slash needs escape.
|
||||
needs_escapes = true;
|
||||
escapes++;
|
||||
} else if (c == '[') {
|
||||
in_char_class = true;
|
||||
} else if (c == ']') {
|
||||
in_char_class = false;
|
||||
} else if (c == '\n') {
|
||||
needs_escapes = true;
|
||||
escapes++;
|
||||
} else if (c == '\r') {
|
||||
needs_escapes = true;
|
||||
escapes++;
|
||||
} else if (static_cast<int>(c) == 0x2028) {
|
||||
needs_escapes = true;
|
||||
escapes += std::strlen("\\u2028") - 1;
|
||||
} else if (static_cast<int>(c) == 0x2029) {
|
||||
needs_escapes = true;
|
||||
escapes += std::strlen("\\u2029") - 1;
|
||||
} else {
|
||||
DCHECK(!IsLineTerminator(c));
|
||||
}
|
||||
}
|
||||
DCHECK(!in_char_class);
|
||||
DCHECK_GE(escapes, 0);
|
||||
DCHECK_IMPLIES(escapes != 0, needs_escapes);
|
||||
*needs_escapes_out = needs_escapes;
|
||||
return escapes;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void WriteStringToCharVector(Vector<Char> v, int* d, const char* string) {
|
||||
int s = 0;
|
||||
while (string[s] != '\0') v[(*d)++] = string[s++];
|
||||
}
|
||||
|
||||
template <typename Char, typename StringType>
|
||||
Handle<StringType> WriteEscapedRegExpSource(Handle<String> source,
|
||||
Handle<StringType> result) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Vector<const Char> src = source->GetCharVector<Char>(no_gc);
|
||||
Vector<Char> dst(result->GetChars(no_gc), result->length());
|
||||
int s = 0;
|
||||
int d = 0;
|
||||
bool in_char_class = false;
|
||||
while (s < src.length()) {
|
||||
const Char c = src[s];
|
||||
if (c == '\\') {
|
||||
if (s + 1 < src.length() && IsLineTerminator(src[s + 1])) {
|
||||
// This '\' is ignored since the next character itself will be escaped.
|
||||
s++;
|
||||
continue;
|
||||
} else {
|
||||
// Escape. Copy this and next character.
|
||||
dst[d++] = src[s++];
|
||||
}
|
||||
if (s == src.length()) break;
|
||||
} else if (c == '/' && !in_char_class) {
|
||||
// Not escaped forward-slash needs escape.
|
||||
dst[d++] = '\\';
|
||||
} else if (c == '[') {
|
||||
in_char_class = true;
|
||||
} else if (c == ']') {
|
||||
in_char_class = false;
|
||||
} else if (c == '\n') {
|
||||
WriteStringToCharVector(dst, &d, "\\n");
|
||||
s++;
|
||||
continue;
|
||||
} else if (c == '\r') {
|
||||
WriteStringToCharVector(dst, &d, "\\r");
|
||||
s++;
|
||||
continue;
|
||||
} else if (static_cast<int>(c) == 0x2028) {
|
||||
WriteStringToCharVector(dst, &d, "\\u2028");
|
||||
s++;
|
||||
continue;
|
||||
} else if (static_cast<int>(c) == 0x2029) {
|
||||
WriteStringToCharVector(dst, &d, "\\u2029");
|
||||
s++;
|
||||
continue;
|
||||
} else {
|
||||
DCHECK(!IsLineTerminator(c));
|
||||
}
|
||||
dst[d++] = src[s++];
|
||||
}
|
||||
DCHECK_EQ(result->length(), d);
|
||||
DCHECK(!in_char_class);
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeHandle<String> EscapeRegExpSource(Isolate* isolate,
|
||||
Handle<String> source) {
|
||||
DCHECK(source->IsFlat());
|
||||
if (source->length() == 0) return isolate->factory()->query_colon_string();
|
||||
bool one_byte = String::IsOneByteRepresentationUnderneath(*source);
|
||||
bool needs_escapes = false;
|
||||
int additional_escape_chars =
|
||||
one_byte ? CountAdditionalEscapeChars<uint8_t>(source, &needs_escapes)
|
||||
: CountAdditionalEscapeChars<uc16>(source, &needs_escapes);
|
||||
if (!needs_escapes) return source;
|
||||
int length = source->length() + additional_escape_chars;
|
||||
if (one_byte) {
|
||||
Handle<SeqOneByteString> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
|
||||
isolate->factory()->NewRawOneByteString(length),
|
||||
String);
|
||||
return WriteEscapedRegExpSource<uint8_t>(source, result);
|
||||
} else {
|
||||
Handle<SeqTwoByteString> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
|
||||
isolate->factory()->NewRawTwoByteString(length),
|
||||
String);
|
||||
return WriteEscapedRegExpSource<uc16>(source, result);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp,
|
||||
Handle<String> source, Flags flags,
|
||||
uint32_t backtrack_limit) {
|
||||
Isolate* isolate = regexp->GetIsolate();
|
||||
Factory* factory = isolate->factory();
|
||||
// If source is the empty string we set it to "(?:)" instead as
|
||||
// suggested by ECMA-262, 5th, section 15.10.4.1.
|
||||
if (source->length() == 0) source = factory->query_colon_string();
|
||||
|
||||
source = String::Flatten(isolate, source);
|
||||
|
||||
RETURN_ON_EXCEPTION(
|
||||
isolate, RegExp::Compile(isolate, regexp, source, flags, backtrack_limit),
|
||||
JSRegExp);
|
||||
|
||||
Handle<String> escaped_source;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, escaped_source,
|
||||
EscapeRegExpSource(isolate, source), JSRegExp);
|
||||
|
||||
regexp->set_source(*escaped_source);
|
||||
regexp->set_flags(Smi::FromInt(flags));
|
||||
|
||||
Map map = regexp->map();
|
||||
Object constructor = map.GetConstructor();
|
||||
if (constructor.IsJSFunction() &&
|
||||
JSFunction::cast(constructor).initial_map() == map) {
|
||||
// If we still have the original map, set in-object properties directly.
|
||||
regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, Smi::zero(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
} else {
|
||||
// Map has changed, so use generic, but slower, method.
|
||||
RETURN_ON_EXCEPTION(
|
||||
isolate,
|
||||
Object::SetProperty(isolate, regexp, factory->lastIndex_string(),
|
||||
Handle<Smi>(Smi::zero(), isolate)),
|
||||
JSRegExp);
|
||||
}
|
||||
|
||||
return regexp;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1254,8 +1254,6 @@ bool Object::ToInt32(int32_t* value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
V8_EXPORT_PRIVATE constexpr Smi SharedFunctionInfo::kNoSharedNameSentinel;
|
||||
|
||||
Handle<SharedFunctionInfo> FunctionTemplateInfo::GetOrCreateSharedFunctionInfo(
|
||||
Isolate* isolate, Handle<FunctionTemplateInfo> info,
|
||||
MaybeHandle<Name> maybe_name) {
|
||||
@ -4960,654 +4958,6 @@ Script Script::Iterator::Next() {
|
||||
return Script();
|
||||
}
|
||||
|
||||
uint32_t SharedFunctionInfo::Hash() {
|
||||
// Hash SharedFunctionInfo based on its start position and script id. Note: we
|
||||
// don't use the function's literal id since getting that is slow for compiled
|
||||
// funcitons.
|
||||
int start_pos = StartPosition();
|
||||
int script_id = script().IsScript() ? Script::cast(script()).id() : 0;
|
||||
return static_cast<uint32_t>(base::hash_combine(start_pos, script_id));
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::Init(ReadOnlyRoots ro_roots, int unique_id) {
|
||||
DisallowHeapAllocation no_allocation;
|
||||
|
||||
// Set the function data to the "illegal" builtin. Ideally we'd use some sort
|
||||
// of "uninitialized" marker here, but it's cheaper to use a valid buitin and
|
||||
// avoid having to do uninitialized checks elsewhere.
|
||||
set_builtin_id(Builtins::kIllegal);
|
||||
|
||||
// Set the name to the no-name sentinel, this can be updated later.
|
||||
set_name_or_scope_info(SharedFunctionInfo::kNoSharedNameSentinel,
|
||||
SKIP_WRITE_BARRIER);
|
||||
|
||||
// Generally functions won't have feedback, unless they have been created
|
||||
// from a FunctionLiteral. Those can just reset this field to keep the
|
||||
// SharedFunctionInfo in a consistent state.
|
||||
set_raw_outer_scope_info_or_feedback_metadata(ro_roots.the_hole_value(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
set_script_or_debug_info(ro_roots.undefined_value(), SKIP_WRITE_BARRIER);
|
||||
set_function_literal_id(kFunctionLiteralIdInvalid);
|
||||
#if V8_SFI_HAS_UNIQUE_ID
|
||||
set_unique_id(unique_id);
|
||||
#endif
|
||||
|
||||
// Set integer fields (smi or int, depending on the architecture).
|
||||
set_length(0);
|
||||
set_internal_formal_parameter_count(0);
|
||||
set_expected_nof_properties(0);
|
||||
set_raw_function_token_offset(0);
|
||||
|
||||
// All flags default to false or 0, except ConstructAsBuiltinBit just because
|
||||
// we're using the kIllegal builtin.
|
||||
set_flags(ConstructAsBuiltinBit::encode(true));
|
||||
set_flags2(0);
|
||||
|
||||
UpdateFunctionMapIndex();
|
||||
|
||||
clear_padding();
|
||||
}
|
||||
|
||||
Code SharedFunctionInfo::GetCode() const {
|
||||
// ======
|
||||
// NOTE: This chain of checks MUST be kept in sync with the equivalent CSA
|
||||
// GetSharedFunctionInfoCode method in code-stub-assembler.cc.
|
||||
// ======
|
||||
|
||||
Isolate* isolate = GetIsolate();
|
||||
Object data = function_data();
|
||||
if (data.IsSmi()) {
|
||||
// Holding a Smi means we are a builtin.
|
||||
DCHECK(HasBuiltinId());
|
||||
return isolate->builtins()->builtin(builtin_id());
|
||||
} else if (data.IsBytecodeArray()) {
|
||||
// Having a bytecode array means we are a compiled, interpreted function.
|
||||
DCHECK(HasBytecodeArray());
|
||||
return isolate->builtins()->builtin(Builtins::kInterpreterEntryTrampoline);
|
||||
} else if (data.IsAsmWasmData()) {
|
||||
// Having AsmWasmData means we are an asm.js/wasm function.
|
||||
DCHECK(HasAsmWasmData());
|
||||
return isolate->builtins()->builtin(Builtins::kInstantiateAsmJs);
|
||||
} else if (data.IsUncompiledData()) {
|
||||
// Having uncompiled data (with or without scope) means we need to compile.
|
||||
DCHECK(HasUncompiledData());
|
||||
return isolate->builtins()->builtin(Builtins::kCompileLazy);
|
||||
} else if (data.IsFunctionTemplateInfo()) {
|
||||
// Having a function template info means we are an API function.
|
||||
DCHECK(IsApiFunction());
|
||||
return isolate->builtins()->builtin(Builtins::kHandleApiCall);
|
||||
} else if (data.IsWasmExportedFunctionData()) {
|
||||
// Having a WasmExportedFunctionData means the code is in there.
|
||||
DCHECK(HasWasmExportedFunctionData());
|
||||
return wasm_exported_function_data().wrapper_code();
|
||||
} else if (data.IsInterpreterData()) {
|
||||
Code code = InterpreterTrampoline();
|
||||
DCHECK(code.IsCode());
|
||||
DCHECK(code.is_interpreter_trampoline_builtin());
|
||||
return code;
|
||||
} else if (data.IsWasmJSFunctionData()) {
|
||||
return wasm_js_function_data().wrapper_code();
|
||||
} else if (data.IsWasmCapiFunctionData()) {
|
||||
return wasm_capi_function_data().wrapper_code();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
WasmExportedFunctionData SharedFunctionInfo::wasm_exported_function_data()
|
||||
const {
|
||||
DCHECK(HasWasmExportedFunctionData());
|
||||
return WasmExportedFunctionData::cast(function_data());
|
||||
}
|
||||
|
||||
WasmJSFunctionData SharedFunctionInfo::wasm_js_function_data() const {
|
||||
DCHECK(HasWasmJSFunctionData());
|
||||
return WasmJSFunctionData::cast(function_data());
|
||||
}
|
||||
|
||||
WasmCapiFunctionData SharedFunctionInfo::wasm_capi_function_data() const {
|
||||
DCHECK(HasWasmCapiFunctionData());
|
||||
return WasmCapiFunctionData::cast(function_data());
|
||||
}
|
||||
|
||||
SharedFunctionInfo::ScriptIterator::ScriptIterator(Isolate* isolate,
|
||||
Script script)
|
||||
: ScriptIterator(handle(script.shared_function_infos(), isolate)) {}
|
||||
|
||||
SharedFunctionInfo::ScriptIterator::ScriptIterator(
|
||||
Handle<WeakFixedArray> shared_function_infos)
|
||||
: shared_function_infos_(shared_function_infos), index_(0) {}
|
||||
|
||||
SharedFunctionInfo SharedFunctionInfo::ScriptIterator::Next() {
|
||||
while (index_ < shared_function_infos_->length()) {
|
||||
MaybeObject raw = shared_function_infos_->Get(index_++);
|
||||
HeapObject heap_object;
|
||||
if (!raw->GetHeapObject(&heap_object) || heap_object.IsUndefined()) {
|
||||
continue;
|
||||
}
|
||||
return SharedFunctionInfo::cast(heap_object);
|
||||
}
|
||||
return SharedFunctionInfo();
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::ScriptIterator::Reset(Isolate* isolate,
|
||||
Script script) {
|
||||
shared_function_infos_ = handle(script.shared_function_infos(), isolate);
|
||||
index_ = 0;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::SetScript(ReadOnlyRoots roots,
|
||||
HeapObject script_object,
|
||||
int function_literal_id,
|
||||
bool reset_preparsed_scope_data) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
if (script() == script_object) return;
|
||||
|
||||
if (reset_preparsed_scope_data && HasUncompiledDataWithPreparseData()) {
|
||||
ClearPreparseData();
|
||||
}
|
||||
|
||||
// Add shared function info to new script's list. If a collection occurs,
|
||||
// the shared function info may be temporarily in two lists.
|
||||
// This is okay because the gc-time processing of these lists can tolerate
|
||||
// duplicates.
|
||||
if (script_object.IsScript()) {
|
||||
DCHECK(!script().IsScript());
|
||||
Script script = Script::cast(script_object);
|
||||
WeakFixedArray list = script.shared_function_infos();
|
||||
#ifdef DEBUG
|
||||
DCHECK_LT(function_literal_id, list.length());
|
||||
MaybeObject maybe_object = list.Get(function_literal_id);
|
||||
HeapObject heap_object;
|
||||
if (maybe_object->GetHeapObjectIfWeak(&heap_object)) {
|
||||
DCHECK_EQ(heap_object, *this);
|
||||
}
|
||||
#endif
|
||||
list.Set(function_literal_id, HeapObjectReference::Weak(*this));
|
||||
} else {
|
||||
DCHECK(script().IsScript());
|
||||
|
||||
// Remove shared function info from old script's list.
|
||||
Script old_script = Script::cast(script());
|
||||
|
||||
// Due to liveedit, it might happen that the old_script doesn't know
|
||||
// about the SharedFunctionInfo, so we have to guard against that.
|
||||
WeakFixedArray infos = old_script.shared_function_infos();
|
||||
if (function_literal_id < infos.length()) {
|
||||
MaybeObject raw =
|
||||
old_script.shared_function_infos().Get(function_literal_id);
|
||||
HeapObject heap_object;
|
||||
if (raw->GetHeapObjectIfWeak(&heap_object) && heap_object == *this) {
|
||||
old_script.shared_function_infos().Set(
|
||||
function_literal_id,
|
||||
HeapObjectReference::Strong(roots.undefined_value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally set new script.
|
||||
set_script(script_object);
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::HasBreakInfo() const {
|
||||
if (!HasDebugInfo()) return false;
|
||||
DebugInfo info = GetDebugInfo();
|
||||
bool has_break_info = info.HasBreakInfo();
|
||||
return has_break_info;
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::BreakAtEntry() const {
|
||||
if (!HasDebugInfo()) return false;
|
||||
DebugInfo info = GetDebugInfo();
|
||||
bool break_at_entry = info.BreakAtEntry();
|
||||
return break_at_entry;
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::HasCoverageInfo() const {
|
||||
if (!HasDebugInfo()) return false;
|
||||
DebugInfo info = GetDebugInfo();
|
||||
bool has_coverage_info = info.HasCoverageInfo();
|
||||
return has_coverage_info;
|
||||
}
|
||||
|
||||
CoverageInfo SharedFunctionInfo::GetCoverageInfo() const {
|
||||
DCHECK(HasCoverageInfo());
|
||||
return CoverageInfo::cast(GetDebugInfo().coverage_info());
|
||||
}
|
||||
|
||||
String SharedFunctionInfo::DebugName() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
String function_name = Name();
|
||||
if (function_name.length() > 0) return function_name;
|
||||
return inferred_name();
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::PassesFilter(const char* raw_filter) {
|
||||
Vector<const char> filter = CStrVector(raw_filter);
|
||||
std::unique_ptr<char[]> cstrname(DebugName().ToCString());
|
||||
return v8::internal::PassesFilter(CStrVector(cstrname.get()), filter);
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::HasSourceCode() const {
|
||||
ReadOnlyRoots roots = GetReadOnlyRoots();
|
||||
return !script().IsUndefined(roots) &&
|
||||
!Script::cast(script()).source().IsUndefined(roots) &&
|
||||
String::cast(Script::cast(script()).source()).length() > 0;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::DiscardCompiledMetadata(
|
||||
Isolate* isolate,
|
||||
std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)>
|
||||
gc_notify_updated_slot) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (is_compiled()) {
|
||||
HeapObject outer_scope_info;
|
||||
if (scope_info().HasOuterScopeInfo()) {
|
||||
outer_scope_info = scope_info().OuterScopeInfo();
|
||||
} else {
|
||||
outer_scope_info = ReadOnlyRoots(isolate).the_hole_value();
|
||||
}
|
||||
|
||||
// Raw setter to avoid validity checks, since we're performing the unusual
|
||||
// task of decompiling.
|
||||
set_raw_outer_scope_info_or_feedback_metadata(outer_scope_info);
|
||||
gc_notify_updated_slot(
|
||||
*this,
|
||||
RawField(SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset),
|
||||
outer_scope_info);
|
||||
} else {
|
||||
DCHECK(outer_scope_info().IsScopeInfo() || outer_scope_info().IsTheHole());
|
||||
}
|
||||
|
||||
// TODO(rmcilroy): Possibly discard ScopeInfo here as well.
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedFunctionInfo::DiscardCompiled(
|
||||
Isolate* isolate, Handle<SharedFunctionInfo> shared_info) {
|
||||
DCHECK(shared_info->CanDiscardCompiled());
|
||||
|
||||
Handle<String> inferred_name_val =
|
||||
handle(shared_info->inferred_name(), isolate);
|
||||
int start_position = shared_info->StartPosition();
|
||||
int end_position = shared_info->EndPosition();
|
||||
|
||||
shared_info->DiscardCompiledMetadata(isolate);
|
||||
|
||||
// Replace compiled data with a new UncompiledData object.
|
||||
if (shared_info->HasUncompiledDataWithPreparseData()) {
|
||||
// If this is uncompiled data with a pre-parsed scope data, we can just
|
||||
// clear out the scope data and keep the uncompiled data.
|
||||
shared_info->ClearPreparseData();
|
||||
} else {
|
||||
// Create a new UncompiledData, without pre-parsed scope, and update the
|
||||
// function data to point to it. Use the raw function data setter to avoid
|
||||
// validity checks, since we're performing the unusual task of decompiling.
|
||||
Handle<UncompiledData> data =
|
||||
isolate->factory()->NewUncompiledDataWithoutPreparseData(
|
||||
inferred_name_val, start_position, end_position);
|
||||
shared_info->set_function_data(*data);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> SharedFunctionInfo::GetSourceCode(
|
||||
Handle<SharedFunctionInfo> shared) {
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
if (!shared->HasSourceCode()) return isolate->factory()->undefined_value();
|
||||
Handle<String> source(String::cast(Script::cast(shared->script()).source()),
|
||||
isolate);
|
||||
return isolate->factory()->NewSubString(source, shared->StartPosition(),
|
||||
shared->EndPosition());
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> SharedFunctionInfo::GetSourceCodeHarmony(
|
||||
Handle<SharedFunctionInfo> shared) {
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
if (!shared->HasSourceCode()) return isolate->factory()->undefined_value();
|
||||
Handle<String> script_source(
|
||||
String::cast(Script::cast(shared->script()).source()), isolate);
|
||||
int start_pos = shared->function_token_position();
|
||||
DCHECK_NE(start_pos, kNoSourcePosition);
|
||||
Handle<String> source = isolate->factory()->NewSubString(
|
||||
script_source, start_pos, shared->EndPosition());
|
||||
if (!shared->is_wrapped()) return source;
|
||||
|
||||
DCHECK(!shared->name_should_print_as_anonymous());
|
||||
IncrementalStringBuilder builder(isolate);
|
||||
builder.AppendCString("function ");
|
||||
builder.AppendString(Handle<String>(shared->Name(), isolate));
|
||||
builder.AppendCString("(");
|
||||
Handle<FixedArray> args(Script::cast(shared->script()).wrapped_arguments(),
|
||||
isolate);
|
||||
int argc = args->length();
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0) builder.AppendCString(", ");
|
||||
builder.AppendString(Handle<String>(String::cast(args->get(i)), isolate));
|
||||
}
|
||||
builder.AppendCString(") {\n");
|
||||
builder.AppendString(source);
|
||||
builder.AppendCString("\n}");
|
||||
return builder.Finish().ToHandleChecked();
|
||||
}
|
||||
|
||||
SharedFunctionInfo::Inlineability SharedFunctionInfo::GetInlineability() const {
|
||||
if (!script().IsScript()) return kHasNoScript;
|
||||
|
||||
if (GetIsolate()->is_precise_binary_code_coverage() &&
|
||||
!has_reported_binary_coverage()) {
|
||||
// We may miss invocations if this function is inlined.
|
||||
return kNeedsBinaryCoverage;
|
||||
}
|
||||
|
||||
if (optimization_disabled()) return kHasOptimizationDisabled;
|
||||
|
||||
// Built-in functions are handled by the JSCallReducer.
|
||||
if (HasBuiltinId()) return kIsBuiltin;
|
||||
|
||||
if (!IsUserJavaScript()) return kIsNotUserCode;
|
||||
|
||||
// If there is no bytecode array, it is either not compiled or it is compiled
|
||||
// with WebAssembly for the asm.js pipeline. In either case we don't want to
|
||||
// inline.
|
||||
if (!HasBytecodeArray()) return kHasNoBytecode;
|
||||
|
||||
if (GetBytecodeArray().length() > FLAG_max_inlined_bytecode_size) {
|
||||
return kExceedsBytecodeLimit;
|
||||
}
|
||||
|
||||
if (HasBreakInfo()) return kMayContainBreakPoints;
|
||||
|
||||
return kIsInlineable;
|
||||
}
|
||||
|
||||
int SharedFunctionInfo::SourceSize() { return EndPosition() - StartPosition(); }
|
||||
|
||||
// Output the source code without any allocation in the heap.
|
||||
std::ostream& operator<<(std::ostream& os, const SourceCodeOf& v) {
|
||||
const SharedFunctionInfo s = v.value;
|
||||
// For some native functions there is no source.
|
||||
if (!s.HasSourceCode()) return os << "<No Source>";
|
||||
|
||||
// Get the source for the script which this function came from.
|
||||
// Don't use String::cast because we don't want more assertion errors while
|
||||
// we are already creating a stack dump.
|
||||
String script_source =
|
||||
String::unchecked_cast(Script::cast(s.script()).source());
|
||||
|
||||
if (!script_source.LooksValid()) return os << "<Invalid Source>";
|
||||
|
||||
if (!s.is_toplevel()) {
|
||||
os << "function ";
|
||||
String name = s.Name();
|
||||
if (name.length() > 0) {
|
||||
name.PrintUC16(os);
|
||||
}
|
||||
}
|
||||
|
||||
int len = s.EndPosition() - s.StartPosition();
|
||||
if (len <= v.max_length || v.max_length < 0) {
|
||||
script_source.PrintUC16(os, s.StartPosition(), s.EndPosition());
|
||||
return os;
|
||||
} else {
|
||||
script_source.PrintUC16(os, s.StartPosition(),
|
||||
s.StartPosition() + v.max_length);
|
||||
return os << "...\n";
|
||||
}
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::DisableOptimization(BailoutReason reason) {
|
||||
DCHECK_NE(reason, BailoutReason::kNoReason);
|
||||
|
||||
set_flags(DisabledOptimizationReasonBits::update(flags(), reason));
|
||||
// Code should be the lazy compilation stub or else interpreted.
|
||||
DCHECK(abstract_code().kind() == AbstractCode::INTERPRETED_FUNCTION ||
|
||||
abstract_code().kind() == AbstractCode::BUILTIN);
|
||||
PROFILE(GetIsolate(),
|
||||
CodeDisableOptEvent(handle(abstract_code(), GetIsolate()),
|
||||
handle(*this, GetIsolate())));
|
||||
if (FLAG_trace_opt) {
|
||||
CodeTracer::Scope scope(GetIsolate()->GetCodeTracer());
|
||||
PrintF(scope.file(), "[disabled optimization for ");
|
||||
ShortPrint(scope.file());
|
||||
PrintF(scope.file(), ", reason: %s]\n", GetBailoutReason(reason));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
template <typename LocalIsolate>
|
||||
void SharedFunctionInfo::InitFromFunctionLiteral(
|
||||
LocalIsolate* isolate, Handle<SharedFunctionInfo> shared_info,
|
||||
FunctionLiteral* lit, bool is_toplevel) {
|
||||
DCHECK(!shared_info->name_or_scope_info().IsScopeInfo());
|
||||
|
||||
// When adding fields here, make sure DeclarationScope::AnalyzePartially is
|
||||
// updated accordingly.
|
||||
shared_info->set_internal_formal_parameter_count(lit->parameter_count());
|
||||
shared_info->SetFunctionTokenPosition(lit->function_token_position(),
|
||||
lit->start_position());
|
||||
shared_info->set_syntax_kind(lit->syntax_kind());
|
||||
shared_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
|
||||
shared_info->set_language_mode(lit->language_mode());
|
||||
shared_info->set_function_literal_id(lit->function_literal_id());
|
||||
// FunctionKind must have already been set.
|
||||
DCHECK(lit->kind() == shared_info->kind());
|
||||
shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject());
|
||||
DCHECK_IMPLIES(lit->requires_instance_members_initializer(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_requires_instance_members_initializer(
|
||||
lit->requires_instance_members_initializer());
|
||||
DCHECK_IMPLIES(lit->class_scope_has_private_brand(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_class_scope_has_private_brand(
|
||||
lit->class_scope_has_private_brand());
|
||||
DCHECK_IMPLIES(lit->has_static_private_methods_or_accessors(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_has_static_private_methods_or_accessors(
|
||||
lit->has_static_private_methods_or_accessors());
|
||||
|
||||
shared_info->set_is_toplevel(is_toplevel);
|
||||
DCHECK(shared_info->outer_scope_info().IsTheHole());
|
||||
if (!is_toplevel) {
|
||||
Scope* outer_scope = lit->scope()->GetOuterScopeWithContext();
|
||||
if (outer_scope) {
|
||||
shared_info->set_outer_scope_info(*outer_scope->scope_info());
|
||||
shared_info->set_private_name_lookup_skips_outer_class(
|
||||
lit->scope()->private_name_lookup_skips_outer_class());
|
||||
}
|
||||
}
|
||||
|
||||
shared_info->set_length(lit->function_length());
|
||||
|
||||
// For lazy parsed functions, the following flags will be inaccurate since we
|
||||
// don't have the information yet. They're set later in
|
||||
// SetSharedFunctionFlagsFromLiteral (compiler.cc), when the function is
|
||||
// really parsed and compiled.
|
||||
if (lit->ShouldEagerCompile()) {
|
||||
shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters());
|
||||
shared_info->UpdateAndFinalizeExpectedNofPropertiesFromEstimate(lit);
|
||||
shared_info->set_is_safe_to_skip_arguments_adaptor(
|
||||
lit->SafeToSkipArgumentsAdaptor());
|
||||
DCHECK_NULL(lit->produced_preparse_data());
|
||||
|
||||
// If we're about to eager compile, we'll have the function literal
|
||||
// available, so there's no need to wastefully allocate an uncompiled data.
|
||||
return;
|
||||
}
|
||||
|
||||
shared_info->set_is_safe_to_skip_arguments_adaptor(false);
|
||||
shared_info->UpdateExpectedNofPropertiesFromEstimate(lit);
|
||||
|
||||
Handle<UncompiledData> data;
|
||||
|
||||
ProducedPreparseData* scope_data = lit->produced_preparse_data();
|
||||
if (scope_data != nullptr) {
|
||||
Handle<PreparseData> preparse_data = scope_data->Serialize(isolate);
|
||||
|
||||
data = isolate->factory()->NewUncompiledDataWithPreparseData(
|
||||
lit->GetInferredName(isolate), lit->start_position(),
|
||||
lit->end_position(), preparse_data);
|
||||
} else {
|
||||
data = isolate->factory()->NewUncompiledDataWithoutPreparseData(
|
||||
lit->GetInferredName(isolate), lit->start_position(),
|
||||
lit->end_position());
|
||||
}
|
||||
|
||||
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, Handle<SharedFunctionInfo> shared_info,
|
||||
FunctionLiteral* lit, bool is_toplevel);
|
||||
|
||||
uint16_t SharedFunctionInfo::get_property_estimate_from_literal(
|
||||
FunctionLiteral* literal) {
|
||||
int estimate = literal->expected_property_count();
|
||||
|
||||
// If this is a class constructor, we may have already parsed fields.
|
||||
if (is_class_constructor()) {
|
||||
estimate += expected_nof_properties();
|
||||
}
|
||||
return estimate;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::UpdateExpectedNofPropertiesFromEstimate(
|
||||
FunctionLiteral* literal) {
|
||||
// Limit actual estimate to fit in a 8 bit field, we will never allocate
|
||||
// more than this in any case.
|
||||
STATIC_ASSERT(JSObject::kMaxInObjectProperties <= kMaxUInt8);
|
||||
int estimate = get_property_estimate_from_literal(literal);
|
||||
set_expected_nof_properties(std::min(estimate, kMaxUInt8));
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::UpdateAndFinalizeExpectedNofPropertiesFromEstimate(
|
||||
FunctionLiteral* literal) {
|
||||
DCHECK(literal->ShouldEagerCompile());
|
||||
if (are_properties_final()) {
|
||||
return;
|
||||
}
|
||||
int estimate = get_property_estimate_from_literal(literal);
|
||||
|
||||
// If no properties are added in the constructor, they are more likely
|
||||
// to be added later.
|
||||
if (estimate == 0) estimate = 2;
|
||||
|
||||
// Limit actual estimate to fit in a 8 bit field, we will never allocate
|
||||
// more than this in any case.
|
||||
STATIC_ASSERT(JSObject::kMaxInObjectProperties <= kMaxUInt8);
|
||||
estimate = std::min(estimate, kMaxUInt8);
|
||||
|
||||
set_expected_nof_properties(estimate);
|
||||
set_are_properties_final(true);
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::SetFunctionTokenPosition(int function_token_position,
|
||||
int start_position) {
|
||||
int offset;
|
||||
if (function_token_position == kNoSourcePosition) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = start_position - function_token_position;
|
||||
}
|
||||
|
||||
if (offset > kMaximumFunctionTokenOffset) {
|
||||
offset = kFunctionTokenOutOfRange;
|
||||
}
|
||||
set_raw_function_token_offset(offset);
|
||||
}
|
||||
|
||||
int SharedFunctionInfo::StartPosition() const {
|
||||
Object maybe_scope_info = name_or_scope_info();
|
||||
if (maybe_scope_info.IsScopeInfo()) {
|
||||
ScopeInfo info = ScopeInfo::cast(maybe_scope_info);
|
||||
if (info.HasPositionInfo()) {
|
||||
return info.StartPosition();
|
||||
}
|
||||
}
|
||||
if (HasUncompiledData()) {
|
||||
// Works with or without scope.
|
||||
return uncompiled_data().start_position();
|
||||
}
|
||||
if (IsApiFunction() || HasBuiltinId()) {
|
||||
DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy);
|
||||
return 0;
|
||||
}
|
||||
if (HasWasmExportedFunctionData()) {
|
||||
WasmInstanceObject instance = wasm_exported_function_data().instance();
|
||||
int func_index = wasm_exported_function_data().function_index();
|
||||
auto& function = instance.module()->functions[func_index];
|
||||
return static_cast<int>(function.code.offset());
|
||||
}
|
||||
return kNoSourcePosition;
|
||||
}
|
||||
|
||||
int SharedFunctionInfo::EndPosition() const {
|
||||
Object maybe_scope_info = name_or_scope_info();
|
||||
if (maybe_scope_info.IsScopeInfo()) {
|
||||
ScopeInfo info = ScopeInfo::cast(maybe_scope_info);
|
||||
if (info.HasPositionInfo()) {
|
||||
return info.EndPosition();
|
||||
}
|
||||
}
|
||||
if (HasUncompiledData()) {
|
||||
// Works with or without scope.
|
||||
return uncompiled_data().end_position();
|
||||
}
|
||||
if (IsApiFunction() || HasBuiltinId()) {
|
||||
DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy);
|
||||
return 0;
|
||||
}
|
||||
if (HasWasmExportedFunctionData()) {
|
||||
WasmInstanceObject instance = wasm_exported_function_data().instance();
|
||||
int func_index = wasm_exported_function_data().function_index();
|
||||
auto& function = instance.module()->functions[func_index];
|
||||
return static_cast<int>(function.code.end_offset());
|
||||
}
|
||||
return kNoSourcePosition;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::SetPosition(int start_position, int end_position) {
|
||||
Object maybe_scope_info = name_or_scope_info();
|
||||
if (maybe_scope_info.IsScopeInfo()) {
|
||||
ScopeInfo info = ScopeInfo::cast(maybe_scope_info);
|
||||
if (info.HasPositionInfo()) {
|
||||
info.SetPositionInfo(start_position, end_position);
|
||||
}
|
||||
} else if (HasUncompiledData()) {
|
||||
if (HasUncompiledDataWithPreparseData()) {
|
||||
// Clear out preparsed scope data, since the position setter invalidates
|
||||
// any scope data.
|
||||
ClearPreparseData();
|
||||
}
|
||||
uncompiled_data().set_start_position(start_position);
|
||||
uncompiled_data().set_end_position(end_position);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::AreSourcePositionsAvailable() const {
|
||||
if (FLAG_enable_lazy_source_positions) {
|
||||
return !HasBytecodeArray() || GetBytecodeArray().HasSourcePositionTable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedFunctionInfo::EnsureSourcePositionsAvailable(
|
||||
Isolate* isolate, Handle<SharedFunctionInfo> shared_info) {
|
||||
if (FLAG_enable_lazy_source_positions && shared_info->HasBytecodeArray() &&
|
||||
!shared_info->GetBytecodeArray().HasSourcePositionTable()) {
|
||||
Compiler::CollectSourcePositions(isolate, shared_info);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void JSArray::Initialize(Handle<JSArray> array, int capacity, int length) {
|
||||
DCHECK_GE(capacity, 0);
|
||||
@ -6218,326 +5568,6 @@ Handle<Object> JSPromise::TriggerPromiseReactions(Isolate* isolate,
|
||||
return isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
// static
|
||||
JSRegExp::Flags JSRegExp::FlagsFromString(Isolate* isolate,
|
||||
Handle<String> flags, bool* success) {
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('g') == JSRegExp::kGlobal);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('i') == JSRegExp::kIgnoreCase);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('m') == JSRegExp::kMultiline);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('s') == JSRegExp::kDotAll);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('u') == JSRegExp::kUnicode);
|
||||
STATIC_ASSERT(*JSRegExp::FlagFromChar('y') == JSRegExp::kSticky);
|
||||
|
||||
int length = flags->length();
|
||||
if (length == 0) {
|
||||
*success = true;
|
||||
return JSRegExp::kNone;
|
||||
}
|
||||
// A longer flags string cannot be valid.
|
||||
if (length > JSRegExp::kFlagCount) return JSRegExp::Flags(0);
|
||||
JSRegExp::Flags value(0);
|
||||
if (flags->IsSeqOneByteString()) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
SeqOneByteString seq_flags = SeqOneByteString::cast(*flags);
|
||||
for (int i = 0; i < length; i++) {
|
||||
base::Optional<JSRegExp::Flag> maybe_flag =
|
||||
JSRegExp::FlagFromChar(seq_flags.Get(i));
|
||||
if (!maybe_flag.has_value()) return JSRegExp::Flags(0);
|
||||
JSRegExp::Flag flag = *maybe_flag;
|
||||
// Duplicate flag.
|
||||
if (value & flag) return JSRegExp::Flags(0);
|
||||
value |= flag;
|
||||
}
|
||||
} else {
|
||||
flags = String::Flatten(isolate, flags);
|
||||
DisallowHeapAllocation no_gc;
|
||||
String::FlatContent flags_content = flags->GetFlatContent(no_gc);
|
||||
for (int i = 0; i < length; i++) {
|
||||
base::Optional<JSRegExp::Flag> maybe_flag =
|
||||
JSRegExp::FlagFromChar(flags_content.Get(i));
|
||||
if (!maybe_flag.has_value()) return JSRegExp::Flags(0);
|
||||
JSRegExp::Flag flag = *maybe_flag;
|
||||
// Duplicate flag.
|
||||
if (value & flag) return JSRegExp::Flags(0);
|
||||
value |= flag;
|
||||
}
|
||||
}
|
||||
*success = true;
|
||||
return value;
|
||||
}
|
||||
|
||||
// static
|
||||
MaybeHandle<JSRegExp> JSRegExp::New(Isolate* isolate, Handle<String> pattern,
|
||||
Flags flags, uint32_t backtrack_limit) {
|
||||
Handle<JSFunction> constructor = isolate->regexp_function();
|
||||
Handle<JSRegExp> regexp =
|
||||
Handle<JSRegExp>::cast(isolate->factory()->NewJSObject(constructor));
|
||||
|
||||
return JSRegExp::Initialize(regexp, pattern, flags, backtrack_limit);
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<JSRegExp> JSRegExp::Copy(Handle<JSRegExp> regexp) {
|
||||
Isolate* const isolate = regexp->GetIsolate();
|
||||
return Handle<JSRegExp>::cast(isolate->factory()->CopyJSObject(regexp));
|
||||
}
|
||||
|
||||
Object JSRegExp::Code(bool is_latin1) const {
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
return DataAt(code_index(is_latin1));
|
||||
}
|
||||
|
||||
Object JSRegExp::Bytecode(bool is_latin1) const {
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
return DataAt(bytecode_index(is_latin1));
|
||||
}
|
||||
|
||||
bool JSRegExp::ShouldProduceBytecode() {
|
||||
return FLAG_regexp_interpret_all ||
|
||||
(FLAG_regexp_tier_up && !MarkedForTierUp());
|
||||
}
|
||||
|
||||
// An irregexp is considered to be marked for tier up if the tier-up ticks value
|
||||
// reaches zero. An atom is not subject to tier-up implementation, so the
|
||||
// tier-up ticks value is not set.
|
||||
bool JSRegExp::MarkedForTierUp() {
|
||||
DCHECK(data().IsFixedArray());
|
||||
if (TypeTag() == JSRegExp::ATOM || !FLAG_regexp_tier_up) {
|
||||
return false;
|
||||
}
|
||||
return Smi::ToInt(DataAt(kIrregexpTicksUntilTierUpIndex)) == 0;
|
||||
}
|
||||
|
||||
void JSRegExp::ResetLastTierUpTick() {
|
||||
DCHECK(FLAG_regexp_tier_up);
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
int tier_up_ticks = Smi::ToInt(DataAt(kIrregexpTicksUntilTierUpIndex)) + 1;
|
||||
FixedArray::cast(data()).set(JSRegExp::kIrregexpTicksUntilTierUpIndex,
|
||||
Smi::FromInt(tier_up_ticks));
|
||||
}
|
||||
|
||||
void JSRegExp::TierUpTick() {
|
||||
DCHECK(FLAG_regexp_tier_up);
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
int tier_up_ticks = Smi::ToInt(DataAt(kIrregexpTicksUntilTierUpIndex));
|
||||
if (tier_up_ticks == 0) {
|
||||
return;
|
||||
}
|
||||
FixedArray::cast(data()).set(JSRegExp::kIrregexpTicksUntilTierUpIndex,
|
||||
Smi::FromInt(tier_up_ticks - 1));
|
||||
}
|
||||
|
||||
void JSRegExp::MarkTierUpForNextExec() {
|
||||
DCHECK(FLAG_regexp_tier_up);
|
||||
DCHECK_EQ(TypeTag(), JSRegExp::IRREGEXP);
|
||||
FixedArray::cast(data()).set(JSRegExp::kIrregexpTicksUntilTierUpIndex,
|
||||
Smi::zero());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsLineTerminator(int c) {
|
||||
// Expected to return true for '\n', '\r', 0x2028, and 0x2029.
|
||||
return unibrow::IsLineTerminator(static_cast<unibrow::uchar>(c));
|
||||
}
|
||||
|
||||
// TODO(jgruber): Consider merging CountAdditionalEscapeChars and
|
||||
// WriteEscapedRegExpSource into a single function to deduplicate dispatch logic
|
||||
// and move related code closer to each other.
|
||||
template <typename Char>
|
||||
int CountAdditionalEscapeChars(Handle<String> source, bool* needs_escapes_out) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
int escapes = 0;
|
||||
bool needs_escapes = false;
|
||||
bool in_char_class = false;
|
||||
Vector<const Char> src = source->GetCharVector<Char>(no_gc);
|
||||
for (int i = 0; i < src.length(); i++) {
|
||||
const Char c = src[i];
|
||||
if (c == '\\') {
|
||||
if (i + 1 < src.length() && IsLineTerminator(src[i + 1])) {
|
||||
// This '\' is ignored since the next character itself will be escaped.
|
||||
escapes--;
|
||||
} else {
|
||||
// Escape. Skip next character, which will be copied verbatim;
|
||||
i++;
|
||||
}
|
||||
} else if (c == '/' && !in_char_class) {
|
||||
// Not escaped forward-slash needs escape.
|
||||
needs_escapes = true;
|
||||
escapes++;
|
||||
} else if (c == '[') {
|
||||
in_char_class = true;
|
||||
} else if (c == ']') {
|
||||
in_char_class = false;
|
||||
} else if (c == '\n') {
|
||||
needs_escapes = true;
|
||||
escapes++;
|
||||
} else if (c == '\r') {
|
||||
needs_escapes = true;
|
||||
escapes++;
|
||||
} else if (static_cast<int>(c) == 0x2028) {
|
||||
needs_escapes = true;
|
||||
escapes += std::strlen("\\u2028") - 1;
|
||||
} else if (static_cast<int>(c) == 0x2029) {
|
||||
needs_escapes = true;
|
||||
escapes += std::strlen("\\u2029") - 1;
|
||||
} else {
|
||||
DCHECK(!IsLineTerminator(c));
|
||||
}
|
||||
}
|
||||
DCHECK(!in_char_class);
|
||||
DCHECK_GE(escapes, 0);
|
||||
DCHECK_IMPLIES(escapes != 0, needs_escapes);
|
||||
*needs_escapes_out = needs_escapes;
|
||||
return escapes;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void WriteStringToCharVector(Vector<Char> v, int* d, const char* string) {
|
||||
int s = 0;
|
||||
while (string[s] != '\0') v[(*d)++] = string[s++];
|
||||
}
|
||||
|
||||
template <typename Char, typename StringType>
|
||||
Handle<StringType> WriteEscapedRegExpSource(Handle<String> source,
|
||||
Handle<StringType> result) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
Vector<const Char> src = source->GetCharVector<Char>(no_gc);
|
||||
Vector<Char> dst(result->GetChars(no_gc), result->length());
|
||||
int s = 0;
|
||||
int d = 0;
|
||||
bool in_char_class = false;
|
||||
while (s < src.length()) {
|
||||
const Char c = src[s];
|
||||
if (c == '\\') {
|
||||
if (s + 1 < src.length() && IsLineTerminator(src[s + 1])) {
|
||||
// This '\' is ignored since the next character itself will be escaped.
|
||||
s++;
|
||||
continue;
|
||||
} else {
|
||||
// Escape. Copy this and next character.
|
||||
dst[d++] = src[s++];
|
||||
}
|
||||
if (s == src.length()) break;
|
||||
} else if (c == '/' && !in_char_class) {
|
||||
// Not escaped forward-slash needs escape.
|
||||
dst[d++] = '\\';
|
||||
} else if (c == '[') {
|
||||
in_char_class = true;
|
||||
} else if (c == ']') {
|
||||
in_char_class = false;
|
||||
} else if (c == '\n') {
|
||||
WriteStringToCharVector(dst, &d, "\\n");
|
||||
s++;
|
||||
continue;
|
||||
} else if (c == '\r') {
|
||||
WriteStringToCharVector(dst, &d, "\\r");
|
||||
s++;
|
||||
continue;
|
||||
} else if (static_cast<int>(c) == 0x2028) {
|
||||
WriteStringToCharVector(dst, &d, "\\u2028");
|
||||
s++;
|
||||
continue;
|
||||
} else if (static_cast<int>(c) == 0x2029) {
|
||||
WriteStringToCharVector(dst, &d, "\\u2029");
|
||||
s++;
|
||||
continue;
|
||||
} else {
|
||||
DCHECK(!IsLineTerminator(c));
|
||||
}
|
||||
dst[d++] = src[s++];
|
||||
}
|
||||
DCHECK_EQ(result->length(), d);
|
||||
DCHECK(!in_char_class);
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeHandle<String> EscapeRegExpSource(Isolate* isolate,
|
||||
Handle<String> source) {
|
||||
DCHECK(source->IsFlat());
|
||||
if (source->length() == 0) return isolate->factory()->query_colon_string();
|
||||
bool one_byte = String::IsOneByteRepresentationUnderneath(*source);
|
||||
bool needs_escapes = false;
|
||||
int additional_escape_chars =
|
||||
one_byte ? CountAdditionalEscapeChars<uint8_t>(source, &needs_escapes)
|
||||
: CountAdditionalEscapeChars<uc16>(source, &needs_escapes);
|
||||
if (!needs_escapes) return source;
|
||||
int length = source->length() + additional_escape_chars;
|
||||
if (one_byte) {
|
||||
Handle<SeqOneByteString> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
|
||||
isolate->factory()->NewRawOneByteString(length),
|
||||
String);
|
||||
return WriteEscapedRegExpSource<uint8_t>(source, result);
|
||||
} else {
|
||||
Handle<SeqTwoByteString> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, result,
|
||||
isolate->factory()->NewRawTwoByteString(length),
|
||||
String);
|
||||
return WriteEscapedRegExpSource<uc16>(source, result);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp,
|
||||
Handle<String> source,
|
||||
Handle<String> flags_string) {
|
||||
Isolate* isolate = regexp->GetIsolate();
|
||||
bool success = false;
|
||||
Flags flags = JSRegExp::FlagsFromString(isolate, flags_string, &success);
|
||||
if (!success) {
|
||||
THROW_NEW_ERROR(
|
||||
isolate,
|
||||
NewSyntaxError(MessageTemplate::kInvalidRegExpFlags, flags_string),
|
||||
JSRegExp);
|
||||
}
|
||||
return Initialize(regexp, source, flags);
|
||||
}
|
||||
|
||||
// static
|
||||
MaybeHandle<JSRegExp> JSRegExp::Initialize(Handle<JSRegExp> regexp,
|
||||
Handle<String> source, Flags flags,
|
||||
uint32_t backtrack_limit) {
|
||||
Isolate* isolate = regexp->GetIsolate();
|
||||
Factory* factory = isolate->factory();
|
||||
// If source is the empty string we set it to "(?:)" instead as
|
||||
// suggested by ECMA-262, 5th, section 15.10.4.1.
|
||||
if (source->length() == 0) source = factory->query_colon_string();
|
||||
|
||||
source = String::Flatten(isolate, source);
|
||||
|
||||
RETURN_ON_EXCEPTION(
|
||||
isolate, RegExp::Compile(isolate, regexp, source, flags, backtrack_limit),
|
||||
JSRegExp);
|
||||
|
||||
Handle<String> escaped_source;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, escaped_source,
|
||||
EscapeRegExpSource(isolate, source), JSRegExp);
|
||||
|
||||
regexp->set_source(*escaped_source);
|
||||
regexp->set_flags(Smi::FromInt(flags));
|
||||
|
||||
Map map = regexp->map();
|
||||
Object constructor = map.GetConstructor();
|
||||
if (constructor.IsJSFunction() &&
|
||||
JSFunction::cast(constructor).initial_map() == map) {
|
||||
// If we still have the original map, set in-object properties directly.
|
||||
regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, Smi::zero(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
} else {
|
||||
// Map has changed, so use generic, but slower, method.
|
||||
RETURN_ON_EXCEPTION(
|
||||
isolate,
|
||||
Object::SetProperty(isolate, regexp, factory->lastIndex_string(),
|
||||
Handle<Smi>(Smi::zero(), isolate)),
|
||||
JSRegExp);
|
||||
}
|
||||
|
||||
return regexp;
|
||||
}
|
||||
|
||||
// RegExpKey carries the source and flags of a regular expression as key.
|
||||
class RegExpKey : public HashTableKey {
|
||||
public:
|
||||
|
667
src/objects/shared-function-info.cc
Normal file
667
src/objects/shared-function-info.cc
Normal file
@ -0,0 +1,667 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/objects/shared-function-info.h"
|
||||
|
||||
#include "src/ast/ast.h"
|
||||
#include "src/ast/scopes.h"
|
||||
#include "src/codegen/compiler.h"
|
||||
#include "src/objects/shared-function-info-inl.h"
|
||||
#include "src/strings/string-builder-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
V8_EXPORT_PRIVATE constexpr Smi SharedFunctionInfo::kNoSharedNameSentinel;
|
||||
|
||||
uint32_t SharedFunctionInfo::Hash() {
|
||||
// Hash SharedFunctionInfo based on its start position and script id. Note: we
|
||||
// don't use the function's literal id since getting that is slow for compiled
|
||||
// funcitons.
|
||||
int start_pos = StartPosition();
|
||||
int script_id = script().IsScript() ? Script::cast(script()).id() : 0;
|
||||
return static_cast<uint32_t>(base::hash_combine(start_pos, script_id));
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::Init(ReadOnlyRoots ro_roots, int unique_id) {
|
||||
DisallowHeapAllocation no_allocation;
|
||||
|
||||
// Set the function data to the "illegal" builtin. Ideally we'd use some sort
|
||||
// of "uninitialized" marker here, but it's cheaper to use a valid buitin and
|
||||
// avoid having to do uninitialized checks elsewhere.
|
||||
set_builtin_id(Builtins::kIllegal);
|
||||
|
||||
// Set the name to the no-name sentinel, this can be updated later.
|
||||
set_name_or_scope_info(SharedFunctionInfo::kNoSharedNameSentinel,
|
||||
SKIP_WRITE_BARRIER);
|
||||
|
||||
// Generally functions won't have feedback, unless they have been created
|
||||
// from a FunctionLiteral. Those can just reset this field to keep the
|
||||
// SharedFunctionInfo in a consistent state.
|
||||
set_raw_outer_scope_info_or_feedback_metadata(ro_roots.the_hole_value(),
|
||||
SKIP_WRITE_BARRIER);
|
||||
set_script_or_debug_info(ro_roots.undefined_value(), SKIP_WRITE_BARRIER);
|
||||
set_function_literal_id(kFunctionLiteralIdInvalid);
|
||||
#if V8_SFI_HAS_UNIQUE_ID
|
||||
set_unique_id(unique_id);
|
||||
#endif
|
||||
|
||||
// Set integer fields (smi or int, depending on the architecture).
|
||||
set_length(0);
|
||||
set_internal_formal_parameter_count(0);
|
||||
set_expected_nof_properties(0);
|
||||
set_raw_function_token_offset(0);
|
||||
|
||||
// All flags default to false or 0, except ConstructAsBuiltinBit just because
|
||||
// we're using the kIllegal builtin.
|
||||
set_flags(ConstructAsBuiltinBit::encode(true));
|
||||
set_flags2(0);
|
||||
|
||||
UpdateFunctionMapIndex();
|
||||
|
||||
clear_padding();
|
||||
}
|
||||
|
||||
Code SharedFunctionInfo::GetCode() const {
|
||||
// ======
|
||||
// NOTE: This chain of checks MUST be kept in sync with the equivalent CSA
|
||||
// GetSharedFunctionInfoCode method in code-stub-assembler.cc.
|
||||
// ======
|
||||
|
||||
Isolate* isolate = GetIsolate();
|
||||
Object data = function_data();
|
||||
if (data.IsSmi()) {
|
||||
// Holding a Smi means we are a builtin.
|
||||
DCHECK(HasBuiltinId());
|
||||
return isolate->builtins()->builtin(builtin_id());
|
||||
} else if (data.IsBytecodeArray()) {
|
||||
// Having a bytecode array means we are a compiled, interpreted function.
|
||||
DCHECK(HasBytecodeArray());
|
||||
return isolate->builtins()->builtin(Builtins::kInterpreterEntryTrampoline);
|
||||
} else if (data.IsAsmWasmData()) {
|
||||
// Having AsmWasmData means we are an asm.js/wasm function.
|
||||
DCHECK(HasAsmWasmData());
|
||||
return isolate->builtins()->builtin(Builtins::kInstantiateAsmJs);
|
||||
} else if (data.IsUncompiledData()) {
|
||||
// Having uncompiled data (with or without scope) means we need to compile.
|
||||
DCHECK(HasUncompiledData());
|
||||
return isolate->builtins()->builtin(Builtins::kCompileLazy);
|
||||
} else if (data.IsFunctionTemplateInfo()) {
|
||||
// Having a function template info means we are an API function.
|
||||
DCHECK(IsApiFunction());
|
||||
return isolate->builtins()->builtin(Builtins::kHandleApiCall);
|
||||
} else if (data.IsWasmExportedFunctionData()) {
|
||||
// Having a WasmExportedFunctionData means the code is in there.
|
||||
DCHECK(HasWasmExportedFunctionData());
|
||||
return wasm_exported_function_data().wrapper_code();
|
||||
} else if (data.IsInterpreterData()) {
|
||||
Code code = InterpreterTrampoline();
|
||||
DCHECK(code.IsCode());
|
||||
DCHECK(code.is_interpreter_trampoline_builtin());
|
||||
return code;
|
||||
} else if (data.IsWasmJSFunctionData()) {
|
||||
return wasm_js_function_data().wrapper_code();
|
||||
} else if (data.IsWasmCapiFunctionData()) {
|
||||
return wasm_capi_function_data().wrapper_code();
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
WasmExportedFunctionData SharedFunctionInfo::wasm_exported_function_data()
|
||||
const {
|
||||
DCHECK(HasWasmExportedFunctionData());
|
||||
return WasmExportedFunctionData::cast(function_data());
|
||||
}
|
||||
|
||||
WasmJSFunctionData SharedFunctionInfo::wasm_js_function_data() const {
|
||||
DCHECK(HasWasmJSFunctionData());
|
||||
return WasmJSFunctionData::cast(function_data());
|
||||
}
|
||||
|
||||
WasmCapiFunctionData SharedFunctionInfo::wasm_capi_function_data() const {
|
||||
DCHECK(HasWasmCapiFunctionData());
|
||||
return WasmCapiFunctionData::cast(function_data());
|
||||
}
|
||||
|
||||
SharedFunctionInfo::ScriptIterator::ScriptIterator(Isolate* isolate,
|
||||
Script script)
|
||||
: ScriptIterator(handle(script.shared_function_infos(), isolate)) {}
|
||||
|
||||
SharedFunctionInfo::ScriptIterator::ScriptIterator(
|
||||
Handle<WeakFixedArray> shared_function_infos)
|
||||
: shared_function_infos_(shared_function_infos), index_(0) {}
|
||||
|
||||
SharedFunctionInfo SharedFunctionInfo::ScriptIterator::Next() {
|
||||
while (index_ < shared_function_infos_->length()) {
|
||||
MaybeObject raw = shared_function_infos_->Get(index_++);
|
||||
HeapObject heap_object;
|
||||
if (!raw->GetHeapObject(&heap_object) || heap_object.IsUndefined()) {
|
||||
continue;
|
||||
}
|
||||
return SharedFunctionInfo::cast(heap_object);
|
||||
}
|
||||
return SharedFunctionInfo();
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::ScriptIterator::Reset(Isolate* isolate,
|
||||
Script script) {
|
||||
shared_function_infos_ = handle(script.shared_function_infos(), isolate);
|
||||
index_ = 0;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::SetScript(ReadOnlyRoots roots,
|
||||
HeapObject script_object,
|
||||
int function_literal_id,
|
||||
bool reset_preparsed_scope_data) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
|
||||
if (script() == script_object) return;
|
||||
|
||||
if (reset_preparsed_scope_data && HasUncompiledDataWithPreparseData()) {
|
||||
ClearPreparseData();
|
||||
}
|
||||
|
||||
// Add shared function info to new script's list. If a collection occurs,
|
||||
// the shared function info may be temporarily in two lists.
|
||||
// This is okay because the gc-time processing of these lists can tolerate
|
||||
// duplicates.
|
||||
if (script_object.IsScript()) {
|
||||
DCHECK(!script().IsScript());
|
||||
Script script = Script::cast(script_object);
|
||||
WeakFixedArray list = script.shared_function_infos();
|
||||
#ifdef DEBUG
|
||||
DCHECK_LT(function_literal_id, list.length());
|
||||
MaybeObject maybe_object = list.Get(function_literal_id);
|
||||
HeapObject heap_object;
|
||||
if (maybe_object->GetHeapObjectIfWeak(&heap_object)) {
|
||||
DCHECK_EQ(heap_object, *this);
|
||||
}
|
||||
#endif
|
||||
list.Set(function_literal_id, HeapObjectReference::Weak(*this));
|
||||
} else {
|
||||
DCHECK(script().IsScript());
|
||||
|
||||
// Remove shared function info from old script's list.
|
||||
Script old_script = Script::cast(script());
|
||||
|
||||
// Due to liveedit, it might happen that the old_script doesn't know
|
||||
// about the SharedFunctionInfo, so we have to guard against that.
|
||||
WeakFixedArray infos = old_script.shared_function_infos();
|
||||
if (function_literal_id < infos.length()) {
|
||||
MaybeObject raw =
|
||||
old_script.shared_function_infos().Get(function_literal_id);
|
||||
HeapObject heap_object;
|
||||
if (raw->GetHeapObjectIfWeak(&heap_object) && heap_object == *this) {
|
||||
old_script.shared_function_infos().Set(
|
||||
function_literal_id,
|
||||
HeapObjectReference::Strong(roots.undefined_value()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally set new script.
|
||||
set_script(script_object);
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::HasBreakInfo() const {
|
||||
if (!HasDebugInfo()) return false;
|
||||
DebugInfo info = GetDebugInfo();
|
||||
bool has_break_info = info.HasBreakInfo();
|
||||
return has_break_info;
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::BreakAtEntry() const {
|
||||
if (!HasDebugInfo()) return false;
|
||||
DebugInfo info = GetDebugInfo();
|
||||
bool break_at_entry = info.BreakAtEntry();
|
||||
return break_at_entry;
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::HasCoverageInfo() const {
|
||||
if (!HasDebugInfo()) return false;
|
||||
DebugInfo info = GetDebugInfo();
|
||||
bool has_coverage_info = info.HasCoverageInfo();
|
||||
return has_coverage_info;
|
||||
}
|
||||
|
||||
CoverageInfo SharedFunctionInfo::GetCoverageInfo() const {
|
||||
DCHECK(HasCoverageInfo());
|
||||
return CoverageInfo::cast(GetDebugInfo().coverage_info());
|
||||
}
|
||||
|
||||
String SharedFunctionInfo::DebugName() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
String function_name = Name();
|
||||
if (function_name.length() > 0) return function_name;
|
||||
return inferred_name();
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::PassesFilter(const char* raw_filter) {
|
||||
Vector<const char> filter = CStrVector(raw_filter);
|
||||
std::unique_ptr<char[]> cstrname(DebugName().ToCString());
|
||||
return v8::internal::PassesFilter(CStrVector(cstrname.get()), filter);
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::HasSourceCode() const {
|
||||
ReadOnlyRoots roots = GetReadOnlyRoots();
|
||||
return !script().IsUndefined(roots) &&
|
||||
!Script::cast(script()).source().IsUndefined(roots) &&
|
||||
String::cast(Script::cast(script()).source()).length() > 0;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::DiscardCompiledMetadata(
|
||||
Isolate* isolate,
|
||||
std::function<void(HeapObject object, ObjectSlot slot, HeapObject target)>
|
||||
gc_notify_updated_slot) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
if (is_compiled()) {
|
||||
HeapObject outer_scope_info;
|
||||
if (scope_info().HasOuterScopeInfo()) {
|
||||
outer_scope_info = scope_info().OuterScopeInfo();
|
||||
} else {
|
||||
outer_scope_info = ReadOnlyRoots(isolate).the_hole_value();
|
||||
}
|
||||
|
||||
// Raw setter to avoid validity checks, since we're performing the unusual
|
||||
// task of decompiling.
|
||||
set_raw_outer_scope_info_or_feedback_metadata(outer_scope_info);
|
||||
gc_notify_updated_slot(
|
||||
*this,
|
||||
RawField(SharedFunctionInfo::kOuterScopeInfoOrFeedbackMetadataOffset),
|
||||
outer_scope_info);
|
||||
} else {
|
||||
DCHECK(outer_scope_info().IsScopeInfo() || outer_scope_info().IsTheHole());
|
||||
}
|
||||
|
||||
// TODO(rmcilroy): Possibly discard ScopeInfo here as well.
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedFunctionInfo::DiscardCompiled(
|
||||
Isolate* isolate, Handle<SharedFunctionInfo> shared_info) {
|
||||
DCHECK(shared_info->CanDiscardCompiled());
|
||||
|
||||
Handle<String> inferred_name_val =
|
||||
handle(shared_info->inferred_name(), isolate);
|
||||
int start_position = shared_info->StartPosition();
|
||||
int end_position = shared_info->EndPosition();
|
||||
|
||||
shared_info->DiscardCompiledMetadata(isolate);
|
||||
|
||||
// Replace compiled data with a new UncompiledData object.
|
||||
if (shared_info->HasUncompiledDataWithPreparseData()) {
|
||||
// If this is uncompiled data with a pre-parsed scope data, we can just
|
||||
// clear out the scope data and keep the uncompiled data.
|
||||
shared_info->ClearPreparseData();
|
||||
} else {
|
||||
// Create a new UncompiledData, without pre-parsed scope, and update the
|
||||
// function data to point to it. Use the raw function data setter to avoid
|
||||
// validity checks, since we're performing the unusual task of decompiling.
|
||||
Handle<UncompiledData> data =
|
||||
isolate->factory()->NewUncompiledDataWithoutPreparseData(
|
||||
inferred_name_val, start_position, end_position);
|
||||
shared_info->set_function_data(*data);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> SharedFunctionInfo::GetSourceCode(
|
||||
Handle<SharedFunctionInfo> shared) {
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
if (!shared->HasSourceCode()) return isolate->factory()->undefined_value();
|
||||
Handle<String> source(String::cast(Script::cast(shared->script()).source()),
|
||||
isolate);
|
||||
return isolate->factory()->NewSubString(source, shared->StartPosition(),
|
||||
shared->EndPosition());
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<Object> SharedFunctionInfo::GetSourceCodeHarmony(
|
||||
Handle<SharedFunctionInfo> shared) {
|
||||
Isolate* isolate = shared->GetIsolate();
|
||||
if (!shared->HasSourceCode()) return isolate->factory()->undefined_value();
|
||||
Handle<String> script_source(
|
||||
String::cast(Script::cast(shared->script()).source()), isolate);
|
||||
int start_pos = shared->function_token_position();
|
||||
DCHECK_NE(start_pos, kNoSourcePosition);
|
||||
Handle<String> source = isolate->factory()->NewSubString(
|
||||
script_source, start_pos, shared->EndPosition());
|
||||
if (!shared->is_wrapped()) return source;
|
||||
|
||||
DCHECK(!shared->name_should_print_as_anonymous());
|
||||
IncrementalStringBuilder builder(isolate);
|
||||
builder.AppendCString("function ");
|
||||
builder.AppendString(Handle<String>(shared->Name(), isolate));
|
||||
builder.AppendCString("(");
|
||||
Handle<FixedArray> args(Script::cast(shared->script()).wrapped_arguments(),
|
||||
isolate);
|
||||
int argc = args->length();
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if (i > 0) builder.AppendCString(", ");
|
||||
builder.AppendString(Handle<String>(String::cast(args->get(i)), isolate));
|
||||
}
|
||||
builder.AppendCString(") {\n");
|
||||
builder.AppendString(source);
|
||||
builder.AppendCString("\n}");
|
||||
return builder.Finish().ToHandleChecked();
|
||||
}
|
||||
|
||||
SharedFunctionInfo::Inlineability SharedFunctionInfo::GetInlineability() const {
|
||||
if (!script().IsScript()) return kHasNoScript;
|
||||
|
||||
if (GetIsolate()->is_precise_binary_code_coverage() &&
|
||||
!has_reported_binary_coverage()) {
|
||||
// We may miss invocations if this function is inlined.
|
||||
return kNeedsBinaryCoverage;
|
||||
}
|
||||
|
||||
if (optimization_disabled()) return kHasOptimizationDisabled;
|
||||
|
||||
// Built-in functions are handled by the JSCallReducer.
|
||||
if (HasBuiltinId()) return kIsBuiltin;
|
||||
|
||||
if (!IsUserJavaScript()) return kIsNotUserCode;
|
||||
|
||||
// If there is no bytecode array, it is either not compiled or it is compiled
|
||||
// with WebAssembly for the asm.js pipeline. In either case we don't want to
|
||||
// inline.
|
||||
if (!HasBytecodeArray()) return kHasNoBytecode;
|
||||
|
||||
if (GetBytecodeArray().length() > FLAG_max_inlined_bytecode_size) {
|
||||
return kExceedsBytecodeLimit;
|
||||
}
|
||||
|
||||
if (HasBreakInfo()) return kMayContainBreakPoints;
|
||||
|
||||
return kIsInlineable;
|
||||
}
|
||||
|
||||
int SharedFunctionInfo::SourceSize() { return EndPosition() - StartPosition(); }
|
||||
|
||||
// Output the source code without any allocation in the heap.
|
||||
std::ostream& operator<<(std::ostream& os, const SourceCodeOf& v) {
|
||||
const SharedFunctionInfo s = v.value;
|
||||
// For some native functions there is no source.
|
||||
if (!s.HasSourceCode()) return os << "<No Source>";
|
||||
|
||||
// Get the source for the script which this function came from.
|
||||
// Don't use String::cast because we don't want more assertion errors while
|
||||
// we are already creating a stack dump.
|
||||
String script_source =
|
||||
String::unchecked_cast(Script::cast(s.script()).source());
|
||||
|
||||
if (!script_source.LooksValid()) return os << "<Invalid Source>";
|
||||
|
||||
if (!s.is_toplevel()) {
|
||||
os << "function ";
|
||||
String name = s.Name();
|
||||
if (name.length() > 0) {
|
||||
name.PrintUC16(os);
|
||||
}
|
||||
}
|
||||
|
||||
int len = s.EndPosition() - s.StartPosition();
|
||||
if (len <= v.max_length || v.max_length < 0) {
|
||||
script_source.PrintUC16(os, s.StartPosition(), s.EndPosition());
|
||||
return os;
|
||||
} else {
|
||||
script_source.PrintUC16(os, s.StartPosition(),
|
||||
s.StartPosition() + v.max_length);
|
||||
return os << "...\n";
|
||||
}
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::DisableOptimization(BailoutReason reason) {
|
||||
DCHECK_NE(reason, BailoutReason::kNoReason);
|
||||
|
||||
set_flags(DisabledOptimizationReasonBits::update(flags(), reason));
|
||||
// Code should be the lazy compilation stub or else interpreted.
|
||||
DCHECK(abstract_code().kind() == AbstractCode::INTERPRETED_FUNCTION ||
|
||||
abstract_code().kind() == AbstractCode::BUILTIN);
|
||||
PROFILE(GetIsolate(),
|
||||
CodeDisableOptEvent(handle(abstract_code(), GetIsolate()),
|
||||
handle(*this, GetIsolate())));
|
||||
if (FLAG_trace_opt) {
|
||||
CodeTracer::Scope scope(GetIsolate()->GetCodeTracer());
|
||||
PrintF(scope.file(), "[disabled optimization for ");
|
||||
ShortPrint(scope.file());
|
||||
PrintF(scope.file(), ", reason: %s]\n", GetBailoutReason(reason));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
template <typename LocalIsolate>
|
||||
void SharedFunctionInfo::InitFromFunctionLiteral(
|
||||
LocalIsolate* isolate, Handle<SharedFunctionInfo> shared_info,
|
||||
FunctionLiteral* lit, bool is_toplevel) {
|
||||
DCHECK(!shared_info->name_or_scope_info().IsScopeInfo());
|
||||
|
||||
// When adding fields here, make sure DeclarationScope::AnalyzePartially is
|
||||
// updated accordingly.
|
||||
shared_info->set_internal_formal_parameter_count(lit->parameter_count());
|
||||
shared_info->SetFunctionTokenPosition(lit->function_token_position(),
|
||||
lit->start_position());
|
||||
shared_info->set_syntax_kind(lit->syntax_kind());
|
||||
shared_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
|
||||
shared_info->set_language_mode(lit->language_mode());
|
||||
shared_info->set_function_literal_id(lit->function_literal_id());
|
||||
// FunctionKind must have already been set.
|
||||
DCHECK(lit->kind() == shared_info->kind());
|
||||
shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject());
|
||||
DCHECK_IMPLIES(lit->requires_instance_members_initializer(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_requires_instance_members_initializer(
|
||||
lit->requires_instance_members_initializer());
|
||||
DCHECK_IMPLIES(lit->class_scope_has_private_brand(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_class_scope_has_private_brand(
|
||||
lit->class_scope_has_private_brand());
|
||||
DCHECK_IMPLIES(lit->has_static_private_methods_or_accessors(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_has_static_private_methods_or_accessors(
|
||||
lit->has_static_private_methods_or_accessors());
|
||||
|
||||
shared_info->set_is_toplevel(is_toplevel);
|
||||
DCHECK(shared_info->outer_scope_info().IsTheHole());
|
||||
if (!is_toplevel) {
|
||||
Scope* outer_scope = lit->scope()->GetOuterScopeWithContext();
|
||||
if (outer_scope) {
|
||||
shared_info->set_outer_scope_info(*outer_scope->scope_info());
|
||||
shared_info->set_private_name_lookup_skips_outer_class(
|
||||
lit->scope()->private_name_lookup_skips_outer_class());
|
||||
}
|
||||
}
|
||||
|
||||
shared_info->set_length(lit->function_length());
|
||||
|
||||
// For lazy parsed functions, the following flags will be inaccurate since we
|
||||
// don't have the information yet. They're set later in
|
||||
// SetSharedFunctionFlagsFromLiteral (compiler.cc), when the function is
|
||||
// really parsed and compiled.
|
||||
if (lit->ShouldEagerCompile()) {
|
||||
shared_info->set_has_duplicate_parameters(lit->has_duplicate_parameters());
|
||||
shared_info->UpdateAndFinalizeExpectedNofPropertiesFromEstimate(lit);
|
||||
shared_info->set_is_safe_to_skip_arguments_adaptor(
|
||||
lit->SafeToSkipArgumentsAdaptor());
|
||||
DCHECK_NULL(lit->produced_preparse_data());
|
||||
|
||||
// If we're about to eager compile, we'll have the function literal
|
||||
// available, so there's no need to wastefully allocate an uncompiled data.
|
||||
return;
|
||||
}
|
||||
|
||||
shared_info->set_is_safe_to_skip_arguments_adaptor(false);
|
||||
shared_info->UpdateExpectedNofPropertiesFromEstimate(lit);
|
||||
|
||||
Handle<UncompiledData> data;
|
||||
|
||||
ProducedPreparseData* scope_data = lit->produced_preparse_data();
|
||||
if (scope_data != nullptr) {
|
||||
Handle<PreparseData> preparse_data = scope_data->Serialize(isolate);
|
||||
|
||||
data = isolate->factory()->NewUncompiledDataWithPreparseData(
|
||||
lit->GetInferredName(isolate), lit->start_position(),
|
||||
lit->end_position(), preparse_data);
|
||||
} else {
|
||||
data = isolate->factory()->NewUncompiledDataWithoutPreparseData(
|
||||
lit->GetInferredName(isolate), lit->start_position(),
|
||||
lit->end_position());
|
||||
}
|
||||
|
||||
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, Handle<SharedFunctionInfo> shared_info,
|
||||
FunctionLiteral* lit, bool is_toplevel);
|
||||
|
||||
uint16_t SharedFunctionInfo::get_property_estimate_from_literal(
|
||||
FunctionLiteral* literal) {
|
||||
int estimate = literal->expected_property_count();
|
||||
|
||||
// If this is a class constructor, we may have already parsed fields.
|
||||
if (is_class_constructor()) {
|
||||
estimate += expected_nof_properties();
|
||||
}
|
||||
return estimate;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::UpdateExpectedNofPropertiesFromEstimate(
|
||||
FunctionLiteral* literal) {
|
||||
// Limit actual estimate to fit in a 8 bit field, we will never allocate
|
||||
// more than this in any case.
|
||||
STATIC_ASSERT(JSObject::kMaxInObjectProperties <= kMaxUInt8);
|
||||
int estimate = get_property_estimate_from_literal(literal);
|
||||
set_expected_nof_properties(std::min(estimate, kMaxUInt8));
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::UpdateAndFinalizeExpectedNofPropertiesFromEstimate(
|
||||
FunctionLiteral* literal) {
|
||||
DCHECK(literal->ShouldEagerCompile());
|
||||
if (are_properties_final()) {
|
||||
return;
|
||||
}
|
||||
int estimate = get_property_estimate_from_literal(literal);
|
||||
|
||||
// If no properties are added in the constructor, they are more likely
|
||||
// to be added later.
|
||||
if (estimate == 0) estimate = 2;
|
||||
|
||||
// Limit actual estimate to fit in a 8 bit field, we will never allocate
|
||||
// more than this in any case.
|
||||
STATIC_ASSERT(JSObject::kMaxInObjectProperties <= kMaxUInt8);
|
||||
estimate = std::min(estimate, kMaxUInt8);
|
||||
|
||||
set_expected_nof_properties(estimate);
|
||||
set_are_properties_final(true);
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::SetFunctionTokenPosition(int function_token_position,
|
||||
int start_position) {
|
||||
int offset;
|
||||
if (function_token_position == kNoSourcePosition) {
|
||||
offset = 0;
|
||||
} else {
|
||||
offset = start_position - function_token_position;
|
||||
}
|
||||
|
||||
if (offset > kMaximumFunctionTokenOffset) {
|
||||
offset = kFunctionTokenOutOfRange;
|
||||
}
|
||||
set_raw_function_token_offset(offset);
|
||||
}
|
||||
|
||||
int SharedFunctionInfo::StartPosition() const {
|
||||
Object maybe_scope_info = name_or_scope_info();
|
||||
if (maybe_scope_info.IsScopeInfo()) {
|
||||
ScopeInfo info = ScopeInfo::cast(maybe_scope_info);
|
||||
if (info.HasPositionInfo()) {
|
||||
return info.StartPosition();
|
||||
}
|
||||
}
|
||||
if (HasUncompiledData()) {
|
||||
// Works with or without scope.
|
||||
return uncompiled_data().start_position();
|
||||
}
|
||||
if (IsApiFunction() || HasBuiltinId()) {
|
||||
DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy);
|
||||
return 0;
|
||||
}
|
||||
if (HasWasmExportedFunctionData()) {
|
||||
WasmInstanceObject instance = wasm_exported_function_data().instance();
|
||||
int func_index = wasm_exported_function_data().function_index();
|
||||
auto& function = instance.module()->functions[func_index];
|
||||
return static_cast<int>(function.code.offset());
|
||||
}
|
||||
return kNoSourcePosition;
|
||||
}
|
||||
|
||||
int SharedFunctionInfo::EndPosition() const {
|
||||
Object maybe_scope_info = name_or_scope_info();
|
||||
if (maybe_scope_info.IsScopeInfo()) {
|
||||
ScopeInfo info = ScopeInfo::cast(maybe_scope_info);
|
||||
if (info.HasPositionInfo()) {
|
||||
return info.EndPosition();
|
||||
}
|
||||
}
|
||||
if (HasUncompiledData()) {
|
||||
// Works with or without scope.
|
||||
return uncompiled_data().end_position();
|
||||
}
|
||||
if (IsApiFunction() || HasBuiltinId()) {
|
||||
DCHECK_IMPLIES(HasBuiltinId(), builtin_id() != Builtins::kCompileLazy);
|
||||
return 0;
|
||||
}
|
||||
if (HasWasmExportedFunctionData()) {
|
||||
WasmInstanceObject instance = wasm_exported_function_data().instance();
|
||||
int func_index = wasm_exported_function_data().function_index();
|
||||
auto& function = instance.module()->functions[func_index];
|
||||
return static_cast<int>(function.code.end_offset());
|
||||
}
|
||||
return kNoSourcePosition;
|
||||
}
|
||||
|
||||
void SharedFunctionInfo::SetPosition(int start_position, int end_position) {
|
||||
Object maybe_scope_info = name_or_scope_info();
|
||||
if (maybe_scope_info.IsScopeInfo()) {
|
||||
ScopeInfo info = ScopeInfo::cast(maybe_scope_info);
|
||||
if (info.HasPositionInfo()) {
|
||||
info.SetPositionInfo(start_position, end_position);
|
||||
}
|
||||
} else if (HasUncompiledData()) {
|
||||
if (HasUncompiledDataWithPreparseData()) {
|
||||
// Clear out preparsed scope data, since the position setter invalidates
|
||||
// any scope data.
|
||||
ClearPreparseData();
|
||||
}
|
||||
uncompiled_data().set_start_position(start_position);
|
||||
uncompiled_data().set_end_position(end_position);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool SharedFunctionInfo::AreSourcePositionsAvailable() const {
|
||||
if (FLAG_enable_lazy_source_positions) {
|
||||
return !HasBytecodeArray() || GetBytecodeArray().HasSourcePositionTable();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void SharedFunctionInfo::EnsureSourcePositionsAvailable(
|
||||
Isolate* isolate, Handle<SharedFunctionInfo> shared_info) {
|
||||
if (FLAG_enable_lazy_source_positions && shared_info->HasBytecodeArray() &&
|
||||
!shared_info->GetBytecodeArray().HasSourcePositionTable()) {
|
||||
Compiler::CollectSourcePositions(isolate, shared_info);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user