如何通过V8引擎从C++获取JS字节码及虚拟机状态
Great questions! Working with V8's internal bytecode and VM state via C++ requires leveraging its low-level API, so let's walk through exactly how to do this.
First off, you'll need to set up your V8 development environment—either compile V8 from source or use prebuilt libraries. Once that's sorted, here's a practical implementation to fetch bytecode from any JavaScript source:
#include <v8.h> #include <iostream> #include <memory> int main() { // Initialize V8's core components v8::V8::InitializeICUDefaultLocation(nullptr); v8::V8::InitializeExternalStartupData(nullptr); std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform(); v8::V8::InitializePlatform(platform.get()); v8::V8::Initialize(); // Create an Isolate (V8's VM instance) and Context (execution context) v8::Isolate::CreateParams create_params; create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator(); v8::Isolate* isolate = v8::Isolate::New(create_params); { v8::Isolate::Scope isolate_scope(isolate); v8::HandleScope handle_scope(isolate); v8::Local<v8::Context> context = v8::Context::New(isolate); v8::Context::Scope context_scope(context); // Your target JavaScript source code const char* js_source = "class Contract { constructor() { this.str = ''; this.count = 0; } update(_value){ this.str = _value; this.count++; } }"; v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, js_source, v8::NewStringType::kNormal).ToLocalChecked(); // Compile the script into a V8 Script object v8::ScriptCompiler::Source script_source(source); v8::Local<v8::Script> script = v8::ScriptCompiler::Compile(context, &script_source).ToLocalChecked(); // Fetch the bytecode array from the compiled script v8::Local<v8::Object> bytecode_array = script->GetBytecodeArray().ToLocalChecked(); v8::Local<v8::Uint8Array> uint8_bytecode = v8::Uint8Array::Cast(bytecode_array); // Extract and print the bytecode data uint8_t* bytecode_bytes = reinterpret_cast<uint8_t*>(uint8_bytecode->Buffer()->GetBackingStore()->Data()); size_t bytecode_length = uint8_bytecode->Length(); std::cout << "Compiled script bytecode length: " << bytecode_length << "\n"; std::cout << "Bytecode (hex): "; for (size_t i = 0; i < bytecode_length; ++i) { std::cout << std::hex << static_cast<int>(bytecode_bytes[i]) << " "; } std::cout << std::dec << "\n"; } // Clean up V8 resources isolate->Dispose(); v8::V8::Dispose(); v8::V8::ShutdownPlatform(); delete create_params.array_buffer_allocator; return 0; }
Key Notes:
- The
GetBytecodeArray()method returns a V8Uint8Arraycontaining the raw bytecode for the compiled script. - You'll need to link against V8's libraries (like
v8.libon Windows orlibv8.soon Linux) when compiling this code.
Let's tailor the approach to your specific Contract class, covering both bytecode for individual methods and VM state tracking.
Fetch Bytecode for the update Method
After compiling the script, you can target specific functions (like update) to get their bytecode:
// Inside the Isolate/Context scope (after compiling the script) v8::Local<v8::Value> contract_class_val = script->Run(context).ToLocalChecked(); v8::Local<v8::Function> contract_constructor = v8::Function::Cast(*contract_class_val); // Get the `update` method from the class prototype v8::Local<v8::String> prototype_key = v8::String::NewFromUtf8(isolate, "prototype").ToLocalChecked(); v8::Local<v8::Object> prototype_obj = contract_constructor->Get(context, prototype_key).ToLocalChecked()->ToObject(context).ToLocalChecked(); v8::Local<v8::String> update_key = v8::String::NewFromUtf8(isolate, "update").ToLocalChecked(); v8::Local<v8::Function> update_func = v8::Function::Cast(*prototype_obj->Get(context, update_key).ToLocalChecked()); // Fetch and print the `update` method's bytecode v8::Local<v8::Object> update_bytecode = update_func->GetBytecodeArray().ToLocalChecked(); v8::Local<v8::Uint8Array> update_uint8 = v8::Uint8Array::Cast(update_bytecode); uint8_t* update_bytes = reinterpret_cast<uint8_t*>(update_uint8->Buffer()->GetBackingStore()->Data()); size_t update_length = update_uint8->Length(); std::cout << "\n`update` method bytecode length: " << update_length << "\n"; std::cout << "`update` bytecode (hex): "; for (size_t i = 0; i < update_length; ++i) { std::cout << std::hex << static_cast<int>(update_bytes[i]) << " "; } std::cout << std::dec << "\n";
Track Virtual Machine State
V8 provides APIs to access various VM metrics and object state:
1. Heap Memory Statistics
Get insights into V8's heap usage:
v8::HeapStatistics heap_stats; isolate->GetHeapStatistics(&heap_stats); std::cout << "\nVM Heap Statistics:\n"; std::cout << "- Total heap size: " << heap_stats.total_heap_size() << " bytes\n"; std::cout << "- Used heap size: " << heap_stats.used_heap_size() << " bytes\n"; std::cout << "- Heap size limit: " << heap_stats.heap_size_limit() << " bytes\n";
2. Contract Instance State
If you create an instance of Contract, you can inspect its runtime properties:
// Create a Contract instance v8::Local<v8::Object> contract_instance = contract_constructor->NewInstance(context).ToLocalChecked(); // Call the `update` method with a test value v8::Local<v8::Value> args[] = { v8::String::NewFromUtf8(isolate, "Hello V8").ToLocalChecked() }; contract_instance->CallAsFunction(context, contract_instance, 1, args).ToLocalChecked(); // Fetch and print instance properties v8::Local<v8::String> str_key = v8::String::NewFromUtf8(isolate, "str").ToLocalChecked(); v8::Local<v8::String> count_key = v8::String::NewFromUtf8(isolate, "count").ToLocalChecked(); v8::String::Utf8Value str_val(isolate, contract_instance->Get(context, str_key).ToLocalChecked()); int count_val = contract_instance->Get(context, count_key).ToLocalChecked()->Int32Value(context).ToChecked(); std::cout << "\nContract Instance State:\n"; std::cout << "- str: " << *str_val << "\n"; std::cout << "- count: " << count_val << "\n";
内容的提问来源于stack exchange,提问作者Lilit Zakaryan

