在V8中从C++代码创建自定义错误类

Creating custom Error classes from C++ code in V8

本文关键字:自定义 错误 创建 代码 V8 中从 C++      更新时间:2023-09-26

我想在本机NodeJS(0.11.5)扩展中创建自定义异常。我正在尝试在V8(3.20.11)中创建内置Error类的子类

基本上,我正在寻找一个相当于以下Javascript代码的C++:

function MyError(message) {
  Error.apply(this, arguments);
  this.message = message;
}
MyError.prototype = new Error;
MyError.prototype.name = MyError.name;
new MyError("message")

我尝试挖掘V8源代码,发现了以下辅助方法,它似乎可以实现我想要的部分功能:

Handle<Object> Factory::NewError(const char* constructor,
                                 Handle<String> message)

不幸的是,它看起来像是一个私有的API,我对V8的理解还不够,无法自行构建类似的东西。如果能够编写一个方法,我可以用类似于创建内置Error实例的方式使用它,那将是一件很棒的事情,例如:

ThrowException(v8::Exception::Error(v8::String::New(msg)))
// becomes...
ThrowException(MyError(v8::String::New(msg)))

我正在寻找一个尽可能接近内置Error类的子类的解决方案。它可能满足以下条件:

var e = new MyError("message");
assert(e instanceof MyError);
assert(e instanceof Error);
assert(e.name === "MyError");
assert(e.message === "message");

从哪里开始有什么建议吗?

我对NodeJS了解不多,但一种可能性可能是抛出任何有意义的C++异常,在扩展边界捕获它,使用标准的V8调用来构建适当的JavaScript异常,并调用v8::ThrowException()。如果NodeJS不允许您的扩展直接访问V8,那么这种方法可能不是一种选择。

下面是一个示例程序,展示了如何设置自定义错误类并从C++实例化它。希望NodeJS能给你足够的V8访问权限来做类似的事情:

#include "v8.h"
#include <iostream>
static void Print(v8::Handle<v8::Value> value)
{
    std::cout << *v8::String::Utf8Value(value) << std::endl;
}
static v8::Local<v8::Value> RunScript(const char* code)
{
    return v8::Script::Compile(v8::String::New(code))->Run();
}
static void RunTest(v8::Isolate* isolate)
{
    // setup
    v8::Locker locker(isolate);
    v8::Isolate::Scope isolateScope(isolate);
    v8::HandleScope handleScope(isolate);
    auto context = v8::Context::New(isolate);
    v8::Context::Scope contextScope(context);
    // create custom error class/function
    auto code =
        "function MyError(message) {"
            "Error.apply(this, arguments);"
            "this.message = message;"
        "}"
        "MyError.prototype = new Error;"
        "MyError.prototype.name = MyError.name;"
        "MyError";
    auto errorFunc = RunScript(code)->ToObject();
    // create custom error instance
    auto message = v8::String::New("message");
    v8::Handle<v8::Value> args[] = { message };
    auto error = errorFunc->CallAsConstructor(1, args);
    // validate custom error instance
    context->Global()->Set(v8::String::New("e"), error);
    Print(RunScript("e instanceof MyError"));
    Print(RunScript("e instanceof Error"));
    Print(RunScript("e.name === 'MyError'"));
    Print(RunScript("e.message === 'message'"));
}
void main(void)
{
    auto isolate = v8::Isolate::New();
    RunTest(isolate);
    isolate->Dispose();
}

也许类似于:

class Error
{
    // Construction
    // ============
    public:
    virtual ~Error() throw() {};
    protected:
    Error(std::exception& exception)
    :   m_exception(&exception)
    {}
    // Element Access
    // ==============
    public:
    const char* msg() const { return m_exception->what(); }
    // Message
    // =======
    protected:
    static std::string message(const std::string& msg, const std::string& context);
    static std::string message(const std::string& msg) {
        return message(msg, "");
    }
    // Cast
    // ====
    protected:
    template <typename Derived>
    static std::exception& cast(Derived& derived) {
        return static_cast<std::exception&>(derived);
    }
    private:
    std::exception* m_exception;
};

class ErrorLogic : public std::logic_error, public Error
{
    public:
    static std::string info();
    ErrorLogic()
    :   std::logic_error(message(info())), Error(cast(*this))
    {}
    explicit ErrorLogic(const std::string& msg)
    :   std::logic_error(message(msg)), Error(cast(*this))
    {}
    template <typename T>
    ErrorLogic(const std::string& msg, const T& context)
    :   std::logic_error(message(msg, context)), Error(cast(*this))
    {}
};