# 异常模型

在脚本语言中,我们通常可以任意调用方法,只需要在方法最外层加一个 `try-catch` 块即可,异常就能很方便的被处理,而无需到处检查。

但是在脚本语言的C API中,通常会把内部脚本出现的异常以类似错误码的形式暴露。这就导致了每调用一个API都要检查是否有异常发生。否则在有异常发生的情况下继续执行逻辑通常会有问题,甚至带来crash。

因此ScriptX在异常模型设计时将脚本出现的异常,统一转成C++异常向外抛出,使得C++代码也可以很方便的处理异常,同时避免多种问题及crash。

对比一下:
```c++
// V8 需要到处检查

{
    v8::TryCatch tryCatch;
    auto ret = eval("string source");
    if (tryCatch.hasCaught()) retrun false;

    auto result = ret.get(key):
    if (tryCatch.hasCaught()) retrun false;

    auto obj = get(obj);
    if (tryCatch.hasCaught()) retrun false;

    obj.set(key, result);
    if (tryCatch.hasCaught()) retrun false;

    return true;
}

// ScriptX

{
    script::EngineScope scope(engine);
    try {
        engine->eval("string source");
        auto result = ret.get(key):
        auto obj = get(obj);
        obj.set(key, result);
        return true;
    } catch(script::Exception& e) {
        log << e;
        return false;
    }
}
```

`script::Exception`是C++的异常类型,包装了脚本异常,提供了便利的方法来获取异常的消息和堆栈。需要注意的是:

1. `script::Exception`:必须在EngineScope内创建。
2. 几乎所有需要EngineScope的接口都有可能抛异常(除非有noexcept修饰)。

**结论:几乎所有的EngineScope都要紧跟一个try-catch用于处理script::Exception异常**。如上代码实例所述,除非你需要在发生异常时 crash掉进程。

## 抛异常&捕获异常。
异常可以在JS和C++间传递。举个例子:

```c++

// 1. script throw, c++ catch
try{
    engine->eval("throw Error('hello error')");
    FAIL();
} catch (const script::Exception& e) {
    std::cout << e.message() << e.stacktrace();
}

// 2. c++ throw, sript catch
auto func = Function::newFunction([](const script::Arguments& args) {
        // c++ throw exception
        throw Exception("invalid argument");
});

engine->set("func", func);
auto ret = engine->eval(R"(
    try {
        func();
        false;
    } catch (e) {
        true;
    }}
)");
EXPECT_TRUE(ret.isBoolean());
EXPECT_TRUE(ret.asBoolean().value());

// 3. c++ throw, c++ catch
try{
    func.call({});
    FAIL();
} catch (const script::Exception& e) {
    log << e.message() << e.stacktrace();
}
```
详见单元测试 [ExceptionTest](../../test/src/ExceptionTest.cc)