V8,从 C++ 生成和抛出错误对象的正确方法是什么

V8, What is the correct way to generate and throw error objects from C++

我正在将 V8(版本 8.1.307)嵌入到应用程序中。 我正在使用节点 source.com 提供的 API reference,以及 Google 的嵌入指南。

我在尝试做一件看似简单的事情时遇到了障碍:生成错误对象以响应从 Javascript 对 C++ 函数的无效调用。 文档表明它应该像构建 v8::Local <v8::String> 并将其传递给 v8::Exception::RangeError() 或类似函数一样简单,但这会触发 V8 内部的段错误。

我仔细研究了可用的示例,但它们只提供了如何将原始字符串作为异常抛出的演示,如果可能的话我宁愿不这样做。 我是不是遗漏了一些简单的东西,或者这真的坏了还是不受支持?

//Note: ErrorFactory is defined as:
    typedef v8::Local <v8::Value>(*ErrorFactory)(v8::Local <v8::String>);

void ScriptManager::ConvertNativeExceptionToJavascriptError(ErrorFactory NewError, std::exception& exception)
{
    v8::Local <v8::String> errstr = v8::String::NewFromUtf8(mIsolate, exception.what(), v8::NewStringType::kNormal).ToLocalChecked();
    
    mIsolate->ThrowException(NewError(errstr));
    
}

后面调用如下:

catch(std::exception& e)
        {
//Just pick RangeError for demonstration's sake.
            engine->ConvertNativeExceptionToJavascriptError(v8::Exception::RangeError, e);
            
        }

它在 RangeError 调用期间在 V8 内部崩溃:


AddressSanitizer:DEADLYSIGNAL
=================================================================
==7680==ERROR: AddressSanitizer: SEGV on unknown address 0x00000000a058 (pc 0x00010e07c771 bp 0x7ffee1c0baf0 sp 0x7ffee1c0ba90 T0)
==7680==The signal is caused by a READ memory access.
    #0 0x10e07c770 in v8::Exception::RangeError(v8::Local<v8::String>) api.cc:9199
    #1 0x10dff9904 in ScriptManager::ConvertNativeExceptionToJavascriptError(v8::Local<v8::Value> (*)(v8::Local<v8::String>), std::exception&) javascript.cpp:316
    #2 0x10dff7cca in CallFunction(v8::FunctionCallbackInfo<v8::Value> const&) javascript.cpp:66
    #3 0x10e0bce80 in v8::internal::FunctionCallbackArguments::Call(v8::internal::CallHandlerInfo) api-arguments-inl.h:158
    #4 0x10e0bc353 in v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::Isolate*, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::HeapObject>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::internal::Handle<v8::internal::Object>, v8::internal::BuiltinArguments) builtins-api.cc:111
    #5 0x10e0bb932 in v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*) builtins-api.cc:141
    #6 0x10ec9ac38 in Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit (test:x86_64+0x100ca8c38)
    #7 0x10ec33452 in Builtins_InterpreterEntryTrampoline (test:x86_64+0x100c41452)
    #8 0x10ec310b9 in Builtins_JSEntryTrampoline (test:x86_64+0x100c3f0b9)
    #9 0x10ec30e97 in Builtins_JSEntry (test:x86_64+0x100c3ee97)
    #10 0x10e180f90 in v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) execution.cc:372
    #11 0x10e180297 in v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::internal::Object>, int, v8::internal::Handle<v8::internal::Object>*) execution.cc:466
    #12 0x10e051399 in v8::Script::Run(v8::Local<v8::Context>) api.cc:2158
    #13 0x10e004b55 in ScriptContext::ExecuteString(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) javascript.cpp:448
    #14 0x10e00572c in ScriptContext::ExecuteFile(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >) javascript.cpp:466
    #15 0x10dff3b23 in main test.cpp:28
    #16 0x7fff6a527cc8 in start (libdyld.dylib:x86_64+0x1acc8)

==7680==Register values:
rax = 0x0000000000000000  rbx = 0x0000000000000000  rcx = 0x0000000000000000  rdx = 0x0000100000000000  
rdi = 0x000062500000c9d0  rsi = 0x00007ffee1c0bb40  rbp = 0x00007ffee1c0baf0  rsp = 0x00007ffee1c0ba90  
 r8 = 0x000062500000c9d0   r9 = 0xffffebffffff7748  r10 = 0x0000000000000043  r11 = 0x0000000000000060  
r12 = 0x000060e000000520  r13 = 0x00007ffee1c0d7e0  r14 = 0x000062500000c9d0  r15 = 0x00001fffdc381afd  
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV api.cc:9199 in v8::Exception::RangeError(v8::Local<v8::String>)
==7680==ABORTING
Abort trap: 6

代码看起来不错。我建议您使用 V8 和您的应用程序的调试版本,看看在崩溃之前是否有任何 DCHECK 失败。这些可能会为问题所在提供有价值的见解。

此类崩溃的一个可能原因是“指针压缩”配置不匹配。默认情况下,V8 在最新版本中启用了此功能。如果不更改该默认值,则必须确保编译任何自己的编译单元,#include v8.h with -DV8_COMPRESS_POINTERS -DV8_31BIT_SMIS_ON_64BIT_ARCH in your C++ compiler flags.

此外,请注意 V8 通常使用 -fno-exceptions 编译。我没有将它与使用 C++ 异常的应用程序链接的经验;所以我不确定这是否会造成任何麻烦。

(任何 v8::String 都可以;无论您使用 NewFromOneByte 还是 NewFromUtf8 构造它只会影响您提供的输入的解码方式,结果 v8::String 将是一样。)