diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
new file mode 100644
index 00000000..84d328a7
--- /dev/null
+++ b/.github/workflows/coverage.yml
@@ -0,0 +1,70 @@
+name: Coverage
+
+on:
+ pull_request:
+ paths-ignore:
+ - 'docs/**'
+ push:
+ branches:
+ - main
+ paths-ignore:
+ - 'docs/**'
+
+jobs:
+ coverage-mac-clang:
+ runs-on: macos-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ backends: [ V8, JavaScriptCore, Lua ]
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/cache@v2
+ with:
+ key: build-dependencies-macos
+ path: |
+ build/ScriptXTestLibs
+ build/googletest-src
+ - name: prepare lcov
+ run: |
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov
+ - name: Configure cmake
+ env:
+ SCRIPTX_TEST_FORCE_UPDATE_DEPS: ON
+ run: |
+ mkdir -p build && cd build
+ cmake ../test \
+ -DSCRIPTX_BACKEND=${{ matrix.backends }} \
+ -DDEVOPS_ENABLE_COVERAGE=ON \
+ -DCMAKE_BUILD_TYPE=DEBUG
+ - name: Compile UnitTests
+ run: |
+ cd build
+ cmake --build . -j $(sysctl -n hw.ncpu) --target UnitTests
+ - name: Run UnitTests
+ run: |
+ cd build && ./UnitTests
+ - name: Generate Coverate Data
+ # https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
+ run: |
+ cd build
+ lcov -c -d CMakeFiles/ -o all.info
+ lcov -e all.info -o coverage.info '*/ScriptX/src/*' '*/ScriptX/backend/*'
+
+ - name: Coveralls GitHub Action
+ uses: coverallsapp/github-action@v1.1.2
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ path-to-lcov: './build/coverage.info'
+ flag-name: "Backend-${{ matrix.backends }}"
+ parallel: true
+
+ coverage-finish:
+ needs: coverage-mac-clang
+ runs-on: ubuntu-latest
+ steps:
+ - name: Coveralls Finished
+ uses: coverallsapp/github-action@v1.1.2
+ with:
+ github-token: ${{ secrets.github_token }}
+ parallel-finished: true
diff --git a/README-zh.md b/README-zh.md
index 5f9ff803..df47106f 100644
--- a/README-zh.md
+++ b/README-zh.md
@@ -4,7 +4,7 @@
全能的脚本引擎抽象层
---
-[](README.md) [](https://www.apache.org/licenses/LICENSE-2.0) [](https://github.com/Tencent/ScriptX/actions/workflows/unit_tests.yml)   
+[](README.md) [](https://www.apache.org/licenses/LICENSE-2.0) [](https://github.com/Tencent/ScriptX/actions/workflows/unit_tests.yml)    [](https://coveralls.io/github/Tencent/ScriptX)
diff --git a/README.md b/README.md
index 65463144..a98d5ac5 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
---
-[](README-zh.md) [](https://www.apache.org/licenses/LICENSE-2.0) [](https://github.com/Tencent/ScriptX/actions/workflows/unit_tests.yml)   
+[](README-zh.md) [](https://www.apache.org/licenses/LICENSE-2.0) [](https://github.com/Tencent/ScriptX/actions/workflows/unit_tests.yml)    [](https://coveralls.io/github/Tencent/ScriptX)

diff --git a/backend/JavaScriptCore/JscLocalReference.cc b/backend/JavaScriptCore/JscLocalReference.cc
index 2af47146..0e853ccb 100644
--- a/backend/JavaScriptCore/JscLocalReference.cc
+++ b/backend/JavaScriptCore/JscLocalReference.cc
@@ -319,6 +319,7 @@ Local Local::asUnsupported() const {
}
bool Local::operator==(const script::Local& other) const {
+ if (isNull()) return other.isNull();
return JSValueIsStrictEqual(jsc_backend::currentEngineContextChecked(),
const_cast(val_), const_cast(other.val_));
}
diff --git a/backend/Lua/LuaEngine.cc b/backend/Lua/LuaEngine.cc
index cd745d50..b51ec2c4 100644
--- a/backend/Lua/LuaEngine.cc
+++ b/backend/Lua/LuaEngine.cc
@@ -496,7 +496,7 @@ bool LuaEngine::isInstanceOf(lua_State* lua, int classIndex, int selfIndex) {
}
bool LuaEngine::isInstanceOf(lua_State* lua, const void* classDefine, int selfIndex) {
- if (selfIndex == 0) {
+ if (selfIndex == 0 || classDefine == nullptr) {
return false;
}
diff --git a/backend/Lua/LuaHelper.cc b/backend/Lua/LuaHelper.cc
index b01bb8be..1b82ea0b 100644
--- a/backend/Lua/LuaHelper.cc
+++ b/backend/Lua/LuaHelper.cc
@@ -28,9 +28,11 @@ namespace {
int luaErrorMessageHandler(lua_State* L) {
auto msg = LuaEngine::make>(1);
- auto error = Object::newObject();
- // set message
- error.set("message", msg);
+ auto error = msg.isObject() ? msg.asObject() : Object::newObject();
+ if (!msg.isObject() || error.has("message")) {
+ // set message
+ error.set("message", msg);
+ }
// set stacktrace
luaL_traceback(L, L, nullptr, 1);
diff --git a/backend/Lua/LuaLocalReference.cc b/backend/Lua/LuaLocalReference.cc
index cf82e679..00788a30 100644
--- a/backend/Lua/LuaLocalReference.cc
+++ b/backend/Lua/LuaLocalReference.cc
@@ -146,11 +146,11 @@ ValueKind Local::getKind() const {
return ValueKind::kBoolean;
} else if (type == LUA_TFUNCTION) {
return ValueKind::kFunction;
+ } else if (isByteBuffer()) {
+ return ValueKind::kByteBuffer;
} else if (type == LUA_TTABLE) {
// lua don't have array type, the are all tables
return ValueKind::kObject;
- } else if (isByteBuffer()) {
- return ValueKind::kByteBuffer;
} else {
return ValueKind::kUnsupported;
}
@@ -160,22 +160,32 @@ bool Local::isNull() const {
return val_ == 0 || lua_isnoneornil(lua_backend::currentLua(), val_);
}
-bool Local::isString() const { return getKind() == ValueKind::kString; }
+bool Local::isString() const {
+ return val_ != 0 && lua_type(lua_backend::currentLua(), val_) == LUA_TSTRING;
+}
-bool Local::isNumber() const { return getKind() == ValueKind::kNumber; }
+bool Local::isNumber() const {
+ return val_ != 0 && lua_type(lua_backend::currentLua(), val_) == LUA_TNUMBER;
+}
-bool Local::isBoolean() const { return getKind() == ValueKind::kBoolean; }
+bool Local::isBoolean() const {
+ return val_ != 0 && lua_type(lua_backend::currentLua(), val_) == LUA_TBOOLEAN;
+}
-bool Local::isFunction() const { return getKind() == ValueKind::kFunction; }
+bool Local::isFunction() const {
+ return val_ != 0 && lua_type(lua_backend::currentLua(), val_) == LUA_TFUNCTION;
+}
-bool Local::isArray() const { return getKind() == ValueKind::kObject; }
+bool Local::isArray() const { return isObject(); }
bool Local::isByteBuffer() const {
auto engine = lua_backend::currentEngine();
return engine->byteBufferDelegate_->isByteBuffer(engine, *this);
}
-bool Local::isObject() const { return getKind() == ValueKind::kObject; }
+bool Local::isObject() const {
+ return val_ != 0 && lua_type(lua_backend::currentLua(), val_) == LUA_TTABLE;
+}
bool Local::isUnsupported() const { return getKind() == ValueKind::kUnsupported; }
diff --git a/backend/V8/V8LocalReference.cc b/backend/V8/V8LocalReference.cc
index 4c8f3b33..549a7663 100644
--- a/backend/V8/V8LocalReference.cc
+++ b/backend/V8/V8LocalReference.cc
@@ -26,43 +26,44 @@
namespace script {
-#define REF_IMPL_BASIC_FUNC(ValueType) \
- Local::Local(const Local& copy) : val_(copy.val_) {} \
- Local::Local(Local&& from) noexcept : val_(from.val_) { \
- from.val_.Clear(); \
- } \
- Local::~Local() { \
- assert(val_.IsEmpty() || EngineScope::currentEngine() != nullptr); \
- } \
- Local& Local::operator=(const Local& from) { \
- if (&from != this) { \
- val_ = from.val_; \
- } \
- return *this; \
- } \
- Local& Local::operator=(Local&& move) noexcept { \
- if (&move != this) { \
- val_ = move.val_; \
- move.val_.Clear(); \
- } \
- return *this; \
- } \
- void Local::swap(Local& rhs) noexcept { \
- if (&rhs != this) { \
- std::swap(val_, rhs.val_); \
- } \
- } \
- bool Local::operator==(const script::Local& other) const { \
- return val_->StrictEquals(other.val_); \
+#define REF_IMPL_BASIC_FUNC(ValueType) \
+ Local::Local(const Local& copy) : val_(copy.val_) {} \
+ Local::Local(Local&& from) noexcept : val_(from.val_) { \
+ from.val_.Clear(); \
+ } \
+ Local::~Local() { \
+ assert(val_.IsEmpty() || EngineScope::currentEngine() != nullptr); \
+ } \
+ Local& Local::operator=(const Local& from) { \
+ if (&from != this) { \
+ val_ = from.val_; \
+ } \
+ return *this; \
+ } \
+ Local& Local::operator=(Local&& move) noexcept { \
+ if (&move != this) { \
+ val_ = move.val_; \
+ move.val_.Clear(); \
+ } \
+ return *this; \
+ } \
+ void Local::swap(Local& rhs) noexcept { \
+ if (&rhs != this) { \
+ std::swap(val_, rhs.val_); \
+ } \
}
// if Local is created with null ref, it's an error
-#define REF_IMPL_BASIC_NOT_VALUE(ValueType) \
- Local::Local(InternalLocalRef v8Local) : val_(v8Local) { \
- if (val_.IsEmpty() || val_->IsNullOrUndefined()) throw Exception("null reference"); \
- } \
- Local Local::describe() const { return asValue().describe(); } \
- std::string Local::describeUtf8() const { return asValue().describeUtf8(); }
+#define REF_IMPL_BASIC_NOT_VALUE(ValueType) \
+ Local::Local(InternalLocalRef v8Local) : val_(v8Local) { \
+ if (val_.IsEmpty() || val_->IsNullOrUndefined()) throw Exception("null reference"); \
+ } \
+ Local Local::describe() const { return asValue().describe(); } \
+ std::string Local::describeUtf8() const { return asValue().describeUtf8(); } \
+ bool Local::operator==(const script::Local& other) const { \
+ if (other.isNull()) return false; \
+ return val_->StrictEquals(other.val_); \
+ }
#define REF_IMPL_TO_VALUE(ValueType) \
Local Local::asValue() const { return Local(val_.As()); }
@@ -133,6 +134,11 @@ ValueKind Local::getKind() const {
}
}
+bool Local::operator==(const script::Local& other) const {
+ if (isNull()) return other.isNull();
+ return val_->StrictEquals(other.val_);
+}
+
bool Local::isObject() const { return !isNull() && val_->IsObject(); }
Local