/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/objects/script.cc
552 строки
19 KB
Michaël Zasso
deps: update V8 to 14.2.231.9
23 окт 2025, 08:36
Не верифицирован
23 окт 2025, 08:36
c2843b7
Код
Авторство
О чём код?
// Copyright 2025 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/script.h" #include "src/ast/ast.h" #include "src/common/globals.h" #include "src/objects/shared-function-info-inl.h" #include "src/tracing/traced-value.h" #include "src/utils/hex-format.h" #include "src/utils/sha-256.h" namespace v8::internal { template <typename IsolateT> MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( DirectHandle<Script> script, IsolateT* isolate, FunctionLiteral* function_literal) { DCHECK(function_literal->shared_function_info().is_null()); int function_literal_id = function_literal->function_literal_id(); CHECK_GE(function_literal_id, 0); // If this check fails, the problem is most probably the function id // renumbering done by AstFunctionLiteralIdReindexer; in particular, that // AstTraversalVisitor doesn't recurse properly in the construct which // triggers the mismatch. CHECK_LT(function_literal_id, script->infos()->length()); Tagged<MaybeObject> shared = script->infos()->get(function_literal_id); Tagged<HeapObject> heap_object; if (!shared.GetHeapObject(&heap_object) || IsUndefined(heap_object, isolate)) { return MaybeHandle<SharedFunctionInfo>(); } Handle<SharedFunctionInfo> result(Cast<SharedFunctionInfo>(heap_object), isolate); CHECK(Is<SharedFunctionInfo>(*result)); CHECK_EQ(result->StartPosition(), function_literal->start_position()); CHECK_EQ(result->EndPosition(), function_literal->end_position()); CHECK_EQ(result->function_literal_id(kRelaxedLoad), function_literal->function_literal_id()); function_literal->set_shared_function_info(result); return result; } template MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( DirectHandle<Script> script, Isolate* isolate, FunctionLiteral* function_literal); template MaybeHandle<SharedFunctionInfo> Script::FindSharedFunctionInfo( DirectHandle<Script> script, LocalIsolate* isolate, FunctionLiteral* function_literal); Script::Iterator::Iterator(Isolate* isolate) : iterator_(isolate->heap()->script_list()) {} Tagged<Script> Script::Iterator::Next() { Tagged<Object> o = iterator_.Next(); if (o != Tagged<Object>()) { return Cast<Script>(o); } return Script(); } // static int Script::GetEvalPosition(Isolate* isolate, DirectHandle<Script> script) { DCHECK(script->compilation_type() == Script::CompilationType::kEval); int position = script->eval_from_position(); if (position < 0) { // Due to laziness, the position may not have been translated from code // offset yet, which would be encoded as negative integer. In that case, // translate and set the position. if (!script->has_eval_from_shared()) { position = 0; } else { DirectHandle<SharedFunctionInfo> shared(script->eval_from_shared(), isolate); SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared); position = shared->abstract_code(isolate)->SourcePosition(isolate, -position); } DCHECK_GE(position, 0); script->set_eval_from_position(position); } return position; } String::LineEndsVector Script::GetLineEnds(Isolate* isolate, DirectHandle<Script> script) { DCHECK(!script->has_line_ends()); Tagged<Object> src_obj = script->source(); if (IsString(src_obj)) { DirectHandle<String> src(Cast<String>(src_obj), isolate); return String::CalculateLineEndsVector(isolate, src, true); } return String::LineEndsVector(); } template <typename IsolateT> // static void Script::InitLineEndsInternal(IsolateT* isolate, DirectHandle<Script> script) { DCHECK(!script->has_line_ends()); DCHECK(script->CanHaveLineEnds()); Tagged<Object> src_obj = script->source(); if (!IsString(src_obj)) { DCHECK(IsUndefined(src_obj, isolate)); script->set_line_ends(ReadOnlyRoots(isolate).empty_fixed_array()); } else { DCHECK(IsString(src_obj)); DirectHandle<String> src(Cast<String>(src_obj), isolate); DirectHandle<FixedArray> array = String::CalculateLineEnds(isolate, src, true); script->set_line_ends(*array); } DCHECK(IsFixedArray(script->line_ends())); DCHECK(script->has_line_ends()); } void Script::SetSource(Isolate* isolate, DirectHandle<Script> script, DirectHandle<String> source) { script->set_source(*source); if (isolate->NeedsSourcePositions()) { InitLineEnds(isolate, script); } else if (script->line_ends() == ReadOnlyRoots(isolate).empty_fixed_array()) { DCHECK(script->has_line_ends()); script->set_line_ends(Smi::zero()); DCHECK(!script->has_line_ends()); } } template V8_EXPORT_PRIVATE void Script::InitLineEndsInternal( Isolate* isolate, DirectHandle<Script> script); template V8_EXPORT_PRIVATE void Script::InitLineEndsInternal( LocalIsolate* isolate, DirectHandle<Script> script); bool Script::GetPositionInfo(DirectHandle<Script> script, int position, PositionInfo* info, OffsetFlag offset_flag) { #if V8_ENABLE_WEBASSEMBLY // For wasm, we do not create an artificial line_ends array, but do the // translation directly. #ifdef DEBUG if (script->type() == Type::kWasm) { DCHECK(script->has_line_ends()); DCHECK_EQ(Cast<FixedArray>(script->line_ends())->length(), 0); } #endif // DEBUG #endif // V8_ENABLE_WEBASSEMBLY InitLineEnds(Isolate::Current(), script); return script->GetPositionInfo(position, info, offset_flag); } bool Script::IsSubjectToDebugging() const { switch (type()) { case Type::kNormal: #if V8_ENABLE_WEBASSEMBLY case Type::kWasm: #endif // V8_ENABLE_WEBASSEMBLY return true; case Type::kNative: case Type::kInspector: case Type::kExtension: return false; } UNREACHABLE(); } bool Script::IsUserJavaScript() const { return type() == Script::Type::kNormal; } #if V8_ENABLE_WEBASSEMBLY bool Script::ContainsAsmModule() { DisallowGarbageCollection no_gc; SharedFunctionInfo::ScriptIterator iter(Isolate::Current(), *this); for (Tagged<SharedFunctionInfo> sfi = iter.Next(); !sfi.is_null(); sfi = iter.Next()) { if (sfi->HasAsmWasmData()) return true; } return false; } #endif // V8_ENABLE_WEBASSEMBLY void Script::TraceScriptRundown() { DisallowGarbageCollection no_gc; Isolate* isolate = Isolate::Current(); Tagged<Object> context_value = isolate->native_context()->debug_context_id(); int contextId = (IsSmi(context_value)) ? Smi::ToInt(context_value) : 0; auto value = v8::tracing::TracedValue::Create(); value->SetInteger("scriptId", this->id()); value->SetInteger("executionContextId", contextId); value->SetString("isolate", std::to_string(isolate->debug()->IsolateId())); value->SetBoolean("isModule", this->origin_options().IsModule()); value->SetBoolean("hasSourceUrl", this->HasSourceURLComment()); if (this->HasValidSource()) { if (this->HasSourceURLComment()) { value->SetString("sourceUrl", Cast<String>(this->source_url())->ToCString().get()); } if (this->HasSourceMappingURLComment()) { Tagged<String> sourceMapUrl = Cast<String>(this->source_mapping_url()); // Source maps can be huge. Don't ever put a huge data url source map in // the trace. Also omit unusually large regular URLs. constexpr int kMaxSourceMapUrlLength = 2048; if (sourceMapUrl->length() > kMaxSourceMapUrlLength) { // Signal that there is a source map - clients may wish to request it by // other means (such as via CDP) or otherwise warn that a source map was // present but elided. value->SetBoolean("sourceMapUrlElided", true); } else { value->SetString("sourceMapUrl", sourceMapUrl->ToCString().get()); } } } if (IsString(this->name())) { value->SetString("url", Cast<String>(this->name())->ToCString().get()); } TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.v8-source-rundown"), "ScriptCatchup", "data", std::move(value)); } void Script::TraceScriptRundownSources() { DisallowGarbageCollection no_gc; Isolate* isolate = Isolate::Current(); if (!IsString(this->source())) return; Tagged<String> source = Cast<String>(this->source()); auto script_id = this->id(); int32_t source_length = source->length(); const int32_t kSourceMaxLength = 25000000; // 25mb const int32_t kSplitMaxLength = 1000000; // 1mb if (source_length > kSourceMaxLength) { auto value = v8::tracing::TracedValue::Create(); value->SetString("isolate", std::to_string(isolate->debug()->IsolateId())); value->SetInteger("scriptId", script_id); value->SetInteger("length", source_length); value->SetInteger("limit", kSourceMaxLength); TRACE_EVENT1( TRACE_DISABLED_BY_DEFAULT("devtools.v8-source-rundown-sources"), "TooLargeScriptCatchup", "data", std::move(value)); } else if (source_length <= kSplitMaxLength) { auto value = v8::tracing::TracedValue::Create(); value->SetString("isolate", std::to_string(isolate->debug()->IsolateId())); value->SetInteger("scriptId", script_id); value->SetInteger("length", source_length); value->SetString("sourceText", source->ToCString().get()); TRACE_EVENT1( TRACE_DISABLED_BY_DEFAULT("devtools.v8-source-rundown-sources"), "ScriptCatchup", "data", std::move(value)); } else { int32_t split_count = source_length / kSplitMaxLength + 1; std::unique_ptr<char[]> source_ptr = source->ToCString(); for (int32_t i = 0; i < split_count; i++) { int32_t begin = i * kSplitMaxLength; int32_t end = std::min(begin + kSplitMaxLength, source_length); auto split_trace_value = v8::tracing::TracedValue::Create(); split_trace_value->SetInteger("splitIndex", i); split_trace_value->SetInteger("splitCount", split_count); split_trace_value->SetString( "isolate", std::to_string(isolate->debug()->IsolateId())); split_trace_value->SetInteger("scriptId", script_id); split_trace_value->SetString( "sourceText", std::string(source_ptr.get() + begin, end - begin)); TRACE_EVENT1( TRACE_DISABLED_BY_DEFAULT("devtools.v8-source-rundown-sources"), "LargeScriptCatchup", "data", std::move(split_trace_value)); } } } void Script::AddPositionInfoOffset(PositionInfo* info, OffsetFlag offset_flag) const { // Add offsets if requested. if (offset_flag == OffsetFlag::kWithOffset) { if (info->line == 0) { info->column += column_offset(); } info->line += line_offset(); } else { DCHECK_EQ(offset_flag, OffsetFlag::kNoOffset); } } namespace { template <typename Char> bool GetPositionInfoSlowImpl(base::Vector<Char> source, int position, Script::PositionInfo* info) { DCHECK(DisallowPositionInfoSlow::IsAllowed()); if (position < 0) { position = 0; } int line = 0; const auto begin = std::cbegin(source); const auto end = std::cend(source); for (auto line_begin = begin; line_begin < end;) { const auto line_end = std::find(line_begin, end, '\n'); if (position <= (line_end - begin)) { info->line = line; info->column = static_cast<int>((begin + position) - line_begin); info->line_start = static_cast<int>(line_begin - begin); info->line_end = static_cast<int>(line_end - begin); return true; } ++line; line_begin = line_end + 1; } return false; } bool GetPositionInfoSlow(const Tagged<Script> script, int position, const DisallowGarbageCollection& no_gc, Script::PositionInfo* info) { if (!IsString(script->source())) { return false; } auto source = Cast<String>(script->source()); const auto flat = source->GetFlatContent(no_gc); return flat.IsOneByte() ? GetPositionInfoSlowImpl(flat.ToOneByteVector(), position, info) : GetPositionInfoSlowImpl(flat.ToUC16Vector(), position, info); } int GetLineEnd(const String::LineEndsVector& vector, int line) { return vector[line]; } int GetLineEnd(const Tagged<FixedArray>& array, int line) { return Smi::ToInt(array->get(line)); } int GetLength(const String::LineEndsVector& vector) { return static_cast<int>(vector.size()); } int GetLength(const Tagged<FixedArray>& array) { return array->length(); } template <typename LineEndsContainer> bool GetLineEndsContainerPositionInfo(const LineEndsContainer& ends, int position, Script::PositionInfo* info, const DisallowGarbageCollection& no_gc) { const int ends_len = GetLength(ends); if (ends_len == 0) return false; // Return early on invalid positions. Negative positions behave as if 0 was // passed, and positions beyond the end of the script return as failure. if (position < 0) { position = 0; } else if (position > GetLineEnd(ends, ends_len - 1)) { return false; } // Determine line number by doing a binary search on the line ends array. if (GetLineEnd(ends, 0) >= position) { info->line = 0; info->line_start = 0; info->column = position; } else { int left = 0; int right = ends_len - 1; while (right > 0) { DCHECK_LE(left, right); const int mid = left + (right - left) / 2; if (position > GetLineEnd(ends, mid)) { left = mid + 1; } else if (position <= GetLineEnd(ends, mid - 1)) { right = mid - 1; } else { info->line = mid; break; } } DCHECK(GetLineEnd(ends, info->line) >= position && GetLineEnd(ends, info->line - 1) < position); info->line_start = GetLineEnd(ends, info->line - 1) + 1; info->column = position - info->line_start; } return true; } } // namespace template <typename LineEndsContainer> bool Script::GetPositionInfoInternal( const LineEndsContainer& ends, int position, Script::PositionInfo* info, const DisallowGarbageCollection& no_gc) const { if (!GetLineEndsContainerPositionInfo(ends, position, info, no_gc)) return false; // Line end is position of the linebreak character. info->line_end = GetLineEnd(ends, info->line); if (info->line_end > 0) { DCHECK(IsString(source())); Tagged<String> src = Cast<String>(source()); if (src->length() >= static_cast<uint32_t>(info->line_end) && src->Get(info->line_end - 1) == '\r') { info->line_end--; } } return true; } template bool Script::GetPositionInfoInternal<String::LineEndsVector>( const String::LineEndsVector& ends, int position, Script::PositionInfo* info, const DisallowGarbageCollection& no_gc) const; template bool Script::GetPositionInfoInternal<Tagged<FixedArray>>( const Tagged<FixedArray>& ends, int position, Script::PositionInfo* info, const DisallowGarbageCollection& no_gc) const; bool Script::GetPositionInfo(int position, PositionInfo* info, OffsetFlag offset_flag) const { DisallowGarbageCollection no_gc; #if V8_ENABLE_WEBASSEMBLY // For wasm, we use the byte offset as the column. if (type() == Script::Type::kWasm) { DCHECK_LE(0, position); wasm::NativeModule* native_module = wasm_native_module(); const wasm::WasmModule* module = native_module->module(); if (module->functions.empty()) return false; info->line = 0; info->column = position; info->line_start = module->functions[0].code.offset(); info->line_end = module->functions.back().code.end_offset(); return true; } #endif // V8_ENABLE_WEBASSEMBLY if (!has_line_ends()) { // Slow mode: we do not have line_ends. We have to iterate through source. if (!GetPositionInfoSlow(*this, position, no_gc, info)) { return false; } } else { DCHECK(has_line_ends()); Tagged<FixedArray> ends = Cast<FixedArray>(line_ends()); if (!GetPositionInfoInternal(ends, position, info, no_gc)) return false; } AddPositionInfoOffset(info, offset_flag); return true; } bool Script::GetPositionInfoWithLineEnds( int position, PositionInfo* info, const String::LineEndsVector& line_ends, OffsetFlag offset_flag) const { DisallowGarbageCollection no_gc; if (!GetPositionInfoInternal(line_ends, position, info, no_gc)) return false; AddPositionInfoOffset(info, offset_flag); return true; } bool Script::GetLineColumnWithLineEnds( int position, int& line, int& column, const String::LineEndsVector& line_ends) { DisallowGarbageCollection no_gc; PositionInfo info; if (!GetLineEndsContainerPositionInfo(line_ends, position, &info, no_gc)) { line = -1; column = -1; return false; } line = info.line; column = info.column; return true; } int Script::GetColumnNumber(DirectHandle<Script> script, int code_pos) { PositionInfo info; GetPositionInfo(script, code_pos, &info); return info.column; } int Script::GetColumnNumber(int code_pos) const { PositionInfo info; GetPositionInfo(code_pos, &info); return info.column; } int Script::GetLineNumber(DirectHandle<Script> script, int code_pos) { PositionInfo info; GetPositionInfo(script, code_pos, &info); return info.line; } int Script::GetLineNumber(int code_pos) const { PositionInfo info; GetPositionInfo(code_pos, &info); return info.line; } Tagged<Object> Script::GetNameOrSourceURL() { // Keep in sync with ScriptNameOrSourceURL in messages.js. if (!IsUndefined(source_url())) return source_url(); return name(); } // static DirectHandle<String> Script::GetScriptHash(Isolate* isolate, DirectHandle<Script> script, bool forceForInspector) { if (script->origin_options().IsOpaque() && !forceForInspector) { return isolate->factory()->empty_string(); } PtrComprCageBase cage_base(isolate); { Tagged<Object> maybe_source_hash = script->source_hash(cage_base); if (IsString(maybe_source_hash, cage_base)) { DirectHandle<String> precomputed(Cast<String>(maybe_source_hash), isolate); if (precomputed->length() > 0) { return precomputed; } } } DirectHandle<String> src_text; { Tagged<Object> maybe_script_source = script->source(cage_base); if (!IsString(maybe_script_source, cage_base)) { return isolate->factory()->empty_string(); } src_text = direct_handle(Cast<String>(maybe_script_source), isolate); } char formatted_hash[kSizeOfFormattedSha256Digest]; std::unique_ptr<char[]> string_val = src_text->ToCString(); size_t len = strlen(string_val.get()); uint8_t hash[kSizeOfSha256Digest]; SHA256_hash(string_val.get(), len, hash); FormatBytesToHex(formatted_hash, kSizeOfFormattedSha256Digest, hash, kSizeOfSha256Digest); formatted_hash[kSizeOfSha256Digest * 2] = '\0'; DirectHandle<String> result = isolate->factory()->NewStringFromAsciiChecked(formatted_hash); script->set_source_hash(*result); return result; } } // namespace v8::internal