diff --git a/.travis.yml b/.travis.yml index cb8683715..662ed9170 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,14 +7,6 @@ dist: precise git: depth: 10 -addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.5 - packages: - - clang-3.5 - matrix: include: # Linux @@ -23,127 +15,23 @@ matrix: env: NODE_VERSION="10" addons: apt: - sources: [ 'ubuntu-toolchain-r-test','llvm-toolchain-precise-3.5', 'gcc-multilib', 'g++-multilib', 'libsqlite3-dev:i386' ] + sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ] 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="5" COVERAGE=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'] - - 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 + env: NODE_VERSION="10" 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'] + packages: [ 'clang-3.5', 'libstdc++-4.9-dev', 'libsqlite3-dev'] # OS X - os: osx compiler: clang - env: NODE_VERSION="5" COVERAGE=true PUBLISHABLE=false # node abi 47 + env: NODE_VERSION="10" COVERAGE=true PUBLISHABLE=false # node abi 47 - 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="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'] env: global: diff --git a/Makefile b/Makefile index 1cd9b770a..f4f3c1617 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ all: build npm install --build-from-source build: ./node_modules - ./node_modules/.bin/node-pre-gyp build --loglevel=silent + ./node_modules/.bin/node-pre-gyp build --loglevel=error debug: ./node_modules/.bin/node-pre-gyp rebuild --debug diff --git a/appveyor.yml b/appveyor.yml index 8740b5bfc..62316038c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,90 +1,9 @@ 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 platform: x86 - # electron - - nodejs_version: 8 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 2.0.1 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 8 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 2.0.1 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 8 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.8.4 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 8 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.8.4 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 7 - platform: x64 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.7.12 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 7 - platform: x86 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.7.12 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 6 - platform: x64 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.6.2 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 6 - platform: x86 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.6.2 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 6 - platform: x64 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.3.14 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron - - nodejs_version: 6 - platform: x86 - msvs_toolset: 12 - NODE_RUNTIME: electron - NODE_RUNTIME_VERSION: 1.3.14 - TOOLSET_ARGS: --dist-url=https://atom.io/download/electron os: Visual Studio 2015 diff --git a/binding.gyp b/binding.gyp index 59c93d30a..11a17d69a 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,7 +7,10 @@ "targets": [ { "target_name": "<(module_name)", - "include_dirs": ["", @@ -37,6 +37,7 @@ "url": "git://github.com/mapbox/node-sqlite3.git" }, "dependencies": { + "node-addon-api": "^1.2.0", "nan": "~2.10.0", "node-pre-gyp": "~0.10.1" }, diff --git a/src/async.h b/src/async.h index 5232c127d..4ab8cea0c 100644 --- a/src/async.h +++ b/src/async.h @@ -3,6 +3,7 @@ #include "threading.h" #include +#include #if defined(NODE_SQLITE3_BOOST_THREADING) #include diff --git a/src/database.cc b/src/database.cc index 4dbd5c6ab..c44f1e312 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(Nan::New("Database handle is closed").ToLocalChecked(), 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(this->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(Nan::New("Database is closed").ToLocalChecked(), 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,37 +106,31 @@ 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(); 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(); + if (info.Length() >= pos && info[pos].IsNumber()) { + 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) { @@ -169,53 +162,56 @@ void Database::Work_Open(uv_work_t* req) { } void Database::Work_AfterOpen(uv_work_t* req) { - Nan::HandleScope scope; - 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(Nan::New(baton->message.c_str()).ToLocalChecked(), 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(); } 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 = info.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) { @@ -247,128 +243,137 @@ void Database::Work_Close(uv_work_t* req) { } void Database::Work_AfterClose(uv_work_t* req) { - Nan::HandleScope scope; - 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(Nan::New(baton->message.c_str()).ToLocalChecked(), 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(); } delete baton; } -NAN_METHOD(Database::Serialize) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = true; - if (!callback.IsEmpty() && callback->IsFunction()) { + if (!callback.IsUndefined() && 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 = info.Env(); + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; db->serialize = false; - if (!callback.IsEmpty() && callback->IsFunction()) { + if (!callback.IsUndefined() && 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 = info.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( - Nan::To(info[0]).ToLocalChecked(), - Nan::New(" is not a valid configuration option").ToLocalChecked() - ))); + Napi::TypeError::New(env, (StringConcat( + 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 = info.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) { @@ -409,13 +414,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; } @@ -449,14 +455,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; } @@ -493,28 +500,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 = info.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) { @@ -546,28 +555,29 @@ void Database::Work_Exec(uv_work_t* req) { } void Database::Work_AfterExec(uv_work_t* req) { - Nan::HandleScope scope; - 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(Nan::New(baton->message.c_str()).ToLocalChecked(), 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(); @@ -575,29 +585,31 @@ void Database::Work_AfterExec(uv_work_t* req) { 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(); @@ -605,16 +617,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 = info.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) { @@ -649,27 +662,29 @@ void Database::Work_LoadExtension(uv_work_t* req) { } void Database::Work_AfterLoadExtension(uv_work_t* req) { - Nan::HandleScope scope; - 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(Nan::New(baton->message.c_str()).ToLocalChecked(), 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(); diff --git a/src/database.h b/src/database.h index c5455abe3..c7c3900f1 100644 --- a/src/database.h +++ b/src/database.h @@ -7,41 +7,45 @@ #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; 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 +56,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_) {} }; @@ -99,17 +103,16 @@ class Database : public Nan::ObjectWrap { friend class Statement; -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() { @@ -119,40 +122,41 @@ class Database : public Nan::ObjectWrap { open = false; } - static NAN_METHOD(New); + Database(const Napi::CallbackInfo& info); +protected: static void Work_BeginOpen(Baton* baton); static void Work_Open(uv_work_t* req); static void Work_AfterOpen(uv_work_t* req); - 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 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 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 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 38399ee86..45d767f72 100644 --- a/src/macros.h +++ b/src/macros.h @@ -3,42 +3,49 @@ 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() ); +} #define REQUIRE_ARGUMENTS(n) \ if (info.Length() < (n)) { \ - return Nan::ThrowTypeError("Expected " #n "arguments"); \ + Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ } #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(); \ } \ - 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(); \ } \ - 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(); \ } \ - Nan::Utf8String var(info[i]); + std::string var = info[i].As(); #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(); \ } \ - var = Local::Cast(info[i]); \ + var = info[i].As(); \ } @@ -47,71 +54,67 @@ 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()) { \ + 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(); \ } #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) \ - ); + Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define NODE_SET_GETTER(target, name, function) \ - Nan::SetAccessor((target)->InstanceTemplate(), \ - Nan::New(name).ToLocalChecked(), (function)); - -#define GET_STRING(source, name, property) \ - Nan::Utf8String name(Nan::Get(source, \ - Nan::New(prop).ToLocalChecked()).ToLocalChecked()); + Napi::SetAccessor((target).InstanceTemplate(), \ + Napi::Symbol::New(env, name), (function)); #define GET_INTEGER(source, name, prop) \ - int name = Nan::To(Nan::Get(source, \ - Nan::New(property).ToLocalChecked()).ToLocalChecked()).FromJust(); + int name = Napi::To((source, \ + Napi::Number::New(env).Get(property))); #define EXCEPTION(msg, errno, name) \ - Local name = Exception::Error( \ - String::Concat( \ - String::Concat( \ - Nan::New(sqlite_code_string(errno)).ToLocalChecked(), \ - Nan::New(": ").ToLocalChecked() \ + Napi::Value name = Napi::Error::New(env, \ + StringConcat( \ + StringConcat( \ + Napi::String::New(env, sqlite_code_string(errno)), \ + Napi::String::New(env, ": ") \ ), \ (msg) \ - ) \ - ); \ - 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()); + ).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); \ - static void Work_Begin##name(Baton* baton); \ - static void Work_##name(uv_work_t* req); \ + 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); #define STATEMENT_BEGIN(type) \ diff --git a/src/node_sqlite3.cc b/src/node_sqlite3.cc index 64326f784..c992e42a2 100644 --- a/src/node_sqlite3.cc +++ b/src/node_sqlite3.cc @@ -12,49 +12,53 @@ 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); + Database::Init(env, exports); + Statement::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_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_STRING(exports, SQLITE_VERSION, VERSION) + DEFINE_CONSTANT_INTEGER(exports, SQLITE_OPEN_FULLMUTEX, OPEN_FULLMUTEX) #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; } } @@ -103,4 +107,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 6efbe5766..a9d4dfcc1 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -1,7 +1,4 @@ #include -#include -#include -#include #include "macros.h" #include "database.h" @@ -9,27 +6,26 @@ 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); - - constructor_template.Reset(t); - Nan::Set(target, Nan::New("Statement").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + exports.Set("Statement", t); + return exports; } void Statement::Process() { @@ -60,56 +56,56 @@ 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(Nan::New(stmt->message.c_str()).ToLocalChecked(), 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) { @@ -145,20 +141,21 @@ void Statement::Work_Prepare(uv_work_t* req) { } void Statement::Work_AfterPrepare(uv_work_t* req) { - Nan::HandleScope scope; - STATEMENT_INIT(PrepareBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_OK) { Error(baton); 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 +163,82 @@ 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); + Statement::BindParameter(const Napi::Value source, T pos) { + if (source.IsString()/* || source.IsRegExp()*/) { + std::string val = source.As().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); } - else if (source->IsInt32()) { - return new Values::Integer(pos, Nan::To(source).FromJust()); + //else if (source.IsNumber()) { + // return new Values::Integer(pos, source.As().Int32Value()); + //} + else if (source.IsNumber()) { + return new Values::Float(pos, source.As().DoubleValue()); } - else if (source->IsNumber()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + else if (source.IsBoolean()) { + return new Values::Integer(pos, source.As().Value() ? 1 : 0); } - else if (source->IsBoolean()) { - return new Values::Integer(pos, Nan::To(source).FromJust() ? 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 (source.IsDate()) { + // return new Values::Float(pos, source.As().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()/* || info[start].IsRegExp() || info[start].IsDate()*/ || 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); - if (name->IsInt32()) { + if (name.IsNumber()) { baton->parameters.push_back( - BindParameter(Nan::Get(object, name).ToLocalChecked(), Nan::To(name).FromJust())); + BindParameter((object).Get(name), name.As().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())); } } } @@ -305,16 +307,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(); } } @@ -332,19 +336,20 @@ void Statement::Work_Bind(uv_work_t* req) { } void Statement::Work_AfterBind(uv_work_t* req) { - Nan::HandleScope scope; - 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 +358,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(); } } @@ -395,25 +402,26 @@ void Statement::Work_Get(uv_work_t* req) { } void Statement::Work_AfterGet(uv_work_t* req) { - Nan::HandleScope scope; - 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 +429,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(); } } @@ -465,38 +475,41 @@ void Statement::Work_Run(uv_work_t* req) { } void Statement::Work_AfterRun(uv_work_t* req) { - Nan::HandleScope scope; - 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(); } } @@ -531,37 +544,38 @@ void Statement::Work_All(uv_work_t* req) { } void Statement::Work_AfterAll(uv_work_t* req) { - Nan::HandleScope scope; - 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 +583,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,8 +611,8 @@ 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); } @@ -652,10 +668,11 @@ void Statement::CloseCallback(uv_handle_t* handle) { } void Statement::AsyncEach(uv_async_t* handle, int status) { - Nan::HandleScope scope; - 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 +684,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) + if (!cb.IsUndefined() && + 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; - STATEMENT_INIT(EachBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_DONE) { Error(baton); } @@ -709,15 +727,16 @@ 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) { @@ -732,51 +751,52 @@ void Statement::Work_Reset(uv_work_t* req) { } void Statement::Work_AfterReset(uv_work_t* req) { - Nan::HandleScope scope; - 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,25 +836,27 @@ 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); - info.GetReturnValue().Set(stmt->db->handle()); + return stmt->db->Value(); } void Statement::Finalize(Baton* baton) { - Nan::HandleScope scope; + Napi::Env env = baton->stmt->Env(); + Napi::HandleScope scope(env); 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; @@ -852,13 +874,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(Nan::New("Statement is already finalized").ToLocalChecked(), 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 +889,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); + if (prepared && !cb.IsUndefined() && + cb.IsFunction()) { + TRY_CATCH_CALL(this->Value(), cb, 1, argv); called = true; } @@ -883,8 +906,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(this->Value(), 2, info); } } else while (!queue.empty()) { diff --git a/src/statement.h b/src/statement.h index 90d295b70..87cdce28e 100644 --- a/src/statement.h +++ b/src/statement.h @@ -12,10 +12,11 @@ #include #include -#include +#include +#include -using namespace v8; -using namespace node; +using namespace Napi; +using namespace Napi; namespace node_sqlite3 { @@ -71,23 +72,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); struct Baton { uv_work_t 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 +100,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 +121,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 +134,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(); } @@ -166,8 +166,8 @@ 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) { @@ -185,14 +185,16 @@ class Statement : public Nan::ObjectWrap { } }; - Statement(Database* db_) : Nan::ObjectWrap(), - db(db_), - _handle(NULL), - status(SQLITE_OK), - prepared(false), - locked(true), - finalized(false) { + Statement(const Napi::CallbackInfo& info); + + void init(Database* db_) { + db = db_; db->Ref(); + _handle = NULL; + status = SQLITE_OK; + prepared = false; + locked = true; + finalized = false; } ~Statement() { @@ -206,7 +208,7 @@ 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); @@ -219,12 +221,12 @@ class Statement : public Nan::ObjectWrap { 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/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) {