Commit c4d1feee authored by Benedikt Meurer's avatar Benedikt Meurer Committed by V8 LUCI CQ

[inspector] Introduce v8::debug::ScriptSource.

This introduces a new (inspector-only) `v8::debug::ScriptSource`,
which represents the source for a given `v8::debug::Script` (in
case of JavaScript it's a `v8::internal::String` while in case of
WebAssembly it's a `Managed<v8::internal::wasm::NativeModule>`).
Every `v8_inspector::V8DebuggerScript` now holds on weakly to the
`v8::debug::Script` and strongly to its `ScriptSource`, making it
possible to access the source even after the `Script` dies.

This is preliminary work to allow for the removal of the special
GC feature that a `WeakCallbackType::kFinalizer` callback can
resurrect the object (this change is split into a separate follow
up CL https://crrev.com/c/3497324).

Bug: chromium:1295659, chromium:1302195
Doc: https://bit.ly/v8-inspector-script-caching
Change-Id: I503d0d9283e2da392023f06f79b8ff35953e7935
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3494242
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarMichael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79363}
parent bf1565d7
......@@ -41,6 +41,7 @@ class JSFinalizationRegistry;
namespace debug {
class AccessorPair;
class GeneratorObject;
class ScriptSource;
class Script;
class EphemeronTable;
} // namespace debug
......@@ -134,6 +135,7 @@ class RegisteredExtension {
V(StackFrame, StackFrameInfo) \
V(Proxy, JSProxy) \
V(debug::GeneratorObject, JSGeneratorObject) \
V(debug::ScriptSource, HeapObject) \
V(debug::Script, Script) \
V(debug::EphemeronTable, EphemeronHashTable) \
V(debug::AccessorPair, AccessorPair) \
......
......@@ -295,6 +295,41 @@ bool CanBreakProgram(Isolate* v8_isolate) {
return !isolate->debug()->AllFramesOnStackAreBlackboxed();
}
size_t ScriptSource::Length() const {
i::Handle<i::HeapObject> source = Utils::OpenHandle(this);
if (source->IsString()) return i::Handle<i::String>::cast(source)->length();
return Size();
}
size_t ScriptSource::Size() const {
#if V8_ENABLE_WEBASSEMBLY
MemorySpan<const uint8_t> wasm_bytecode;
if (WasmBytecode().To(&wasm_bytecode)) {
return wasm_bytecode.size();
}
#endif // V8_ENABLE_WEBASSEMBLY
i::Handle<i::HeapObject> source = Utils::OpenHandle(this);
if (!source->IsString()) return 0;
i::Handle<i::String> string = i::Handle<i::String>::cast(source);
return string->length() * (string->IsTwoByteRepresentation() ? 2 : 1);
}
MaybeLocal<String> ScriptSource::JavaScriptCode() const {
i::Handle<i::HeapObject> source = Utils::OpenHandle(this);
if (!source->IsString()) return MaybeLocal<String>();
return Utils::ToLocal(i::Handle<i::String>::cast(source));
}
#if V8_ENABLE_WEBASSEMBLY
Maybe<MemorySpan<const uint8_t>> ScriptSource::WasmBytecode() const {
i::Handle<i::HeapObject> source = Utils::OpenHandle(this);
if (!source->IsForeign()) return Nothing<MemorySpan<const uint8_t>>();
base::Vector<const uint8_t> wire_bytes =
i::Managed<i::wasm::NativeModule>::cast(*source).raw()->wire_bytes();
return Just(MemorySpan<const uint8_t>{wire_bytes.begin(), wire_bytes.size()});
}
#endif // V8_ENABLE_WEBASSEMBLY
Isolate* Script::GetIsolate() const {
return reinterpret_cast<Isolate*>(Utils::OpenHandle(this)->GetIsolate());
}
......@@ -387,12 +422,18 @@ Maybe<int> Script::ContextId() const {
return Nothing<int>();
}
MaybeLocal<String> Script::Source() const {
Local<ScriptSource> Script::Source() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
i::Handle<i::PrimitiveHeapObject> value(script->source(), isolate);
if (!value->IsString()) return MaybeLocal<String>();
return Utils::ToLocal(i::Handle<i::String>::cast(value));
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::TYPE_WASM) {
i::Handle<i::Object> wasm_native_module(
script->wasm_managed_native_module(), isolate);
return Utils::Convert<i::Object, ScriptSource>(wasm_native_module);
}
#endif // V8_ENABLE_WEBASSEMBLY
i::Handle<i::PrimitiveHeapObject> source(script->source(), isolate);
return Utils::Convert<i::PrimitiveHeapObject, ScriptSource>(source);
}
#if V8_ENABLE_WEBASSEMBLY
......@@ -633,13 +674,6 @@ int WasmScript::NumImportedFunctions() const {
return static_cast<int>(module->num_imported_functions);
}
MemorySpan<const uint8_t> WasmScript::Bytecode() const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
base::Vector<const uint8_t> wire_bytes =
script->wasm_native_module()->wire_bytes();
return {wire_bytes.begin(), wire_bytes.size()};
}
std::pair<int, int> WasmScript::GetFunctionRange(int function_index) const {
i::DisallowGarbageCollection no_gc;
i::Handle<i::Script> script = Utils::OpenHandle(this);
......
......@@ -160,6 +160,28 @@ struct LiveEditResult {
int column_number = -1;
};
/**
* An internal representation of the source for a given
* `v8::debug::Script`, which can be a `v8::String`, in
* which case it represents JavaScript source, or it can
* be a managed pointer to a native Wasm module, or it
* can be undefined to indicate that source is unavailable.
*/
class V8_EXPORT_PRIVATE ScriptSource {
public:
// The number of characters in case of JavaScript or
// the size of the memory in case of WebAssembly.
size_t Length() const;
// The actual size of the source in bytes.
size_t Size() const;
MaybeLocal<String> JavaScriptCode() const;
#if V8_ENABLE_WEBASSEMBLY
Maybe<MemorySpan<const uint8_t>> WasmBytecode() const;
#endif // V8_ENABLE_WEBASSEMBLY
};
/**
* Native wrapper around v8::internal::Script object.
*/
......@@ -179,7 +201,7 @@ class V8_EXPORT_PRIVATE Script {
MaybeLocal<String> SourceURL() const;
MaybeLocal<String> SourceMappingURL() const;
Maybe<int> ContextId() const;
MaybeLocal<String> Source() const;
Local<ScriptSource> Source() const;
bool IsModule() const;
bool GetPossibleBreakpoints(
const debug::Location& start, const debug::Location& end,
......@@ -209,7 +231,6 @@ class WasmScript : public Script {
MemorySpan<const char> ExternalSymbolsURL() const;
int NumFunctions() const;
int NumImportedFunctions() const;
MemorySpan<const uint8_t> Bytecode() const;
std::pair<int, int> GetFunctionRange(int function_index) const;
int GetContainingFunction(int byte_offset) const;
......
......@@ -107,7 +107,9 @@ class ActualScript : public V8DebuggerScript {
String16 source(size_t pos, size_t len) const override {
v8::HandleScope scope(m_isolate);
v8::Local<v8::String> v8Source;
if (!script()->Source().ToLocal(&v8Source)) return String16();
if (!m_scriptSource.Get(m_isolate)->JavaScriptCode().ToLocal(&v8Source)) {
return String16();
}
if (pos >= static_cast<size_t>(v8Source->Length())) return String16();
size_t substringLength =
std::min(len, static_cast<size_t>(v8Source->Length()) - pos);
......@@ -121,9 +123,11 @@ class ActualScript : public V8DebuggerScript {
#if V8_ENABLE_WEBASSEMBLY
v8::Maybe<v8::MemorySpan<const uint8_t>> wasmBytecode() const override {
v8::HandleScope scope(m_isolate);
auto script = this->script();
if (!script->IsWasm()) return v8::Nothing<v8::MemorySpan<const uint8_t>>();
return v8::Just(v8::debug::WasmScript::Cast(*script)->Bytecode());
v8::MemorySpan<const uint8_t> bytecode;
if (m_scriptSource.Get(m_isolate)->WasmBytecode().To(&bytecode)) {
return v8::Just(bytecode);
}
return v8::Nothing<v8::MemorySpan<const uint8_t>>();
}
v8::Maybe<v8::debug::WasmScript::DebugSymbolsType> getDebugSymbolsType()
......@@ -157,16 +161,7 @@ class ActualScript : public V8DebuggerScript {
return 0;
}
int length() const override {
auto script = this->script();
#if V8_ENABLE_WEBASSEMBLY
if (script->IsWasm()) {
return static_cast<int>(
v8::debug::WasmScript::Cast(*script)->Bytecode().size());
}
#endif // V8_ENABLE_WEBASSEMBLY
v8::HandleScope scope(m_isolate);
v8::Local<v8::String> v8Source;
return script->Source().ToLocal(&v8Source) ? v8Source->Length() : 0;
return static_cast<int>(m_scriptSource.Get(m_isolate)->Length());
}
const String16& sourceMappingURL() const override {
......@@ -263,7 +258,7 @@ class ActualScript : public V8DebuggerScript {
if (!m_hash.isEmpty()) return m_hash;
v8::HandleScope scope(m_isolate);
v8::Local<v8::String> v8Source;
if (!script()->Source().ToLocal(&v8Source)) {
if (!m_scriptSource.Get(m_isolate)->JavaScriptCode().ToLocal(&v8Source)) {
v8Source = v8::String::Empty(m_isolate);
}
m_hash = calculateHash(m_isolate, v8Source);
......@@ -321,6 +316,8 @@ class ActualScript : public V8DebuggerScript {
m_script.Reset(m_isolate, script);
m_script.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
m_scriptSource.Reset(m_isolate, script->Source());
m_scriptSource.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
}
void MakeWeak() override {
......@@ -348,6 +345,7 @@ class ActualScript : public V8DebuggerScript {
int m_endLine = 0;
int m_endColumn = 0;
v8::Global<v8::debug::Script> m_script;
v8::Global<v8::debug::ScriptSource> m_scriptSource;
};
} // namespace
......
......@@ -59,6 +59,7 @@ class V8DebuggerScript {
V8DebuggerScript(const V8DebuggerScript&) = delete;
V8DebuggerScript& operator=(const V8DebuggerScript&) = delete;
v8::Local<v8::debug::ScriptSource> scriptSource();
const String16& scriptId() const { return m_id; }
bool hasSourceURLComment() const { return m_hasSourceURLComment; }
const String16& sourceURL() const { return m_url; }
......
......@@ -4360,6 +4360,7 @@ TEST(DebugCoverage) {
v8::debug::Coverage::ScriptData script_data = coverage.GetScriptData(0);
v8::Local<v8::debug::Script> script = script_data.GetScript();
CHECK(script->Source()
->JavaScriptCode()
.ToLocalChecked()
->Equals(env.local(), source)
.FromMaybe(false));
......@@ -4413,6 +4414,7 @@ TEST(DebugCoverageWithCoverageOutOfScope) {
GetScriptDataAndDeleteCoverage(isolate);
v8::Local<v8::debug::Script> script = script_data.GetScript();
CHECK(script->Source()
->JavaScriptCode()
.ToLocalChecked()
->Equals(env.local(), source)
.FromMaybe(false));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment