diff --git a/.gitignore b/.gitignore index cfbc04f98..7990d97d5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,5 @@ test/nw/app.nw local.env .mason .eslintrc.js -setup.sh \ No newline at end of file +setup.sh +/build-tmp-napi-v3 diff --git a/.travis.yml b/.travis.yml index f92e44d52..3a506145b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,56 +48,6 @@ matrix: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="9" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="8" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="7" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="6" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="5" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="4" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - # test building against external sqlite - - os: linux - compiler: clang - env: NODE_VERSION="8" EXTERNAL_SQLITE=true PUBLISHABLE=false - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5','libsqlite3-dev'] # OS X - os: osx compiler: clang @@ -111,28 +61,10 @@ matrix: - os: osx compiler: clang env: NODE_VERSION="10" # node abi 64 - - os: osx - compiler: clang - env: NODE_VERSION="9" # node abi 59 - - os: osx - compiler: clang - env: NODE_VERSION="8" # node abi 57 - - os: osx - compiler: clang - env: NODE_VERSION="7" # node abi 51 - - os: osx - compiler: clang - env: NODE_VERSION="6" # node abi 48 - - os: osx - compiler: clang - env: NODE_VERSION="5" # node abi 47 - - os: osx - compiler: clang - env: NODE_VERSION="4" # node abi 46 # electron Linux - os: linux compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.2.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.2.0" dist: trusty addons: apt: @@ -140,7 +72,7 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.1.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.1.0" dist: trusty addons: apt: @@ -148,7 +80,7 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.0.0" dist: trusty addons: apt: @@ -156,7 +88,7 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="7.1.0" + env: NODE_VERSION="12" ELECTRON_VERSION="7.1.0" dist: trusty addons: apt: @@ -164,7 +96,7 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="7.0.0" + env: NODE_VERSION="12" ELECTRON_VERSION="7.0.0" dist: trusty addons: apt: @@ -172,7 +104,7 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.1.0" + env: NODE_VERSION="12" ELECTRON_VERSION="6.1.0" dist: trusty addons: apt: @@ -180,140 +112,34 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.0.0" - dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="5.0.0" - dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="4.2.0" - dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="4.1.0" - dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="4.0.0" - dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="3.0.6" + env: NODE_VERSION="12" ELECTRON_VERSION="6.0.0" dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise addons: apt: sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="2.0.1" - dist: trusty # needed for libc6 / 'version `GLIBC_2.17` not found' error on precise - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5', 'libc6'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.8.4" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.7.12" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.6.2" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] - - os: linux - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.3.14" - addons: - apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] - packages: [ 'clang-3.5'] # electron MacOs - os: osx compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.2.0" - - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.1.0" - - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" - - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="7.1.0" - - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="7.0.0" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.1.0" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.0.0" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="5.0.0" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="4.2.0" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="4.1.0" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="4.0.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.2.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="3.0.6" + env: NODE_VERSION="12" ELECTRON_VERSION="8.1.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="2.0.1" + env: NODE_VERSION="12" ELECTRON_VERSION="8.0.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.8.4" + env: NODE_VERSION="12" ELECTRON_VERSION="7.1.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.7.12" + env: NODE_VERSION="12" ELECTRON_VERSION="7.0.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.6.2" + env: NODE_VERSION="12" ELECTRON_VERSION="6.1.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.3.14" + env: NODE_VERSION="12" ELECTRON_VERSION="6.0.0" env: global: diff --git a/README.md b/README.md index 9b691f2bb..06faeb95d 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,13 @@ Asynchronous, non-blocking [SQLite3](https://sqlite.org/) bindings for [Node.js] [![Coverage Status](https://coveralls.io/repos/mapbox/node-sqlite3/badge.svg?branch=master&service=github)](https://coveralls.io/github/mapbox/node-sqlite3?branch=master) [![Dependencies](https://david-dm.org/mapbox/node-sqlite3.svg)](https://david-dm.org/mapbox/node-sqlite3) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fnode-sqlite3?ref=badge_shield) +[![N-API v3 Badge](https://img.shields.io/badge/N--API-v3-green.svg)](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api) ## Supported platforms The `sqlite3` module works with: -* Node.js v4.x, v6.x, v8.x, v10.x, v11.x, v12.x and v13.x. -* Electron v1.3.X, v1.6.x, v1.7.x, v1.8.x, v2.0.x, v3.0.x, v4.0.x, v4.1.x, v4.2.x, v5.0.x, v6.0.x, v6.1.x, v7.0.x, v7.1.x, v8.0.x, v8.1.x and v8.2.x +* Node.js v11.x, v12.x, v13.x and v14.x. +* Electron v6.0.x, v6.1.x, v7.0.x, v7.1.x, v8.0.x, v8.1.x and v8.2.x Binaries for most Node versions and platforms are provided by default via [node-pre-gyp](https://github.com/mapbox/node-pre-gyp). diff --git a/appveyor.yml b/appveyor.yml index 6ec3a5935..26d7eefb8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,31 +1,5 @@ environment: matrix: - - nodejs_version: 4 - platform: x64 - msvs_toolset: 12 - - nodejs_version: 4 - platform: x86 - msvs_toolset: 12 - - nodejs_version: 5 - platform: x64 - - nodejs_version: 5 - platform: x86 - - nodejs_version: 6 - platform: x64 - - nodejs_version: 6 - platform: x86 - - nodejs_version: 7 - platform: x64 - - nodejs_version: 7 - platform: x86 - - nodejs_version: 8 - platform: x64 - - nodejs_version: 8 - platform: x86 - - nodejs_version: 9 - platform: x64 - - nodejs_version: 9 - platform: x86 - nodejs_version: 10 platform: x64 - nodejs_version: 10 @@ -113,100 +87,6 @@ environment: NODE_RUNTIME: electron NODE_RUNTIME_VERSION: 6.0.0 TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 12 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 5.0.0 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 12 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 5.0.0 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 10 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 4.2.0 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 10 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 4.1.0 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 10 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 4.0.0 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 10 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 4.0.0 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 10 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 3.0.6 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 10 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 3.0.6 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 8 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 2.0.1 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 8 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 2.0.1 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 8 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.8.4 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 8 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.8.4 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 7 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.7.12 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 7 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.7.12 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 6 - platform: x64 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.6.2 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 6 - platform: x86 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.6.2 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 6 - platform: x64 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.3.14 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers - - nodejs_version: 6 - platform: x86 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.3.14 - TOOLSET_ARGS: --dist-url=https://electronjs.org/headers os: Visual Studio 2015 diff --git a/binding.gyp b/binding.gyp index 019df41e7..f1336f6f7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,10 +7,21 @@ "targets": [ { "target_name": "<(module_name)", - "include_dirs": ["", @@ -37,7 +40,7 @@ "url": "git://github.com/mapbox/node-sqlite3.git" }, "dependencies": { - "nan": "^2.12.1", + "node-addon-api": "2.0.0", "node-pre-gyp": "^0.11.0" }, "devDependencies": { diff --git a/src/async.h b/src/async.h index 5232c127d..9c4d8926a 100644 --- a/src/async.h +++ b/src/async.h @@ -1,8 +1,10 @@ #ifndef NODE_SQLITE3_SRC_ASYNC_H #define NODE_SQLITE3_SRC_ASYNC_H +#include +#include + #include "threading.h" -#include #if defined(NODE_SQLITE3_BOOST_THREADING) #include @@ -26,10 +28,12 @@ template class Async { : callback(cb_), parent(parent_) { watcher.data = this; NODE_SQLITE3_MUTEX_INIT - uv_async_init(uv_default_loop(), &watcher, reinterpret_cast(listener)); + uv_loop_t *loop; + napi_get_uv_event_loop(parent_->Env(), &loop); + uv_async_init(loop, &watcher, reinterpret_cast(listener)); } - static void listener(uv_async_t* handle, int status) { + static void listener(uv_async_t* handle) { Async* async = static_cast(handle->data); std::vector rows; NODE_SQLITE3_MUTEX_LOCK(&async->mutex) @@ -51,7 +55,7 @@ template class Async { // Need to call the listener again to ensure all items have been // processed. Is this a bug in uv_async? Feels like uv_close // should handle that. - listener(&watcher, 0); + listener(&watcher); uv_close((uv_handle_t*)&watcher, close); } diff --git a/src/backup.cc b/src/backup.cc index 17eafc3db..470d8f5f4 100644 --- a/src/backup.cc +++ b/src/backup.cc @@ -1,7 +1,5 @@ #include -#include -#include -#include +#include #include "macros.h" #include "database.h" @@ -9,31 +7,28 @@ using namespace node_sqlite3; -Nan::Persistent Backup::constructor_template; +Napi::FunctionReference Backup::constructor; -NAN_MODULE_INIT(Backup::Init) { - Nan::HandleScope scope; +Napi::Object Backup::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); - Local t = Nan::New(New); + Napi::Function t = DefineClass(env, "Backup", { + InstanceMethod("step", &Backup::Step), + InstanceMethod("finish", &Backup::Finish), + InstanceAccessor("idle", &Backup::IdleGetter, nullptr), + InstanceAccessor("completed", &Backup::CompletedGetter, nullptr), + InstanceAccessor("failed", &Backup::FailedGetter, nullptr), + InstanceAccessor("remaining", &Backup::RemainingGetter, nullptr), + InstanceAccessor("pageCount", &Backup::PageCountGetter, nullptr), + InstanceAccessor("retryErrors", &Backup::RetryErrorGetter, &Backup::RetryErrorSetter), + }); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Backup").ToLocalChecked()); + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); - Nan::SetPrototypeMethod(t, "step", Step); - Nan::SetPrototypeMethod(t, "finish", Finish); - - NODE_SET_GETTER(t, "idle", IdleGetter); - NODE_SET_GETTER(t, "completed", CompletedGetter); - NODE_SET_GETTER(t, "failed", FailedGetter); - NODE_SET_GETTER(t, "remaining", RemainingGetter); - NODE_SET_GETTER(t, "pageCount", PageCountGetter); - - NODE_SET_SETTER(t, "retryErrors", RetryErrorGetter, RetryErrorSetter); - - constructor_template.Reset(t); - Nan::Set(target, Nan::New("Backup").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + exports.Set("Backup", t); + return exports; } void Backup::Process() { @@ -64,33 +59,35 @@ void Backup::Schedule(Work_Callback callback, Baton* baton) { } template void Backup::Error(T* baton) { - Nan::HandleScope scope; + Napi::Env env = baton->backup->Env(); + Napi::HandleScope scope(env); Backup* backup = baton->backup; // Fail hard on logic errors. assert(backup->status != 0); - EXCEPTION(backup->message, backup->status, exception); + EXCEPTION(Napi::String::New(env, backup->message), backup->status, exception); - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(backup->handle(), cb, 1, argv); + if (!cb.IsEmpty() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(backup->Value(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(backup->handle(), 2, argv); + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(backup->Value(), 2, argv); } } void Backup::CleanQueue() { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (inited && !queue.empty()) { // This backup has already been initialized and is now finished. // Fire error for all remaining items in the queue. - EXCEPTION("Backup is already finished", SQLITE_MISUSE, exception); - Local argv[] = { exception }; + EXCEPTION(Napi::String::New(env, "Backup is already finished"), SQLITE_MISUSE, exception); + Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. @@ -98,11 +95,11 @@ void Backup::CleanQueue() { Call* call = queue.front(); queue.pop(); - Local cb = Nan::New(call->baton->callback); + Napi::Function cb = call->baton->callback.Value(); if (inited && !cb.IsEmpty() && - cb->IsFunction()) { - TRY_CATCH_CALL(handle(), cb, 1, argv); + cb.IsFunction()) { + TRY_CATCH_CALL(Value(), cb, 1, argv); called = true; } @@ -115,8 +112,8 @@ void Backup::CleanQueue() { // When we couldn't call a callback function, emit an error on the // Backup object. if (!called) { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { @@ -132,65 +129,74 @@ void Backup::CleanQueue() { } } -NAN_METHOD(Backup::New) { +Backup::Backup(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Backup objects"); + Napi::TypeError::New(env, "Use the new operator to create new Backup objects").ThrowAsJavaScriptException(); + return; } int length = info.Length(); if (length <= 0 || !Database::HasInstance(info[0])) { - return Nan::ThrowTypeError("Database object expected"); + Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); + return; } - else if (length <= 1 || !info[1]->IsString()) { - return Nan::ThrowTypeError("Filename expected"); + else if (length <= 1 || !info[1].IsString()) { + Napi::TypeError::New(env, "Filename expected").ThrowAsJavaScriptException(); + return; } - else if (length <= 2 || !info[2]->IsString()) { - return Nan::ThrowTypeError("Source database name expected"); + else if (length <= 2 || !info[2].IsString()) { + Napi::TypeError::New(env, "Source database name expected").ThrowAsJavaScriptException(); + return; } - else if (length <= 3 || !info[3]->IsString()) { - return Nan::ThrowTypeError("Destination database name expected"); + else if (length <= 3 || !info[3].IsString()) { + Napi::TypeError::New(env, "Destination database name expected").ThrowAsJavaScriptException(); + return; } - else if (length <= 4 || !info[4]->IsBoolean()) { - return Nan::ThrowTypeError("Direction flag expected"); + else if (length <= 4 || !info[4].IsBoolean()) { + Napi::TypeError::New(env, "Direction flag expected").ThrowAsJavaScriptException(); + return; } - else if (length > 5 && !info[5]->IsUndefined() && !info[5]->IsFunction()) { - return Nan::ThrowTypeError("Callback expected"); + else if (length > 5 && !info[5].IsUndefined() && !info[5].IsFunction()) { + Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); + return; } - Database* db = Nan::ObjectWrap::Unwrap(info[0].As()); - Local filename = Local::Cast(info[1]); - Local sourceName = Local::Cast(info[2]); - Local destName = Local::Cast(info[3]); - Local filenameIsDest = Local::Cast(info[4]); - - Nan::ForceSet(info.This(), Nan::New("filename").ToLocalChecked(), filename, ReadOnly); - Nan::ForceSet(info.This(), Nan::New("sourceName").ToLocalChecked(), sourceName, ReadOnly); - Nan::ForceSet(info.This(), Nan::New("destName").ToLocalChecked(), destName, ReadOnly); - Nan::ForceSet(info.This(), Nan::New("filenameIsDest").ToLocalChecked(), filenameIsDest, ReadOnly); - - Backup* backup = new Backup(db); - backup->Wrap(info.This()); - - InitializeBaton* baton = new InitializeBaton(db, Local::Cast(info[5]), backup); - baton->filename = std::string(*Nan::Utf8String(filename)); - baton->sourceName = std::string(*Nan::Utf8String(sourceName)); - baton->destName = std::string(*Nan::Utf8String(destName)); - baton->filenameIsDest = Nan::To(filenameIsDest).FromJust(); - db->Schedule(Work_BeginInitialize, baton); + Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); + Napi::String filename = info[1].As(); + Napi::String sourceName = info[2].As(); + Napi::String destName = info[3].As(); + Napi::Boolean filenameIsDest = info[4].As(); - info.GetReturnValue().Set(info.This()); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filename", filename)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("sourceName", sourceName)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("destName", destName)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filenameIsDest", filenameIsDest)); + + init(db); + + InitializeBaton* baton = new InitializeBaton(db, info[5].As(), this); + baton->filename = filename.Utf8Value(); + baton->sourceName = sourceName.Utf8Value(); + baton->destName = destName.Utf8Value(); + baton->filenameIsDest = filenameIsDest.Value(); + db->Schedule(Work_BeginInitialize, baton); } void Backup::Work_BeginInitialize(Database::Baton* baton) { assert(baton->db->open); baton->db->pending++; - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Initialize, (uv_after_work_cb)Work_AfterInitialize); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Backup.Initialize"), + Work_Initialize, Work_AfterInitialize, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Backup::Work_Initialize(uv_work_t* req) { +void Backup::Work_Initialize(napi_env e, void* data) { BACKUP_INIT(InitializeBaton); // In case stepping fails, we use a mutex to make sure we get the associated @@ -219,28 +225,30 @@ void Backup::Work_Initialize(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Backup::Work_AfterInitialize(uv_work_t* req) { - Nan::HandleScope scope; - +void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) { BACKUP_INIT(InitializeBaton); + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + if (backup->status != SQLITE_OK) { Error(baton); backup->FinishAll(); } else { backup->inited = true; - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(backup->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(backup->Value(), cb, 1, argv); } } BACKUP_END(); } -NAN_METHOD(Backup::Step) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Backup::Step(const Napi::CallbackInfo& info) { + Backup* backup = this; + Napi::Env env = backup->Env(); REQUIRE_ARGUMENT_INTEGER(0, pages); OPTIONAL_ARGUMENT_FUNCTION(1, callback); @@ -248,14 +256,14 @@ NAN_METHOD(Backup::Step) { StepBaton* baton = new StepBaton(backup, callback, pages); backup->GetRetryErrors(baton->retryErrorsSet); backup->Schedule(Work_BeginStep, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Backup::Work_BeginStep(Baton* baton) { BACKUP_BEGIN(Step); } -void Backup::Work_Step(uv_work_t* req) { +void Backup::Work_Step(napi_env e, void* data) { BACKUP_INIT(StepBaton); if (backup->_handle) { backup->status = sqlite3_backup_step(backup->_handle, baton->pages); @@ -279,11 +287,12 @@ void Backup::Work_Step(uv_work_t* req) { } } -void Backup::Work_AfterStep(uv_work_t* req) { - Nan::HandleScope scope; - +void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) { BACKUP_INIT(StepBaton); + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + if (backup->status == SQLITE_DONE) { backup->completed = true; } else if (!backup->_handle) { @@ -295,45 +304,48 @@ void Backup::Work_AfterStep(uv_work_t* req) { } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null(), Nan::New(backup->status == SQLITE_DONE) }; - TRY_CATCH_CALL(backup->handle(), cb, 2, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null(), Napi::Boolean::New(env, backup->status == SQLITE_DONE) }; + TRY_CATCH_CALL(backup->Value(), cb, 2, argv); } } BACKUP_END(); } -NAN_METHOD(Backup::Finish) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Backup::Finish(const Napi::CallbackInfo& info) { + Backup* backup = this; + Napi::Env env = backup->Env(); OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(backup, callback); backup->Schedule(Work_BeginFinish, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Backup::Work_BeginFinish(Baton* baton) { BACKUP_BEGIN(Finish); } -void Backup::Work_Finish(uv_work_t* req) { +void Backup::Work_Finish(napi_env e, void* data) { BACKUP_INIT(Baton); backup->FinishSqlite(); } -void Backup::Work_AfterFinish(uv_work_t* req) { - Nan::HandleScope scope; - +void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) { BACKUP_INIT(Baton); + + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + backup->FinishAll(); // Fire callback in case there was one. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(backup->handle(), cb, 0, NULL); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { + TRY_CATCH_CALL(backup->Value(), cb, 0, NULL); } BACKUP_END(); @@ -362,55 +374,56 @@ void Backup::FinishSqlite() { _destDb = NULL; } -NAN_GETTER(Backup::IdleGetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Backup::IdleGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; bool idle = backup->inited && !backup->locked && backup->queue.empty(); - info.GetReturnValue().Set(idle); + return Napi::Boolean::New(this->Env(), idle); } -NAN_GETTER(Backup::CompletedGetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(backup->completed); +Napi::Value Backup::CompletedGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Boolean::New(this->Env(), backup->completed); } -NAN_GETTER(Backup::FailedGetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(backup->failed); +Napi::Value Backup::FailedGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Boolean::New(this->Env(), backup->failed); } -NAN_GETTER(Backup::RemainingGetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(backup->remaining); +Napi::Value Backup::RemainingGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Number::New(this->Env(), backup->remaining); } -NAN_GETTER(Backup::PageCountGetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(backup->pageCount); +Napi::Value Backup::PageCountGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return Napi::Number::New(this->Env(), backup->pageCount); } -NAN_GETTER(Backup::RetryErrorGetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(Nan::New(backup->retryErrors)); +Napi::Value Backup::RetryErrorGetter(const Napi::CallbackInfo& info) { + Backup* backup = this; + return backup->retryErrors.Value(); } -NAN_SETTER(Backup::RetryErrorSetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); - if (!value->IsArray()) { - return Nan::ThrowError("retryErrors must be an array"); +void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value) { + Backup* backup = this; + Napi::Env env = backup->Env(); + if (!value.IsArray()) { + Napi::Error::New(env, "retryErrors must be an array").ThrowAsJavaScriptException(); + return; } - Local array = Local::Cast(value); - backup->retryErrors.Reset(array); + Napi::Array array = value.As(); + backup->retryErrors.Reset(array, 1); } void Backup::GetRetryErrors(std::set& retryErrorsSet) { retryErrorsSet.clear(); - Local array = Nan::New(retryErrors); - int length = array->Length(); + Napi::Array array = retryErrors.Value(); + int length = array.Length(); for (int i = 0; i < length; i++) { - Local code = Nan::Get(array, i).ToLocalChecked(); - if (code->IsInt32()) { - retryErrorsSet.insert(Nan::To(code).FromJust()); + Napi::Value code = (array).Get(i); + if (code.IsNumber()) { + retryErrorsSet.insert(code.As().Int32Value()); } } } - diff --git a/src/backup.h b/src/backup.h index 723f8c94c..95edc1ab6 100644 --- a/src/backup.h +++ b/src/backup.h @@ -8,10 +8,9 @@ #include #include -#include +#include -using namespace v8; -using namespace node; +using namespace Napi; namespace node_sqlite3 { @@ -92,22 +91,20 @@ namespace node_sqlite3 { * backup.finish(); * */ -class Backup : public Nan::ObjectWrap { +class Backup : public Napi::ObjectWrap { public: - static Nan::Persistent constructor_template; + static Napi::FunctionReference constructor; - static NAN_MODULE_INIT(Init); - static NAN_METHOD(New); + static Napi::Object Init(Napi::Env env, Napi::Object exports); struct Baton { - uv_work_t request; + napi_async_work request; Backup* backup; - Nan::Persistent callback; + Napi::FunctionReference callback; - Baton(Backup* backup_, Local cb_) : backup(backup_) { + Baton(Backup* backup_, Napi::Function cb_) : backup(backup_) { backup->Ref(); - request.data = this; - callback.Reset(cb_); + callback.Reset(cb_, 1); } virtual ~Baton() { backup->Unref(); @@ -121,7 +118,7 @@ class Backup : public Nan::ObjectWrap { std::string sourceName; std::string destName; bool filenameIsDest; - InitializeBaton(Database* db_, Local cb_, Backup* backup_) : + InitializeBaton(Database* db_, Napi::Function cb_, Backup* backup_) : Baton(db_, cb_), backup(backup_), filenameIsDest(true) { backup->Ref(); } @@ -137,7 +134,7 @@ class Backup : public Nan::ObjectWrap { struct StepBaton : Baton { int pages; std::set retryErrorsSet; - StepBaton(Backup* backup_, Local cb_, int pages_) : + StepBaton(Backup* backup_, Napi::Function cb_, int pages_) : Baton(backup_, cb_), pages(pages_) {} }; @@ -149,21 +146,23 @@ class Backup : public Nan::ObjectWrap { Baton* baton; }; - Backup(Database* db_) : Nan::ObjectWrap(), - db(db_), - _handle(NULL), - _otherDb(NULL), - _destDb(NULL), - inited(false), - locked(true), - completed(false), - failed(false), - remaining(-1), - pageCount(-1), - finished(false) { + void init(Database* db_) { + db = db_; + _handle = NULL; + _otherDb = NULL; + _destDb = NULL; + inited = false; + locked = true; + completed = false; + failed = false; + remaining = -1; + pageCount = -1; + finished = false; db->Ref(); } + Backup(const Napi::CallbackInfo& info); + ~Backup() { if (!finished) { FinishAll(); @@ -173,21 +172,21 @@ class Backup : public Nan::ObjectWrap { WORK_DEFINITION(Step); WORK_DEFINITION(Finish); - static NAN_GETTER(IdleGetter); - static NAN_GETTER(CompletedGetter); - static NAN_GETTER(FailedGetter); - static NAN_GETTER(PageCountGetter); - static NAN_GETTER(RemainingGetter); - static NAN_GETTER(FatalErrorGetter); - static NAN_GETTER(RetryErrorGetter); + Napi::Value IdleGetter(const Napi::CallbackInfo& info); + Napi::Value CompletedGetter(const Napi::CallbackInfo& info); + Napi::Value FailedGetter(const Napi::CallbackInfo& info); + Napi::Value PageCountGetter(const Napi::CallbackInfo& info); + Napi::Value RemainingGetter(const Napi::CallbackInfo& info); + Napi::Value FatalErrorGetter(const Napi::CallbackInfo& info); + Napi::Value RetryErrorGetter(const Napi::CallbackInfo& info); - static NAN_SETTER(FatalErrorSetter); - static NAN_SETTER(RetryErrorSetter); + void FatalErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); + void RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value); protected: static void Work_BeginInitialize(Database::Baton* baton); - static void Work_Initialize(uv_work_t* req); - static void Work_AfterInitialize(uv_work_t* req); + static void Work_Initialize(napi_env env, void* data); + static void Work_AfterInitialize(napi_env env, napi_status status, void* data); void Schedule(Work_Callback callback, Baton* baton); void Process(); @@ -215,7 +214,7 @@ class Backup : public Nan::ObjectWrap { bool finished; std::queue queue; - Nan::Persistent retryErrors; + Napi::Reference retryErrors; }; } diff --git a/src/database.cc b/src/database.cc index ace5fa0b0..7b33193ae 100644 --- a/src/database.cc +++ b/src/database.cc @@ -6,47 +6,45 @@ using namespace node_sqlite3; -Nan::Persistent Database::constructor_template; +Napi::FunctionReference Database::constructor; -NAN_MODULE_INIT(Database::Init) { - Nan::HandleScope scope; +Napi::Object Database::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); - Local t = Nan::New(New); + Napi::Function t = DefineClass(env, "Database", { + InstanceMethod("close", &Database::Close), + InstanceMethod("exec", &Database::Exec), + InstanceMethod("wait", &Database::Wait), + InstanceMethod("loadExtension", &Database::LoadExtension), + InstanceMethod("serialize", &Database::Serialize), + InstanceMethod("parallelize", &Database::Parallelize), + InstanceMethod("configure", &Database::Configure), + InstanceMethod("interrupt", &Database::Interrupt), + InstanceAccessor("open", &Database::OpenGetter, nullptr) + }); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Database").ToLocalChecked()); + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); - Nan::SetPrototypeMethod(t, "close", Close); - Nan::SetPrototypeMethod(t, "exec", Exec); - Nan::SetPrototypeMethod(t, "wait", Wait); - Nan::SetPrototypeMethod(t, "loadExtension", LoadExtension); - Nan::SetPrototypeMethod(t, "serialize", Serialize); - Nan::SetPrototypeMethod(t, "parallelize", Parallelize); - Nan::SetPrototypeMethod(t, "configure", Configure); - Nan::SetPrototypeMethod(t, "interrupt", Interrupt); - - NODE_SET_GETTER(t, "open", OpenGetter); - - constructor_template.Reset(t); - - Nan::Set(target, Nan::New("Database").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + exports.Set("Database", t); + return exports; } void Database::Process() { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (!open && locked && !queue.empty()) { - EXCEPTION("Database handle is closed", SQLITE_MISUSE, exception); - Local argv[] = { exception }; + EXCEPTION(Napi::String::New(env, "Database handle is closed"), SQLITE_MISUSE, exception); + Napi::Value argv[] = { exception }; bool called = false; // Call all callbacks with the error object. while (!queue.empty()) { Call* call = queue.front(); - Local cb = Nan::New(call->baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(this->handle(), cb, 1, argv); + Napi::Function cb = call->baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(this->Value(), cb, 1, argv); called = true; } queue.pop(); @@ -59,8 +57,8 @@ void Database::Process() { // When we couldn't call a callback function, emit an error on the // Database object. if (!called) { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, info); } return; } @@ -82,18 +80,19 @@ void Database::Process() { } void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (!open && locked) { - EXCEPTION("Database is closed", SQLITE_MISUSE, exception); - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(handle(), cb, 1, argv); + EXCEPTION(Napi::String::New(env, "Database is closed"), SQLITE_MISUSE, exception); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(Value(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, argv); + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, argv); } return; } @@ -107,47 +106,51 @@ void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { } } -NAN_METHOD(Database::New) { - if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Database objects"); +Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + init(); + Napi::Env env = info.Env(); + + if (info.Length() <= 0 || !info[0].IsString()) { + Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException(); + return; } + std::string filename = info[0].As(); - REQUIRE_ARGUMENT_STRING(0, filename); - int pos = 1; + unsigned int pos = 1; int mode; - if (info.Length() >= pos && info[pos]->IsInt32()) { - mode = Nan::To(info[pos++]).FromJust(); - } else { + if (info.Length() >= pos && info[pos].IsNumber() && OtherIsInt(info[pos].As())) { + mode = info[pos++].As().Int32Value(); + } + else { mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; } - Local callback; - if (info.Length() >= pos && info[pos]->IsFunction()) { - callback = Local::Cast(info[pos++]); + Napi::Function callback; + if (info.Length() >= pos && info[pos].IsFunction()) { + callback = info[pos++].As(); } - Database* db = new Database(); - db->Wrap(info.This()); - - Nan::ForceSet(info.This(), Nan::New("filename").ToLocalChecked(), info[0].As(), ReadOnly); - Nan::ForceSet(info.This(), Nan::New("mode").ToLocalChecked(), Nan::New(mode), ReadOnly); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("filename", info[0].As(), napi_default)); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("mode", Napi::Number::New(env, mode), napi_default)); // Start opening the database. - OpenBaton* baton = new OpenBaton(db, callback, *filename, mode); + OpenBaton* baton = new OpenBaton(this, callback, filename.c_str(), mode); Work_BeginOpen(baton); - - info.GetReturnValue().Set(info.This()); } void Database::Work_BeginOpen(Baton* baton) { - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Open, (uv_after_work_cb)Work_AfterOpen); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.Open"), + Work_Open, Work_AfterOpen, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_Open(uv_work_t* req) { - OpenBaton* baton = static_cast(req->data); +void Database::Work_Open(napi_env e, void* data) { + OpenBaton* baton = static_cast(data); Database* db = baton->db; baton->status = sqlite3_open_v2( @@ -168,54 +171,59 @@ void Database::Work_Open(uv_work_t* req) { } } -void Database::Work_AfterOpen(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) { + OpenBaton* baton = static_cast(data); - OpenBaton* baton = static_cast(req->data); Database* db = baton->db; - Local argv[1]; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + Napi::Value argv[1]; if (baton->status != SQLITE_OK) { - EXCEPTION(baton->message, baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); argv[0] = exception; } else { db->open = true; - argv[0] = Nan::Null(); + argv[0] = env.Null(); } - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else if (!db->open) { - Local info[] = { Nan::New("error").ToLocalChecked(), argv[0] }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; + EMIT_EVENT(db->Value(), 2, info); } if (db->open) { - Local info[] = { Nan::New("open").ToLocalChecked() }; - EMIT_EVENT(db->handle(), 1, info); + Napi::Value info[] = { Napi::String::New(env, "open") }; + EMIT_EVENT(db->Value(), 1, info); db->Process(); } + napi_delete_async_work(e, baton->request); delete baton; } -NAN_GETTER(Database::OpenGetter) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); - info.GetReturnValue().Set(db->open); +Napi::Value Database::OpenGetter(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; + return Napi::Boolean::New(env, db->open); } -NAN_METHOD(Database::Close) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Close(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(db, callback); db->Schedule(Work_BeginClose, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginClose(Baton* baton) { @@ -227,13 +235,18 @@ void Database::Work_BeginClose(Baton* baton) { baton->db->RemoveCallbacks(); baton->db->closing = true; - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Close, (uv_after_work_cb)Work_AfterClose); + Napi::Env env = baton->db->Env(); + + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.Close"), + Work_Close, Work_AfterClose, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_Close(uv_work_t* req) { - Baton* baton = static_cast(req->data); +void Database::Work_Close(napi_env e, void* data) { + Baton* baton = static_cast(data); Database* db = baton->db; baton->status = sqlite3_close(db->_handle); @@ -246,132 +259,143 @@ void Database::Work_Close(uv_work_t* req) { } } -void Database::Work_AfterClose(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterClose(napi_env e, napi_status status, void* data) { + Baton* baton = static_cast(data); - Baton* baton = static_cast(req->data); Database* db = baton->db; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + db->closing = false; - Local argv[1]; + Napi::Value argv[1]; if (baton->status != SQLITE_OK) { - EXCEPTION(baton->message, baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); argv[0] = exception; } else { db->open = false; // Leave db->locked to indicate that this db object has reached // the end of its life. - argv[0] = Nan::Null(); + argv[0] = env.Null(); } - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); // Fire callbacks. - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else if (db->open) { - Local info[] = { Nan::New("error").ToLocalChecked(), argv[0] }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; + EMIT_EVENT(db->Value(), 2, info); } if (!db->open) { - Local info[] = { Nan::New("close").ToLocalChecked(), argv[0] }; - EMIT_EVENT(db->handle(), 1, info); + Napi::Value info[] = { Napi::String::New(env, "close"), argv[0] }; + EMIT_EVENT(db->Value(), 1, info); db->Process(); } + napi_delete_async_work(e, baton->request); delete baton; } -NAN_METHOD(Database::Serialize) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = true; - if (!callback.IsEmpty() && callback->IsFunction()) { + if (!callback.IsEmpty() && callback.IsFunction()) { TRY_CATCH_CALL(info.This(), callback, 0, NULL); db->serialize = before; } db->Process(); - info.GetReturnValue().Set(info.This()); + return info.This(); } -NAN_METHOD(Database::Parallelize) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = false; - if (!callback.IsEmpty() && callback->IsFunction()) { + if (!callback.IsEmpty() && callback.IsFunction()) { TRY_CATCH_CALL(info.This(), callback, 0, NULL); db->serialize = before; } db->Process(); - info.GetReturnValue().Set(info.This()); + return info.This(); } -NAN_METHOD(Database::Configure) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Configure(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; REQUIRE_ARGUMENTS(2); - if (Nan::Equals(info[0], Nan::New("trace").ToLocalChecked()).FromJust()) { - Local handle; + if (info[0].StrictEquals( Napi::String::New(env, "trace"))) { + Napi::Function handle; Baton* baton = new Baton(db, handle); db->Schedule(RegisterTraceCallback, baton); } - else if (Nan::Equals(info[0], Nan::New("profile").ToLocalChecked()).FromJust()) { - Local handle; + else if (info[0].StrictEquals( Napi::String::New(env, "profile"))) { + Napi::Function handle; Baton* baton = new Baton(db, handle); db->Schedule(RegisterProfileCallback, baton); } - else if (Nan::Equals(info[0], Nan::New("busyTimeout").ToLocalChecked()).FromJust()) { - if (!info[1]->IsInt32()) { - return Nan::ThrowTypeError("Value must be an integer"); + else if (info[0].StrictEquals( Napi::String::New(env, "busyTimeout"))) { + if (!info[1].IsNumber()) { + Napi::TypeError::New(env, "Value must be an integer").ThrowAsJavaScriptException(); + return env.Null(); } - Local handle; + Napi::Function handle; Baton* baton = new Baton(db, handle); - baton->status = Nan::To(info[1]).FromJust(); + baton->status = info[1].As().Int32Value(); db->Schedule(SetBusyTimeout, baton); } else { - return Nan::ThrowError(Exception::Error(String::Concat( + Napi::TypeError::New(env, (StringConcat( #if V8_MAJOR_VERSION > 6 info.GetIsolate(), #endif - Nan::To(info[0]).ToLocalChecked(), - Nan::New(" is not a valid configuration option").ToLocalChecked() - ))); + info[0].As(), + Napi::String::New(env, " is not a valid configuration option") + )).Utf8Value().c_str()).ThrowAsJavaScriptException(); + return env.Null(); } db->Process(); - info.GetReturnValue().Set(info.This()); + return info.This(); } -NAN_METHOD(Database::Interrupt) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; if (!db->open) { - return Nan::ThrowError("Database is not open"); + Napi::Error::New(env, "Database is not open").ThrowAsJavaScriptException(); + return env.Null(); } if (db->closing) { - return Nan::ThrowError("Database is closing"); + Napi::Error::New(env, "Database is closing").ThrowAsJavaScriptException(); + return env.Null(); } sqlite3_interrupt(db->_handle); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::SetBusyTimeout(Baton* baton) { @@ -412,13 +436,14 @@ void Database::TraceCallback(void* db, const char* sql) { void Database::TraceCallback(Database* db, std::string* sql) { // Note: This function is called in the main V8 thread. - Nan::HandleScope scope; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); - Local argv[] = { - Nan::New("trace").ToLocalChecked(), - Nan::New(sql->c_str()).ToLocalChecked() + Napi::Value argv[] = { + Napi::String::New(env, "trace"), + Napi::String::New(env, sql->c_str()) }; - EMIT_EVENT(db->handle(), 2, argv); + EMIT_EVENT(db->Value(), 2, argv); delete sql; } @@ -452,14 +477,15 @@ void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) } void Database::ProfileCallback(Database *db, ProfileInfo* info) { - Nan::HandleScope scope; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); - Local argv[] = { - Nan::New("profile").ToLocalChecked(), - Nan::New(info->sql.c_str()).ToLocalChecked(), - Nan::New((double)info->nsecs / 1000000.0) + Napi::Value argv[] = { + Napi::String::New(env, "profile"), + Napi::String::New(env, info->sql.c_str()), + Napi::Number::New(env, (double)info->nsecs / 1000000.0) }; - EMIT_EVENT(db->handle(), 3, argv); + EMIT_EVENT(db->Value(), 3, argv); delete info; } @@ -496,28 +522,30 @@ void Database::UpdateCallback(void* db, int type, const char* database, } void Database::UpdateCallback(Database *db, UpdateInfo* info) { - Nan::HandleScope scope; - - Local argv[] = { - Nan::New(sqlite_authorizer_string(info->type)).ToLocalChecked(), - Nan::New(info->database.c_str()).ToLocalChecked(), - Nan::New(info->table.c_str()).ToLocalChecked(), - Nan::New(info->rowid), + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + Napi::Value argv[] = { + Napi::String::New(env, sqlite_authorizer_string(info->type)), + Napi::String::New(env, info->database.c_str()), + Napi::String::New(env, info->table.c_str()), + Napi::Number::New(env, info->rowid), }; - EMIT_EVENT(db->handle(), 4, argv); + EMIT_EVENT(db->Value(), 4, argv); delete info; } -NAN_METHOD(Database::Exec) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Exec(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; REQUIRE_ARGUMENT_STRING(0, sql); OPTIONAL_ARGUMENT_FUNCTION(1, callback); - Baton* baton = new ExecBaton(db, callback, *sql); + Baton* baton = new ExecBaton(db, callback, sql.c_str()); db->Schedule(Work_BeginExec, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginExec(Baton* baton) { @@ -525,13 +553,17 @@ void Database::Work_BeginExec(Baton* baton) { assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Exec, (uv_after_work_cb)Work_AfterExec); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.Exec"), + Work_Exec, Work_AfterExec, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_Exec(uv_work_t* req) { - ExecBaton* baton = static_cast(req->data); +void Database::Work_Exec(napi_env e, void* data) { + ExecBaton* baton = static_cast(data); char* message = NULL; baton->status = sqlite3_exec( @@ -548,59 +580,64 @@ void Database::Work_Exec(uv_work_t* req) { } } -void Database::Work_AfterExec(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterExec(napi_env e, napi_status status, void* data) { + ExecBaton* baton = static_cast(data); - ExecBaton* baton = static_cast(req->data); Database* db = baton->db; - Local cb = Nan::New(baton->callback); + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + Napi::Function cb = baton->callback.Value(); if (baton->status != SQLITE_OK) { - EXCEPTION(baton->message, baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(db->Value(), 2, info); } } - else if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + else if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); + napi_delete_async_work(e, baton->request); delete baton; } -NAN_METHOD(Database::Wait) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Wait(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(db, callback); db->Schedule(Work_Wait, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_Wait(Baton* baton) { - Nan::HandleScope scope; + Napi::Env env = baton->db->Env(); + Napi::HandleScope scope(env); assert(baton->db->locked); assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(baton->db->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(baton->db->Value(), cb, 1, argv); } baton->db->Process(); @@ -608,16 +645,17 @@ void Database::Work_Wait(Baton* baton) { delete baton; } -NAN_METHOD(Database::LoadExtension) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::LoadExtension(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); + Database* db = this; REQUIRE_ARGUMENT_STRING(0, filename); OPTIONAL_ARGUMENT_FUNCTION(1, callback); - Baton* baton = new LoadExtensionBaton(db, callback, *filename); + Baton* baton = new LoadExtensionBaton(db, callback, filename.c_str()); db->Schedule(Work_BeginLoadExtension, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginLoadExtension(Baton* baton) { @@ -625,13 +663,17 @@ void Database::Work_BeginLoadExtension(Baton* baton) { assert(baton->db->open); assert(baton->db->_handle); assert(baton->db->pending == 0); - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_LoadExtension, reinterpret_cast(Work_AfterLoadExtension)); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Database.LoadExtension"), + Work_LoadExtension, Work_AfterLoadExtension, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Database::Work_LoadExtension(uv_work_t* req) { - LoadExtensionBaton* baton = static_cast(req->data); +void Database::Work_LoadExtension(napi_env e, void* data) { + LoadExtensionBaton* baton = static_cast(data); sqlite3_enable_load_extension(baton->db->_handle, 1); @@ -651,32 +693,36 @@ void Database::Work_LoadExtension(uv_work_t* req) { } } -void Database::Work_AfterLoadExtension(uv_work_t* req) { - Nan::HandleScope scope; +void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) { + LoadExtensionBaton* baton = static_cast(data); - LoadExtensionBaton* baton = static_cast(req->data); Database* db = baton->db; - Local cb = Nan::New(baton->callback); + + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + + Napi::Function cb = baton->callback.Value(); if (baton->status != SQLITE_OK) { - EXCEPTION(baton->message, baton->status, exception); + EXCEPTION(Napi::String::New(env, baton->message.c_str()), baton->status, exception); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(db->handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(db->Value(), 2, info); } } - else if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + else if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); + napi_delete_async_work(e, baton->request); delete baton; } diff --git a/src/database.h b/src/database.h index 1ec101548..b31f68136 100644 --- a/src/database.h +++ b/src/database.h @@ -3,45 +3,48 @@ #define NODE_SQLITE3_SRC_DATABASE_H +#include #include #include #include -#include +#include #include "async.h" -using namespace v8; +using namespace Napi; namespace node_sqlite3 { class Database; -class Database : public Nan::ObjectWrap { +class Database : public Napi::ObjectWrap { public: - static Nan::Persistent constructor_template; - static NAN_MODULE_INIT(Init); - - static inline bool HasInstance(Local val) { - Nan::HandleScope scope; - if (!val->IsObject()) return false; - Local obj = val.As(); - return Nan::New(constructor_template)->HasInstance(obj); + static Napi::FunctionReference constructor; + static Napi::Object Init(Napi::Env env, Napi::Object exports); + + static inline bool HasInstance(Napi::Value val) { + Napi::Env env = val.Env(); + Napi::HandleScope scope(env); + if (!val.IsObject()) return false; + Napi::Object obj = val.As(); + return obj.InstanceOf(constructor.Value()); } struct Baton { - uv_work_t request; + napi_async_work request; Database* db; - Nan::Persistent callback; + Napi::FunctionReference callback; int status; std::string message; - Baton(Database* db_, Local cb_) : + Baton(Database* db_, Napi::Function cb_) : db(db_), status(SQLITE_OK) { db->Ref(); - request.data = this; - callback.Reset(cb_); + if (!cb_.IsUndefined() && cb_.IsFunction()) { + callback.Reset(cb_, 1); + } } virtual ~Baton() { db->Unref(); @@ -52,19 +55,19 @@ class Database : public Nan::ObjectWrap { struct OpenBaton : Baton { std::string filename; int mode; - OpenBaton(Database* db_, Local cb_, const char* filename_, int mode_) : + OpenBaton(Database* db_, Napi::Function cb_, const char* filename_, int mode_) : Baton(db_, cb_), filename(filename_), mode(mode_) {} }; struct ExecBaton : Baton { std::string sql; - ExecBaton(Database* db_, Local cb_, const char* sql_) : + ExecBaton(Database* db_, Napi::Function cb_, const char* sql_) : Baton(db_, cb_), sql(sql_) {} }; struct LoadExtensionBaton : Baton { std::string filename; - LoadExtensionBaton(Database* db_, Local cb_, const char* filename_) : + LoadExtensionBaton(Database* db_, Napi::Function cb_, const char* filename_) : Baton(db_, cb_), filename(filename_) {} }; @@ -100,19 +103,20 @@ class Database : public Nan::ObjectWrap { friend class Statement; friend class Backup; -protected: - Database() : Nan::ObjectWrap(), - _handle(NULL), - open(false), - closing(false), - locked(false), - pending(0), - serialize(false), - debug_trace(NULL), - debug_profile(NULL), - update_event(NULL) { + void init() { + _handle = NULL; + open = false; + closing = false; + locked = false; + pending = 0; + serialize = false; + debug_trace = NULL; + debug_profile = NULL; + update_event = NULL; } + Database(const Napi::CallbackInfo& info); + ~Database() { RemoveCallbacks(); sqlite3_close(_handle); @@ -120,40 +124,40 @@ class Database : public Nan::ObjectWrap { open = false; } - static NAN_METHOD(New); +protected: static void Work_BeginOpen(Baton* baton); - static void Work_Open(uv_work_t* req); - static void Work_AfterOpen(uv_work_t* req); + static void Work_Open(napi_env env, void* data); + static void Work_AfterOpen(napi_env env, napi_status status, void* data); - static NAN_GETTER(OpenGetter); + Napi::Value OpenGetter(const Napi::CallbackInfo& info); void Schedule(Work_Callback callback, Baton* baton, bool exclusive = false); void Process(); - static NAN_METHOD(Exec); + Napi::Value Exec(const Napi::CallbackInfo& info); static void Work_BeginExec(Baton* baton); - static void Work_Exec(uv_work_t* req); - static void Work_AfterExec(uv_work_t* req); + static void Work_Exec(napi_env env, void* data); + static void Work_AfterExec(napi_env env, napi_status status, void* data); - static NAN_METHOD(Wait); + Napi::Value Wait(const Napi::CallbackInfo& info); static void Work_Wait(Baton* baton); - static NAN_METHOD(Close); + Napi::Value Close(const Napi::CallbackInfo& info); static void Work_BeginClose(Baton* baton); - static void Work_Close(uv_work_t* req); - static void Work_AfterClose(uv_work_t* req); + static void Work_Close(napi_env env, void* data); + static void Work_AfterClose(napi_env env, napi_status status, void* data); - static NAN_METHOD(LoadExtension); + Napi::Value LoadExtension(const Napi::CallbackInfo& info); static void Work_BeginLoadExtension(Baton* baton); - static void Work_LoadExtension(uv_work_t* req); - static void Work_AfterLoadExtension(uv_work_t* req); + static void Work_LoadExtension(napi_env env, void* data); + static void Work_AfterLoadExtension(napi_env env, napi_status status, void* data); - static NAN_METHOD(Serialize); - static NAN_METHOD(Parallelize); + Napi::Value Serialize(const Napi::CallbackInfo& info); + Napi::Value Parallelize(const Napi::CallbackInfo& info); - static NAN_METHOD(Configure); + Napi::Value Configure(const Napi::CallbackInfo& info); - static NAN_METHOD(Interrupt); + Napi::Value Interrupt(const Napi::CallbackInfo& info); static void SetBusyTimeout(Baton* baton); diff --git a/src/macros.h b/src/macros.h index 9c0136cc5..ce9448403 100644 --- a/src/macros.h +++ b/src/macros.h @@ -3,47 +3,71 @@ const char* sqlite_code_string(int code); const char* sqlite_authorizer_string(int type); - +#include + +// TODO: better way to work around StringConcat? +#include +inline Napi::String StringConcat(Napi::Value str1, Napi::Value str2) { + return Napi::String::New(str1.Env(), str1.As().Utf8Value() + + str2.As().Utf8Value() ); +} + +// A Napi substitute IsInt32() +inline bool OtherIsInt(Napi::Number source) { + double orig_val = source.DoubleValue(); + double int_val = (double)source.Int32Value(); + if (orig_val == int_val) { + return true; + } else { + return false; + } +} #define REQUIRE_ARGUMENTS(n) \ if (info.Length() < (n)) { \ - return Nan::ThrowTypeError("Expected " #n "arguments"); \ + Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ + return env.Null(); \ } #define REQUIRE_ARGUMENT_EXTERNAL(i, var) \ - if (info.Length() <= (i) || !info[i]->IsExternal()) { \ - return Nan::ThrowTypeError("Argument " #i " invalid"); \ + if (info.Length() <= (i) || !info[i].IsExternal()) { \ + Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - Local var = Local::Cast(info[i]); + Napi::External var = info[i].As(); #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ - if (info.Length() <= (i) || !info[i]->IsFunction()) { \ - return Nan::ThrowTypeError("Argument " #i " must be a function"); \ + if (info.Length() <= (i) || !info[i].IsFunction()) { \ + Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - Local var = Local::Cast(info[i]); + Napi::Function var = info[i].As(); #define REQUIRE_ARGUMENT_STRING(i, var) \ - if (info.Length() <= (i) || !info[i]->IsString()) { \ - return Nan::ThrowTypeError("Argument " #i " must be a string"); \ + if (info.Length() <= (i) || !info[i].IsString()) { \ + Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - Nan::Utf8String var(info[i]); + std::string var = info[i].As(); #define REQUIRE_ARGUMENT_INTEGER(i, var) \ - if (info.Length() <= (i) || !info[i]->IsInt32()) { \ - return Nan::ThrowTypeError("Argument " #i " must be an integer"); \ + if (info.Length() <= (i) || !info[i].IsNumber()) { \ + Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - int var(Nan::To(info[i]).FromJust()); + int var(info[i].As().Int32Value()); #define OPTIONAL_ARGUMENT_FUNCTION(i, var) \ - Local var; \ - if (info.Length() > i && !info[i]->IsUndefined()) { \ - if (!info[i]->IsFunction()) { \ - return Nan::ThrowTypeError("Argument " #i " must be a function"); \ + Napi::Function var; \ + if (info.Length() > i && !info[i].IsUndefined()) { \ + if (!info[i].IsFunction()) { \ + Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ - var = Local::Cast(info[i]); \ + var = info[i].As(); \ } @@ -52,70 +76,62 @@ const char* sqlite_authorizer_string(int type); if (info.Length() <= (i)) { \ var = (default); \ } \ - else if (info[i]->IsInt32()) { \ - var = Nan::To(info[i]).FromJust(); \ + else if (info[i].IsNumber()) { \ + if (OtherIsInt(info[i].As())) { \ + var = info[i].As().Int32Value(); \ + } \ } \ else { \ - return Nan::ThrowTypeError("Argument " #i " must be an integer"); \ + Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ + return env.Null(); \ } #define DEFINE_CONSTANT_INTEGER(target, constant, name) \ - Nan::ForceSet(target, \ - Nan::New(#name).ToLocalChecked(), \ - Nan::New(constant), \ - static_cast(ReadOnly | DontDelete) \ - ); + Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define DEFINE_CONSTANT_STRING(target, constant, name) \ - Nan::ForceSet(target, \ - Nan::New(#name).ToLocalChecked(), \ - Nan::New(constant).ToLocalChecked(), \ - static_cast(ReadOnly | DontDelete) \ - ); - - -#define NODE_SET_GETTER(target, name, function) \ - Nan::SetAccessor((target)->InstanceTemplate(), \ - Nan::New(name).ToLocalChecked(), (function)); - -#define NODE_SET_SETTER(target, name, getter, setter) \ - Nan::SetAccessor((target)->InstanceTemplate(), \ - Nan::New(name).ToLocalChecked(), getter, setter); - -#define GET_STRING(source, name, property) \ - Nan::Utf8String name(Nan::Get(source, \ - Nan::New(prop).ToLocalChecked()).ToLocalChecked()); - -#define GET_INTEGER(source, name, prop) \ - int name = Nan::To(Nan::Get(source, \ - Nan::New(property).ToLocalChecked()).ToLocalChecked()).FromJust(); + Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define EXCEPTION(msg, errno, name) \ - Local name = Exception::Error(Nan::New( \ - std::string(sqlite_code_string(errno)) + \ - std::string(": ") + std::string(msg) \ - ).ToLocalChecked()); \ - Local name ##_obj = name.As(); \ - Nan::Set(name ##_obj, Nan::New("errno").ToLocalChecked(), Nan::New(errno));\ - Nan::Set(name ##_obj, Nan::New("code").ToLocalChecked(), \ - Nan::New(sqlite_code_string(errno)).ToLocalChecked()); + Napi::Value name = Napi::Error::New(env, \ + StringConcat( \ + StringConcat( \ + Napi::String::New(env, sqlite_code_string(errno)), \ + Napi::String::New(env, ": ") \ + ), \ + (msg) \ + ).Utf8Value() \ + ).Value(); \ + Napi::Object name ##_obj = name.As(); \ + (name ##_obj).Set( Napi::String::New(env, "errno"), Napi::Number::New(env, errno)); \ + (name ##_obj).Set( Napi::String::New(env, "code"), \ + Napi::String::New(env, sqlite_code_string(errno))); + #define EMIT_EVENT(obj, argc, argv) \ TRY_CATCH_CALL((obj), \ - Nan::Get(obj, \ - Nan::New("emit").ToLocalChecked()).ToLocalChecked().As(),\ + (obj).Get("emit").As(),\ argc, argv \ ); +// The Mac OS compiler complains when argv is NULL unless we +// first assign it to a locally defined variable. #define TRY_CATCH_CALL(context, callback, argc, argv) \ - Nan::MakeCallback((context), (callback), (argc), (argv)) + Napi::Value* passed_argv = argv;\ + std::vector args;\ + if ((argc != 0) && (passed_argv != NULL)) {\ + args.assign(passed_argv, passed_argv + argc);\ + }\ + (callback).MakeCallback(Napi::Value(context), args); #define WORK_DEFINITION(name) \ - static NAN_METHOD(name); \ + Napi::Value name(const Napi::CallbackInfo& info); \ static void Work_Begin##name(Baton* baton); \ - static void Work_##name(uv_work_t* req); \ - static void Work_After##name(uv_work_t* req); + static void Work_##name(napi_env env, void* data); \ + static void Work_After##name(napi_env env, napi_status status, void* data); #define STATEMENT_BEGIN(type) \ assert(baton); \ @@ -125,13 +141,16 @@ const char* sqlite_authorizer_string(int type); assert(baton->stmt->prepared); \ baton->stmt->locked = true; \ baton->stmt->db->pending++; \ - int status = uv_queue_work(uv_default_loop(), \ - &baton->request, \ - Work_##type, reinterpret_cast(Work_After##type)); \ - assert(status == 0); + Napi::Env env = baton->stmt->Env(); \ + int status = napi_create_async_work( \ + env, NULL, Napi::String::New(env, "sqlite3.Statement."#type), \ + Work_##type, Work_After##type, baton, &baton->request \ + ); \ + assert(status == 0); \ + napi_queue_async_work(env, baton->request); #define STATEMENT_INIT(type) \ - type* baton = static_cast(req->data); \ + type* baton = static_cast(data); \ Statement* stmt = baton->stmt; #define STATEMENT_END() \ @@ -141,6 +160,7 @@ const char* sqlite_authorizer_string(int type); stmt->db->pending--; \ stmt->Process(); \ stmt->db->Process(); \ + napi_delete_async_work(e, baton->request); \ delete baton; #define BACKUP_BEGIN(type) \ @@ -151,13 +171,16 @@ const char* sqlite_authorizer_string(int type); assert(baton->backup->inited); \ baton->backup->locked = true; \ baton->backup->db->pending++; \ - int status = uv_queue_work(uv_default_loop(), \ - &baton->request, \ - Work_##type, reinterpret_cast(Work_After##type)); \ - assert(status == 0); + Napi::Env env = baton->backup->Env(); \ + int status = napi_create_async_work( \ + env, NULL, Napi::String::New(env, "sqlite3.Backup."#type), \ + Work_##type, Work_After##type, baton, &baton->request \ + ); \ + assert(status == 0); \ + napi_queue_async_work(env, baton->request); #define BACKUP_INIT(type) \ - type* baton = static_cast(req->data); \ + type* baton = static_cast(data); \ Backup* backup = baton->backup; #define BACKUP_END() \ @@ -167,6 +190,7 @@ const char* sqlite_authorizer_string(int type); backup->db->pending--; \ backup->Process(); \ backup->db->Process(); \ + napi_delete_async_work(e, baton->request); \ delete baton; #define DELETE_FIELD(field) \ diff --git a/src/node_sqlite3.cc b/src/node_sqlite3.cc index 10f88ea68..b101b451f 100644 --- a/src/node_sqlite3.cc +++ b/src/node_sqlite3.cc @@ -13,53 +13,57 @@ using namespace node_sqlite3; namespace { -NAN_MODULE_INIT(RegisterModule) { - Nan::HandleScope scope; +Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); - Database::Init(target); - Statement::Init(target); - Backup::Init(target); + Database::Init(env, exports); + Statement::Init(env, exports); + Backup::Init(env, exports); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READONLY, OPEN_READONLY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READWRITE, OPEN_READWRITE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_CREATE, OPEN_CREATE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_FULLMUTEX, OPEN_FULLMUTEX); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_URI, OPEN_URI); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_SHAREDCACHE, OPEN_SHAREDCACHE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_PRIVATECACHE, OPEN_PRIVATECACHE); - DEFINE_CONSTANT_STRING(target, SQLITE_VERSION, VERSION); + exports.DefineProperties({ + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READONLY, OPEN_READONLY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_READWRITE, OPEN_READWRITE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_CREATE, OPEN_CREATE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_FULLMUTEX, OPEN_FULLMUTEX) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_URI, OPEN_URI) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_SHAREDCACHE, OPEN_SHAREDCACHE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_PRIVATECACHE, OPEN_PRIVATECACHE) + DEFINE_CONSTANT_STRING(exports, SQLITE_VERSION, VERSION) #ifdef SQLITE_SOURCE_ID - DEFINE_CONSTANT_STRING(target, SQLITE_SOURCE_ID, SOURCE_ID); + DEFINE_CONSTANT_STRING(exports, SQLITE_SOURCE_ID, SOURCE_ID) #endif - DEFINE_CONSTANT_INTEGER(target, SQLITE_VERSION_NUMBER, VERSION_NUMBER); + DEFINE_CONSTANT_INTEGER(exports, SQLITE_VERSION_NUMBER, VERSION_NUMBER) - DEFINE_CONSTANT_INTEGER(target, SQLITE_OK, OK); - DEFINE_CONSTANT_INTEGER(target, SQLITE_ERROR, ERROR); - DEFINE_CONSTANT_INTEGER(target, SQLITE_INTERNAL, INTERNAL); - DEFINE_CONSTANT_INTEGER(target, SQLITE_PERM, PERM); - DEFINE_CONSTANT_INTEGER(target, SQLITE_ABORT, ABORT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_BUSY, BUSY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_LOCKED, LOCKED); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOMEM, NOMEM); - DEFINE_CONSTANT_INTEGER(target, SQLITE_READONLY, READONLY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_INTERRUPT, INTERRUPT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_IOERR, IOERR); - DEFINE_CONSTANT_INTEGER(target, SQLITE_CORRUPT, CORRUPT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOTFOUND, NOTFOUND); - DEFINE_CONSTANT_INTEGER(target, SQLITE_FULL, FULL); - DEFINE_CONSTANT_INTEGER(target, SQLITE_CANTOPEN, CANTOPEN); - DEFINE_CONSTANT_INTEGER(target, SQLITE_PROTOCOL, PROTOCOL); - DEFINE_CONSTANT_INTEGER(target, SQLITE_EMPTY, EMPTY); - DEFINE_CONSTANT_INTEGER(target, SQLITE_SCHEMA, SCHEMA); - DEFINE_CONSTANT_INTEGER(target, SQLITE_TOOBIG, TOOBIG); - DEFINE_CONSTANT_INTEGER(target, SQLITE_CONSTRAINT, CONSTRAINT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_MISMATCH, MISMATCH); - DEFINE_CONSTANT_INTEGER(target, SQLITE_MISUSE, MISUSE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOLFS, NOLFS); - DEFINE_CONSTANT_INTEGER(target, SQLITE_AUTH, AUTH); - DEFINE_CONSTANT_INTEGER(target, SQLITE_FORMAT, FORMAT); - DEFINE_CONSTANT_INTEGER(target, SQLITE_RANGE, RANGE); - DEFINE_CONSTANT_INTEGER(target, SQLITE_NOTADB, NOTADB); + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OK, OK) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_ERROR, ERROR) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERNAL, INTERNAL) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_PERM, PERM) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_ABORT, ABORT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_BUSY, BUSY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_LOCKED, LOCKED) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOMEM, NOMEM) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_READONLY, READONLY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_INTERRUPT, INTERRUPT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_IOERR, IOERR) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_CORRUPT, CORRUPT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTFOUND, NOTFOUND) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_FULL, FULL) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_CANTOPEN, CANTOPEN) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_PROTOCOL, PROTOCOL) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_EMPTY, EMPTY) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_SCHEMA, SCHEMA) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_TOOBIG, TOOBIG) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_CONSTRAINT, CONSTRAINT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISMATCH, MISMATCH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_MISUSE, MISUSE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOLFS, NOLFS) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_AUTH, AUTH) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_FORMAT, FORMAT) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_RANGE, RANGE) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_NOTADB, NOTADB) + }); + + return exports; } } @@ -108,4 +112,4 @@ const char* sqlite_authorizer_string(int type) { } } -NODE_MODULE(node_sqlite3, RegisterModule) +NODE_API_MODULE(node_sqlite3, RegisterModule) diff --git a/src/statement.cc b/src/statement.cc index e09aeafff..15d6f91a1 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -1,7 +1,6 @@ #include -#include -#include -#include +#include +#include #include "macros.h" #include "database.h" @@ -9,27 +8,37 @@ using namespace node_sqlite3; -Nan::Persistent Statement::constructor_template; +Napi::FunctionReference Statement::constructor; -NAN_MODULE_INIT(Statement::Init) { - Nan::HandleScope scope; +Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { + Napi::HandleScope scope(env); - Local t = Nan::New(New); + Napi::Function t = DefineClass(env, "Statement", { + InstanceMethod("bind", &Statement::Bind), + InstanceMethod("get", &Statement::Get), + InstanceMethod("run", &Statement::Run), + InstanceMethod("all", &Statement::All), + InstanceMethod("each", &Statement::Each), + InstanceMethod("reset", &Statement::Reset), + InstanceMethod("finalize", &Statement::Finalize_), + }); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Statement").ToLocalChecked()); + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); - Nan::SetPrototypeMethod(t, "bind", Bind); - Nan::SetPrototypeMethod(t, "get", Get); - Nan::SetPrototypeMethod(t, "run", Run); - Nan::SetPrototypeMethod(t, "all", All); - Nan::SetPrototypeMethod(t, "each", Each); - Nan::SetPrototypeMethod(t, "reset", Reset); - Nan::SetPrototypeMethod(t, "finalize", Finalize); + exports.Set("Statement", t); + return exports; +} + +// A Napi InstanceOf for Javascript Objects "Date" and "RegExp" +bool OtherInstanceOf(Napi::Object source, char* object_type) { + if (object_type == "Date") { + return source.InstanceOf(source.Env().Global().Get("Date").As()); + } else if (object_type == "RegExp") { + return source.InstanceOf(source.Env().Global().Get("RegExp").As()); + } - constructor_template.Reset(t); - Nan::Set(target, Nan::New("Statement").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + return false; } void Statement::Process() { @@ -60,67 +69,71 @@ void Statement::Schedule(Work_Callback callback, Baton* baton) { } template void Statement::Error(T* baton) { - Nan::HandleScope scope; - Statement* stmt = baton->stmt; + + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + // Fail hard on logic errors. assert(stmt->status != 0); - EXCEPTION(stmt->message, stmt->status, exception); + EXCEPTION(Napi::String::New(env, stmt->message.c_str()), stmt->status, exception); - Local cb = Nan::New(baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { exception }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(stmt->handle(), 2, argv); + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(stmt->Value(), 2, argv); } } // { Database db, String sql, Array params, Function callback } -NAN_METHOD(Statement::New) { - if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Statement objects"); - } - +Statement::Statement(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); int length = info.Length(); if (length <= 0 || !Database::HasInstance(info[0])) { - return Nan::ThrowTypeError("Database object expected"); + Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); + return; } - else if (length <= 1 || !info[1]->IsString()) { - return Nan::ThrowTypeError("SQL query expected"); + else if (length <= 1 || !info[1].IsString()) { + Napi::TypeError::New(env, "SQL query expected").ThrowAsJavaScriptException(); + return; } - else if (length > 2 && !info[2]->IsUndefined() && !info[2]->IsFunction()) { - return Nan::ThrowTypeError("Callback expected"); + else if (length > 2 && !info[2].IsUndefined() && !info[2].IsFunction()) { + Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); + return; } - Database* db = Nan::ObjectWrap::Unwrap(info[0].As()); - Local sql = Local::Cast(info[1]); + Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); + Napi::String sql = info[1].As(); - Nan::ForceSet(info.This(),Nan::New("sql").ToLocalChecked(), sql, ReadOnly); + info.This().As().DefineProperty(Napi::PropertyDescriptor::Value("sql", sql, napi_default)); - Statement* stmt = new Statement(db); - stmt->Wrap(info.This()); + init(db); + Statement* stmt = this; - PrepareBaton* baton = new PrepareBaton(db, Local::Cast(info[2]), stmt); - baton->sql = std::string(*Nan::Utf8String(sql)); + PrepareBaton* baton = new PrepareBaton(db, info[2].As(), stmt); + baton->sql = std::string(sql.As().Utf8Value().c_str()); db->Schedule(Work_BeginPrepare, baton); - - info.GetReturnValue().Set(info.This()); } void Statement::Work_BeginPrepare(Database::Baton* baton) { assert(baton->db->open); baton->db->pending++; - int status = uv_queue_work(uv_default_loop(), - &baton->request, Work_Prepare, (uv_after_work_cb)Work_AfterPrepare); + Napi::Env env = baton->db->Env(); + int status = napi_create_async_work( + env, NULL, Napi::String::New(env, "sqlite3.Statement.Prepare"), + Work_Prepare, Work_AfterPrepare, baton, &baton->request + ); assert(status == 0); + napi_queue_async_work(env, baton->request); } -void Statement::Work_Prepare(uv_work_t* req) { +void Statement::Work_Prepare(napi_env e, void* data) { STATEMENT_INIT(PrepareBaton); // In case preparing fails, we use a mutex to make sure we get the associated @@ -144,21 +157,22 @@ void Statement::Work_Prepare(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterPrepare(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) { STATEMENT_INIT(PrepareBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_OK) { Error(baton); - stmt->Finalize(); + stmt->Finalize_(); } else { stmt->prepared = true; - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + if (!baton->callback.IsEmpty() && baton->callback.Value().IsFunction()) { + Napi::Function cb = baton->callback.Value(); + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -166,77 +180,88 @@ void Statement::Work_AfterPrepare(uv_work_t* req) { } template Values::Field* - Statement::BindParameter(const Local source, T pos) { - if (source->IsString() || source->IsRegExp()) { - Nan::Utf8String val(source); - return new Values::Text(pos, val.length(), *val); - } - else if (source->IsInt32()) { - return new Values::Integer(pos, Nan::To(source).FromJust()); - } - else if (source->IsNumber()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + Statement::BindParameter(const Napi::Value source, T pos) { + if (source.IsString()) { + std::string val = source.As().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); + } + else if (OtherInstanceOf(source.As(), "RegExp")) { + std::string val = source.ToString().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); + } + else if (source.IsNumber()) { + if (OtherIsInt(source.As())) { + return new Values::Integer(pos, source.As().Int32Value()); + } else { + return new Values::Float(pos, source.As().DoubleValue()); + } } - else if (source->IsBoolean()) { - return new Values::Integer(pos, Nan::To(source).FromJust() ? 1 : 0); + else if (source.IsBoolean()) { + return new Values::Integer(pos, source.As().Value() ? 1 : 0); } - else if (source->IsNull()) { + else if (source.IsNull()) { return new Values::Null(pos); } - else if (Buffer::HasInstance(source)) { - Local buffer = Nan::To(source).ToLocalChecked(); - return new Values::Blob(pos, Buffer::Length(buffer), Buffer::Data(buffer)); + else if (source.IsBuffer()) { + Napi::Buffer buffer = source.As>(); + return new Values::Blob(pos, buffer.Length(), buffer.Data()); } - else if (source->IsDate()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + else if (OtherInstanceOf(source.As(), "Date")) { + return new Values::Float(pos, source.ToNumber().DoubleValue()); + } + else if (source.IsObject()) { + std::string val = source.ToString().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); } else { return NULL; } } -template T* Statement::Bind(Nan::NAN_METHOD_ARGS_TYPE info, int start, int last) { - Nan::HandleScope scope; +template T* Statement::Bind(const Napi::CallbackInfo& info, int start, int last) { + Napi::Env env = info.Env(); + Napi::HandleScope scope(env); if (last < 0) last = info.Length(); - Local callback; - if (last > start && info[last - 1]->IsFunction()) { - callback = Local::Cast(info[last - 1]); + Napi::Function callback; + if (last > start && info[last - 1].IsFunction()) { + callback = info[last - 1].As(); last--; } T* baton = new T(this, callback); if (start < last) { - if (info[start]->IsArray()) { - Local array = Local::Cast(info[start]); - int length = array->Length(); + if (info[start].IsArray()) { + Napi::Array array = info[start].As(); + int length = array.Length(); // Note: bind parameters start with 1. for (int i = 0, pos = 1; i < length; i++, pos++) { - baton->parameters.push_back(BindParameter(Nan::Get(array, i).ToLocalChecked(), pos)); + baton->parameters.push_back(BindParameter((array).Get(i), pos)); } } - else if (!info[start]->IsObject() || info[start]->IsRegExp() || info[start]->IsDate() || Buffer::HasInstance(info[start])) { + else if (!info[start].IsObject() || OtherInstanceOf(info[start].As(), "RegExp") || OtherInstanceOf(info[start].As(), "Date") || info[start].IsBuffer()) { // Parameters directly in array. // Note: bind parameters start with 1. for (int i = start, pos = 1; i < last; i++, pos++) { baton->parameters.push_back(BindParameter(info[i], pos)); } } - else if (info[start]->IsObject()) { - Local object = Local::Cast(info[start]); - Local array = Nan::GetPropertyNames(object).ToLocalChecked(); - int length = array->Length(); + else if (info[start].IsObject()) { + Napi::Object object = info[start].As(); + Napi::Array array = object.GetPropertyNames(); + int length = array.Length(); for (int i = 0; i < length; i++) { - Local name = Nan::Get(array, i).ToLocalChecked(); + Napi::Value name = (array).Get(i); + Napi::Number num = name.ToNumber(); - if (name->IsInt32()) { + if (num.Int32Value() == num.DoubleValue()) { baton->parameters.push_back( - BindParameter(Nan::Get(object, name).ToLocalChecked(), Nan::To(name).FromJust())); + BindParameter((object).Get(name), num.Int32Value())); } else { - baton->parameters.push_back(BindParameter(Nan::Get(object, name).ToLocalChecked(), - *Nan::Utf8String(name))); + baton->parameters.push_back(BindParameter((object).Get(name), + name.As().Utf8Value().c_str())); } } } @@ -263,7 +288,7 @@ bool Statement::Bind(const Parameters & parameters) { Values::Field* field = *it; if (field != NULL) { - int pos; + unsigned int pos; if (field->index > 0) { pos = field->index; } @@ -305,16 +330,18 @@ bool Statement::Bind(const Parameters & parameters) { return true; } -NAN_METHOD(Statement::Bind) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Bind(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowTypeError("Data type is not supported"); + Napi::TypeError::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginBind, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -322,7 +349,7 @@ void Statement::Work_BeginBind(Baton* baton) { STATEMENT_BEGIN(Bind); } -void Statement::Work_Bind(uv_work_t* req) { +void Statement::Work_Bind(napi_env e, void* data) { STATEMENT_INIT(Baton); sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); @@ -331,20 +358,21 @@ void Statement::Work_Bind(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterBind(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) { STATEMENT_INIT(Baton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_OK) { Error(baton); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -353,16 +381,18 @@ void Statement::Work_AfterBind(uv_work_t* req) { -NAN_METHOD(Statement::Get) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Get(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginGet, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -370,7 +400,7 @@ void Statement::Work_BeginGet(Baton* baton) { STATEMENT_BEGIN(Get); } -void Statement::Work_Get(uv_work_t* req) { +void Statement::Work_Get(napi_env e, void* data) { STATEMENT_INIT(RowBaton); if (stmt->status != SQLITE_DONE || baton->parameters.size()) { @@ -394,26 +424,27 @@ void Statement::Work_Get(uv_work_t* req) { } } -void Statement::Work_AfterGet(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) { STATEMENT_INIT(RowBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { Error(baton); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { if (stmt->status == SQLITE_ROW) { // Create the result array from the data we acquired. - Local argv[] = { Nan::Null(), RowToJS(&baton->row) }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + Napi::Value argv[] = { env.Null(), RowToJS(env, &baton->row) }; + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } } @@ -421,16 +452,18 @@ void Statement::Work_AfterGet(uv_work_t* req) { STATEMENT_END(); } -NAN_METHOD(Statement::Run) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Run(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginRun, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -438,7 +471,7 @@ void Statement::Work_BeginRun(Baton* baton) { STATEMENT_BEGIN(Run); } -void Statement::Work_Run(uv_work_t* req) { +void Statement::Work_Run(napi_env e, void* data) { STATEMENT_INIT(RunBaton); sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); @@ -464,39 +497,42 @@ void Statement::Work_Run(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterRun(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) { STATEMENT_INIT(RunBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_ROW && stmt->status != SQLITE_DONE) { Error(baton); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Nan::Set(stmt->handle(), Nan::New("lastID").ToLocalChecked(), Nan::New(baton->inserted_id)); - Nan::Set(stmt->handle(), Nan::New("changes").ToLocalChecked(), Nan::New(baton->changes)); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + (stmt->Value()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id)); + (stmt->Value()).Set( Napi::String::New(env, "changes"), Napi::Number::New(env, baton->changes)); - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } STATEMENT_END(); } -NAN_METHOD(Statement::All) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::All(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; Baton* baton = stmt->Bind(info); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { stmt->Schedule(Work_BeginAll, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -504,7 +540,7 @@ void Statement::Work_BeginAll(Baton* baton) { STATEMENT_BEGIN(All); } -void Statement::Work_All(uv_work_t* req) { +void Statement::Work_All(napi_env e, void* data) { STATEMENT_INIT(RowsBaton); sqlite3_mutex* mtx = sqlite3_db_mutex(stmt->db->_handle); @@ -530,38 +566,39 @@ void Statement::Work_All(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterAll(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { STATEMENT_INIT(RowsBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_DONE) { Error(baton); } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { if (baton->rows.size()) { // Create the result array from the data we acquired. - Local result(Nan::New(baton->rows.size())); + Napi::Array result(Napi::Array::New(env, baton->rows.size())); Rows::const_iterator it = baton->rows.begin(); Rows::const_iterator end = baton->rows.end(); for (int i = 0; it < end; ++it, i++) { - Nan::Set(result, i, RowToJS(*it)); + (result).Set(i, RowToJS(env,*it)); delete *it; } - Local argv[] = { Nan::Null(), result }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + Napi::Value argv[] = { env.Null(), result }; + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { // There were no result rows. - Local argv[] = { - Nan::Null(), - Nan::New(0) + Napi::Value argv[] = { + env.Null(), + Napi::Array::New(env, 0) }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } } } @@ -569,24 +606,26 @@ void Statement::Work_AfterAll(uv_work_t* req) { STATEMENT_END(); } -NAN_METHOD(Statement::Each) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Each(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; int last = info.Length(); - Local completed; - if (last >= 2 && info[last - 1]->IsFunction() && info[last - 2]->IsFunction()) { - completed = Local::Cast(info[--last]); + Napi::Function completed; + if (last >= 2 && info[last - 1].IsFunction() && info[last - 2].IsFunction()) { + completed = info[--last].As(); } EachBaton* baton = stmt->Bind(info, 0, last); if (baton == NULL) { - return Nan::ThrowError("Data type is not supported"); + Napi::Error::New(env, "Data type is not supported").ThrowAsJavaScriptException(); + return env.Null(); } else { - baton->completed.Reset(completed); + baton->completed.Reset(completed, 1); stmt->Schedule(Work_BeginEach, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -595,13 +634,13 @@ void Statement::Work_BeginEach(Baton* baton) { // the event loop. This prevents dangling events. EachBaton* each_baton = static_cast(baton); each_baton->async = new Async(each_baton->stmt, reinterpret_cast(AsyncEach)); - each_baton->async->item_cb.Reset(each_baton->callback); - each_baton->async->completed_cb.Reset(each_baton->completed); + each_baton->async->item_cb.Reset(each_baton->callback.Value(), 1); + each_baton->async->completed_cb.Reset(each_baton->completed.Value(), 1); STATEMENT_BEGIN(Each); } -void Statement::Work_Each(uv_work_t* req) { +void Statement::Work_Each(napi_env e, void* data) { STATEMENT_INIT(EachBaton); Async* async = baton->async; @@ -651,11 +690,12 @@ void Statement::CloseCallback(uv_handle_t* handle) { delete async; } -void Statement::AsyncEach(uv_async_t* handle, int status) { - Nan::HandleScope scope; - +void Statement::AsyncEach(uv_async_t* handle) { Async* async = static_cast(handle->data); + Napi::Env env = async->stmt->Env(); + Napi::HandleScope scope(env); + while (true) { // Get the contents out of the data cache for us to process in the JS callback. Rows rows; @@ -667,41 +707,42 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { break; } - Local cb = Nan::New(async->item_cb); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[2]; - argv[0] = Nan::Null(); + Napi::Function cb = async->item_cb.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[2]; + argv[0] = env.Null(); Rows::const_iterator it = rows.begin(); Rows::const_iterator end = rows.end(); for (int i = 0; it < end; ++it, i++) { - argv[1] = RowToJS(*it); + argv[1] = RowToJS(env,*it); async->retrieved++; - TRY_CATCH_CALL(async->stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); delete *it; } } } - Local cb = Nan::New(async->completed_cb); + Napi::Function cb = async->completed_cb.Value(); if (async->completed) { if (!cb.IsEmpty() && - cb->IsFunction()) { - Local argv[] = { - Nan::Null(), - Nan::New(async->retrieved) + cb.IsFunction()) { + Napi::Value argv[] = { + env.Null(), + Napi::Number::New(env, async->retrieved) }; - TRY_CATCH_CALL(async->stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(async->stmt->Value(), cb, 2, argv); } uv_close(reinterpret_cast(handle), CloseCallback); } } -void Statement::Work_AfterEach(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) { STATEMENT_INIT(EachBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_DONE) { Error(baton); } @@ -709,74 +750,76 @@ void Statement::Work_AfterEach(uv_work_t* req) { STATEMENT_END(); } -NAN_METHOD(Statement::Reset) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Reset(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(stmt, callback); stmt->Schedule(Work_BeginReset, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Statement::Work_BeginReset(Baton* baton) { STATEMENT_BEGIN(Reset); } -void Statement::Work_Reset(uv_work_t* req) { +void Statement::Work_Reset(napi_env e, void* data) { STATEMENT_INIT(Baton); sqlite3_reset(stmt->_handle); stmt->status = SQLITE_OK; } -void Statement::Work_AfterReset(uv_work_t* req) { - Nan::HandleScope scope; - +void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) { STATEMENT_INIT(Baton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + // Fire callbacks. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + Napi::Value argv[] = { env.Null() }; + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } STATEMENT_END(); } -Local Statement::RowToJS(Row* row) { - Nan::EscapableHandleScope scope; +Napi::Value Statement::RowToJS(Napi::Env env, Row* row) { + Napi::EscapableHandleScope scope(env); - Local result = Nan::New(); + Napi::Object result = Napi::Object::New(env); Row::const_iterator it = row->begin(); Row::const_iterator end = row->end(); for (int i = 0; it < end; ++it, i++) { Values::Field* field = *it; - Local value; + Napi::Value value; switch (field->type) { case SQLITE_INTEGER: { - value = Nan::New(((Values::Integer*)field)->value); + value = Napi::Number::New(env, ((Values::Integer*)field)->value); } break; case SQLITE_FLOAT: { - value = Nan::New(((Values::Float*)field)->value); + value = Napi::Number::New(env, ((Values::Float*)field)->value); } break; case SQLITE_TEXT: { - value = Nan::New(((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()).ToLocalChecked(); + value = Napi::String::New(env, ((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()); } break; case SQLITE_BLOB: { - value = Nan::CopyBuffer(((Values::Blob*)field)->value, ((Values::Blob*)field)->length).ToLocalChecked(); + value = Napi::Buffer::Copy(env, ((Values::Blob*)field)->value, ((Values::Blob*)field)->length); } break; case SQLITE_NULL: { - value = Nan::Null(); + value = env.Null(); } break; } - Nan::Set(result, Nan::New(field->name.c_str()).ToLocalChecked(), value); + (result).Set(Napi::String::New(env, field->name.c_str()), value); DELETE_FIELD(field); } @@ -816,31 +859,33 @@ void Statement::GetRow(Row* row, sqlite3_stmt* stmt) { } } -NAN_METHOD(Statement::Finalize) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::Finalize_(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); Baton* baton = new Baton(stmt, callback); - stmt->Schedule(Finalize, baton); + stmt->Schedule(Finalize_, baton); - info.GetReturnValue().Set(stmt->db->handle()); + return stmt->db->Value(); } -void Statement::Finalize(Baton* baton) { - Nan::HandleScope scope; +void Statement::Finalize_(Baton* baton) { + Napi::Env env = baton->stmt->Env(); + Napi::HandleScope scope(env); - baton->stmt->Finalize(); + baton->stmt->Finalize_(); // Fire callback in case there was one. - Local cb = Nan::New(baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - TRY_CATCH_CALL(baton->stmt->handle(), cb, 0, NULL); + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { + TRY_CATCH_CALL(baton->stmt->Value(), cb, 0, NULL); } delete baton; } -void Statement::Finalize() { +void Statement::Finalize_() { assert(!finalized); finalized = true; CleanQueue(); @@ -852,13 +897,14 @@ void Statement::Finalize() { } void Statement::CleanQueue() { - Nan::HandleScope scope; + Napi::Env env = this->Env(); + Napi::HandleScope scope(env); if (prepared && !queue.empty()) { // This statement has already been prepared and is now finalized. // Fire error for all remaining items in the queue. - EXCEPTION("Statement is already finalized", SQLITE_MISUSE, exception); - Local argv[] = { exception }; + EXCEPTION(Napi::String::New(env, "Statement is already finalized"), SQLITE_MISUSE, exception); + Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. @@ -866,11 +912,11 @@ void Statement::CleanQueue() { Call* call = queue.front(); queue.pop(); - Local cb = Nan::New(call->baton->callback); + Napi::Function cb = call->baton->callback.Value(); if (prepared && !cb.IsEmpty() && - cb->IsFunction()) { - TRY_CATCH_CALL(handle(), cb, 1, argv); + cb.IsFunction()) { + TRY_CATCH_CALL(Value(), cb, 1, argv); called = true; } @@ -883,8 +929,8 @@ void Statement::CleanQueue() { // When we couldn't call a callback function, emit an error on the // Statement object. if (!called) { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; - EMIT_EVENT(handle(), 2, info); + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; + EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { diff --git a/src/statement.h b/src/statement.h index 90d295b70..04a572153 100644 --- a/src/statement.h +++ b/src/statement.h @@ -1,10 +1,6 @@ #ifndef NODE_SQLITE3_SRC_STATEMENT_H #define NODE_SQLITE3_SRC_STATEMENT_H - -#include "database.h" -#include "threading.h" - #include #include #include @@ -12,10 +8,13 @@ #include #include -#include +#include +#include -using namespace v8; -using namespace node; +#include "database.h" +#include "threading.h" + +using namespace Napi; namespace node_sqlite3 { @@ -71,23 +70,22 @@ typedef Row Parameters; -class Statement : public Nan::ObjectWrap { +class Statement : public Napi::ObjectWrap { public: - static Nan::Persistent constructor_template; + static Napi::FunctionReference constructor; - static NAN_MODULE_INIT(Init); - static NAN_METHOD(New); + static Napi::Object Init(Napi::Env env, Napi::Object exports); + static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { - uv_work_t request; + napi_async_work request; Statement* stmt; - Nan::Persistent callback; + Napi::FunctionReference callback; Parameters parameters; - Baton(Statement* stmt_, Local cb_) : stmt(stmt_) { + Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { stmt->Ref(); - request.data = this; - callback.Reset(cb_); + callback.Reset(cb_, 1); } virtual ~Baton() { for (unsigned int i = 0; i < parameters.size(); i++) { @@ -100,20 +98,20 @@ class Statement : public Nan::ObjectWrap { }; struct RowBaton : Baton { - RowBaton(Statement* stmt_, Local cb_) : + RowBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Row row; }; struct RunBaton : Baton { - RunBaton(Statement* stmt_, Local cb_) : + RunBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_), inserted_id(0), changes(0) {} sqlite3_int64 inserted_id; int changes; }; struct RowsBaton : Baton { - RowsBaton(Statement* stmt_, Local cb_) : + RowsBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} Rows rows; }; @@ -121,10 +119,10 @@ class Statement : public Nan::ObjectWrap { struct Async; struct EachBaton : Baton { - Nan::Persistent completed; + Napi::FunctionReference completed; Async* async; // Isn't deleted when the baton is deleted. - EachBaton(Statement* stmt_, Local cb_) : + EachBaton(Statement* stmt_, Napi::Function cb_) : Baton(stmt_, cb_) {} virtual ~EachBaton() { completed.Reset(); @@ -134,7 +132,7 @@ class Statement : public Nan::ObjectWrap { struct PrepareBaton : Database::Baton { Statement* stmt; std::string sql; - PrepareBaton(Database* db_, Local cb_, Statement* stmt_) : + PrepareBaton(Database* db_, Napi::Function cb_, Statement* stmt_) : Baton(db_, cb_), stmt(stmt_) { stmt->Ref(); } @@ -143,7 +141,7 @@ class Statement : public Nan::ObjectWrap { if (!db->IsOpen() && db->IsLocked()) { // The database handle was closed before the statement could be // prepared. - stmt->Finalize(); + stmt->Finalize_(); } } }; @@ -166,15 +164,17 @@ class Statement : public Nan::ObjectWrap { // Store the callbacks here because we don't have // access to the baton in the async callback. - Nan::Persistent item_cb; - Nan::Persistent completed_cb; + Napi::FunctionReference item_cb; + Napi::FunctionReference completed_cb; Async(Statement* st, uv_async_cb async_cb) : stmt(st), completed(false), retrieved(0) { watcher.data = this; NODE_SQLITE3_MUTEX_INIT stmt->Ref(); - uv_async_init(uv_default_loop(), &watcher, async_cb); + uv_loop_t *loop; + napi_get_uv_event_loop(stmt->Env(), &loop); + uv_async_init(loop, &watcher, async_cb); } ~Async() { @@ -185,18 +185,20 @@ class Statement : public Nan::ObjectWrap { } }; - Statement(Database* db_) : Nan::ObjectWrap(), - db(db_), - _handle(NULL), - status(SQLITE_OK), - prepared(false), - locked(true), - finalized(false) { + void init(Database* db_) { + db = db_; + _handle = NULL; + status = SQLITE_OK; + prepared = false; + locked = true; + finalized = false; db->Ref(); } + Statement(const Napi::CallbackInfo& info); + ~Statement() { - if (!finalized) Finalize(); + if (!finalized) Finalize_(); } WORK_DEFINITION(Bind); @@ -206,25 +208,25 @@ class Statement : public Nan::ObjectWrap { WORK_DEFINITION(Each); WORK_DEFINITION(Reset); - static NAN_METHOD(Finalize); + Napi::Value Finalize_(const Napi::CallbackInfo& info); protected: static void Work_BeginPrepare(Database::Baton* baton); - static void Work_Prepare(uv_work_t* req); - static void Work_AfterPrepare(uv_work_t* req); + static void Work_Prepare(napi_env env, void* data); + static void Work_AfterPrepare(napi_env env, napi_status status, void* data); - static void AsyncEach(uv_async_t* handle, int status); + static void AsyncEach(uv_async_t* handle); static void CloseCallback(uv_handle_t* handle); - static void Finalize(Baton* baton); - void Finalize(); + static void Finalize_(Baton* baton); + void Finalize_(); - template inline Values::Field* BindParameter(const Local source, T pos); - template T* Bind(Nan::NAN_METHOD_ARGS_TYPE info, int start = 0, int end = -1); + template inline Values::Field* BindParameter(const Napi::Value source, T pos); + template T* Bind(const Napi::CallbackInfo& info, int start = 0, int end = -1); bool Bind(const Parameters ¶meters); static void GetRow(Row* row, sqlite3_stmt* stmt); - static Local RowToJS(Row* row); + static Napi::Value RowToJS(Napi::Env env, Row* row); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue(); diff --git a/test/affected.test.js b/test/affected.test.js index f0bf192e6..031dc1b9c 100644 --- a/test/affected.test.js +++ b/test/affected.test.js @@ -11,7 +11,7 @@ describe('query properties', function() { it('should return the correct lastID', function(done) { var stmt = db.prepare("INSERT INTO foo VALUES(?, ?)"); var j = 1; - for (var i = 0; i < 1000; i++) { + for (var i = 0; i < 5000; i++) { stmt.run(i, "demo", function(err) { if (err) throw err; // Relies on SQLite's row numbering to be gapless and starting @@ -25,7 +25,7 @@ describe('query properties', function() { it('should return the correct changes count', function(done) { db.run("UPDATE foo SET id = id + 1 WHERE id % 2 = 0", function(err) { if (err) throw err; - assert.equal(500, this.changes); + assert.equal(2500, this.changes); done(); }); }); diff --git a/test/database_fail.test.js b/test/database_fail.test.js index 35589ece5..8b936769e 100644 --- a/test/database_fail.test.js +++ b/test/database_fail.test.js @@ -10,11 +10,11 @@ describe('error handling', function() { it('throw when calling Database() without new', function() { assert.throws(function() { sqlite3.Database(':memory:'); - }, (/Use the new operator to create new Database objects/)); + }, (/Class constructors cannot be invoked without 'new'/)); assert.throws(function() { sqlite3.Statement(); - }, (/Use the new operator to create new Statement objects/)); + }, (/Class constructors cannot be invoked without 'new'/)); }); it('should error when calling Database#get on a missing table', function(done) {