From de8888ef5ff76a42ceaf75f298fe127d4be4d673 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 30 Sep 2019 21:23:59 +0400 Subject: [PATCH 01/26] Convert NAN to N-API using tool --- binding.gyp | 28 ++++- package.json | 2 +- src/backup.cc | 185 +++++++++++++++------------- src/backup.h | 45 +++---- src/database.cc | 236 +++++++++++++++++------------------ src/database.h | 56 +++++---- src/macros.h | 101 ++++++++------- src/node_sqlite3.cc | 12 +- src/statement.cc | 292 +++++++++++++++++++++++--------------------- src/statement.h | 45 +++---- 10 files changed, 529 insertions(+), 473 deletions(-) diff --git a/binding.gyp b/binding.gyp index 019df41e7..536a0309b 100644 --- a/binding.gyp +++ b/binding.gyp @@ -7,10 +7,21 @@ "targets": [ { "target_name": "<(module_name)", - "include_dirs": [" -#include +#include +#include #include #include @@ -9,19 +10,19 @@ 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::FunctionReference t = Napi::Function::New(env, New); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Backup").ToLocalChecked()); - Nan::SetPrototypeMethod(t, "step", Step); - Nan::SetPrototypeMethod(t, "finish", Finish); + t->SetClassName(Napi::String::New(env, "Backup")); + + InstanceMethod("step", &Step), + InstanceMethod("finish", &Finish), NODE_SET_GETTER(t, "idle", IdleGetter); NODE_SET_GETTER(t, "completed", CompletedGetter); @@ -31,9 +32,9 @@ NAN_MODULE_INIT(Backup::Init) { NODE_SET_SETTER(t, "retryErrors", RetryErrorGetter, RetryErrorSetter); - constructor_template.Reset(t); - Nan::Set(target, Nan::New("Backup").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + constructor.Reset(t); + (target).Set(Napi::String::New(env, "Backup"), + Napi::GetFunction(t)); } void Backup::Process() { @@ -64,33 +65,33 @@ void Backup::Schedule(Work_Callback callback, Baton* baton) { } template void Backup::Error(T* baton) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); Backup* backup = baton->backup; // Fail hard on logic errors. assert(backup->status != 0); EXCEPTION(backup->message, backup->status, exception); - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; + Napi::Value argv[] = { exception }; TRY_CATCH_CALL(backup->handle(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(backup->handle(), 2, argv); } } void Backup::CleanQueue() { - Nan::HandleScope scope; + 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 }; + Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. @@ -98,7 +99,7 @@ void Backup::CleanQueue() { Call* call = queue.front(); queue.pop(); - Local cb = Nan::New(call->baton->callback); + Napi::Function cb = Napi::New(env, call->baton->callback); if (inited && !cb.IsEmpty() && cb->IsFunction()) { @@ -115,7 +116,7 @@ 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 }; + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(handle(), 2, info); } } @@ -132,54 +133,61 @@ void Backup::CleanQueue() { } } -NAN_METHOD(Backup::New) { +Napi::Value Backup::New(const Napi::CallbackInfo& info) { 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 env.Null(); } 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 env.Null(); } - 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 env.Null(); } - 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 env.Null(); } - 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 env.Null(); } - 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 env.Null(); } - 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 env.Null(); } - 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]); + Database* db = info[0].As().Unwrap(); + Napi::String filename = info[1].As(); + Napi::String sourceName = info[2].As(); + Napi::String destName = info[3].As(); + Napi::Boolean filenameIsDest = info[4].As(); - 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); + info.This().DefineProperty(Napi::String::New(env, "filename"), filename, ReadOnly); + info.This().DefineProperty(Napi::String::New(env, "sourceName"), sourceName, ReadOnly); + info.This().DefineProperty(Napi::String::New(env, "destName"), destName, ReadOnly); + info.This().DefineProperty(Napi::String::New(env, "filenameIsDest"), 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(); + InitializeBaton* baton = new InitializeBaton(db, info[5].As(), backup); + baton->filename = std::string(filename->As().Utf8Value().c_str()); + baton->sourceName = std::string(sourceName->As().Utf8Value().c_str()); + baton->destName = std::string(destName->As().Utf8Value().c_str()); + baton->filenameIsDest = filenameIsDest.As().Value(); db->Schedule(Work_BeginInitialize, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Backup::Work_BeginInitialize(Database::Baton* baton) { @@ -220,7 +228,7 @@ void Backup::Work_Initialize(uv_work_t* req) { } void Backup::Work_AfterInitialize(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); BACKUP_INIT(InitializeBaton); @@ -230,17 +238,17 @@ void Backup::Work_AfterInitialize(uv_work_t* req) { } else { backup->inited = true; - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(backup->handle(), 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; REQUIRE_ARGUMENT_INTEGER(0, pages); OPTIONAL_ARGUMENT_FUNCTION(1, callback); @@ -248,7 +256,7 @@ 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) { @@ -280,7 +288,7 @@ void Backup::Work_Step(uv_work_t* req) { } void Backup::Work_AfterStep(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); BACKUP_INIT(StepBaton); @@ -295,9 +303,9 @@ void Backup::Work_AfterStep(uv_work_t* req) { } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null(), Nan::New(backup->status == SQLITE_DONE) }; + Napi::Value argv[] = { env.Null(), Napi::New(env, backup->status == SQLITE_DONE) }; TRY_CATCH_CALL(backup->handle(), cb, 2, argv); } } @@ -305,14 +313,14 @@ void Backup::Work_AfterStep(uv_work_t* req) { BACKUP_END(); } -NAN_METHOD(Backup::Finish) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Backup::Finish(const Napi::CallbackInfo& info) { + Backup* backup = this; 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) { @@ -325,13 +333,13 @@ void Backup::Work_Finish(uv_work_t* req) { } void Backup::Work_AfterFinish(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); BACKUP_INIT(Baton); backup->FinishAll(); // Fire callback in case there was one. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { TRY_CATCH_CALL(backup->handle(), cb, 0, NULL); } @@ -362,54 +370,55 @@ 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 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 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 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 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 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 Napi::New(env, backup->retryErrors); } -NAN_SETTER(Backup::RetryErrorSetter) { - Backup* backup = Nan::ObjectWrap::Unwrap(info.This()); +void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value) { + Backup* backup = this; if (!value->IsArray()) { - return Nan::ThrowError("retryErrors must be an array"); + Napi::Error::New(env, "retryErrors must be an array").ThrowAsJavaScriptException(); + return env.Null(); } - Local array = Local::Cast(value); + Napi::Array array = value.As(); backup->retryErrors.Reset(array); } void Backup::GetRetryErrors(std::set& retryErrorsSet) { retryErrorsSet.clear(); - Local array = Nan::New(retryErrors); + Napi::Array array = Napi::New(env, retryErrors); 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..8aa39b9f5 100644 --- a/src/backup.h +++ b/src/backup.h @@ -8,10 +8,11 @@ #include #include -#include +#include +#include -using namespace v8; -using namespace node; +using namespace Napi; +using namespace Napi; namespace node_sqlite3 { @@ -92,19 +93,19 @@ 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); + static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { uv_work_t 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_); @@ -121,7 +122,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 +138,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,7 +150,7 @@ class Backup : public Nan::ObjectWrap { Baton* baton; }; - Backup(Database* db_) : Nan::ObjectWrap(), + Backup(Database* db_) : Napi::ObjectWrap(), db(db_), _handle(NULL), _otherDb(NULL), @@ -173,16 +174,16 @@ 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); @@ -215,7 +216,7 @@ class Backup : public Nan::ObjectWrap { bool finished; std::queue queue; - Nan::Persistent retryErrors; + Napi::Persistent retryErrors; }; } diff --git a/src/database.cc b/src/database.cc index ace5fa0b0..d5ccb6dd3 100644 --- a/src/database.cc +++ b/src/database.cc @@ -6,45 +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::FunctionReference t = Napi::Function::New(env, New); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Database").ToLocalChecked()); - 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); + t->SetClassName(Napi::String::New(env, "Database")); + + InstanceMethod("close", &Close), + InstanceMethod("exec", &Exec), + InstanceMethod("wait", &Wait), + InstanceMethod("loadExtension", &LoadExtension), + InstanceMethod("serialize", &Serialize), + InstanceMethod("parallelize", &Parallelize), + InstanceMethod("configure", &Configure), + InstanceMethod("interrupt", &Interrupt), NODE_SET_GETTER(t, "open", OpenGetter); - constructor_template.Reset(t); + constructor.Reset(t); - Nan::Set(target, Nan::New("Database").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + (target).Set(Napi::String::New(env, "Database"), + Napi::GetFunction(t)); } void Database::Process() { - Nan::HandleScope scope; + Napi::HandleScope scope(env); if (!open && locked && !queue.empty()) { EXCEPTION("Database handle is closed", SQLITE_MISUSE, exception); - Local argv[] = { 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); + Napi::Function cb = Napi::New(env, call->baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { TRY_CATCH_CALL(this->handle(), cb, 1, argv); called = true; @@ -59,7 +59,7 @@ 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 }; + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(handle(), 2, info); } return; @@ -82,17 +82,17 @@ void Database::Process() { } void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); if (!open && locked) { EXCEPTION("Database is closed", SQLITE_MISUSE, exception); - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; + Napi::Value argv[] = { exception }; TRY_CATCH_CALL(handle(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(handle(), 2, argv); } return; @@ -107,37 +107,38 @@ void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { } } -NAN_METHOD(Database::New) { +Napi::Value Database::New(const Napi::CallbackInfo& info) { if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Database objects"); + Napi::TypeError::New(env, "Use the new operator to create new Database objects").ThrowAsJavaScriptException(); + return env.Null(); } REQUIRE_ARGUMENT_STRING(0, filename); 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().DefineProperty(Napi::String::New(env, "filename"), info[0].As(), ReadOnly); + info.This().DefineProperty(Napi::String::New(env, "mode"), Napi::New(env, mode), ReadOnly); // Start opening the database. OpenBaton* baton = new OpenBaton(db, callback, *filename, mode); Work_BeginOpen(baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginOpen(Baton* baton) { @@ -169,33 +170,33 @@ void Database::Work_Open(uv_work_t* req) { } void Database::Work_AfterOpen(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); OpenBaton* baton = static_cast(req->data); Database* db = baton->db; - Local argv[1]; + Napi::Value argv[1]; if (baton->status != SQLITE_OK) { EXCEPTION(baton->message, 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 = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { TRY_CATCH_CALL(db->handle(), cb, 1, argv); } else if (!db->open) { - Local info[] = { Nan::New("error").ToLocalChecked(), argv[0] }; + Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; EMIT_EVENT(db->handle(), 2, info); } if (db->open) { - Local info[] = { Nan::New("open").ToLocalChecked() }; + Napi::Value info[] = { Napi::String::New(env, "open") }; EMIT_EVENT(db->handle(), 1, info); db->Process(); } @@ -203,19 +204,19 @@ void Database::Work_AfterOpen(uv_work_t* req) { 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) { + Database* db = this; + return db->open; } -NAN_METHOD(Database::Close) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Close(const Napi::CallbackInfo& info) { + 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,14 +248,14 @@ void Database::Work_Close(uv_work_t* req) { } void Database::Work_AfterClose(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); Baton* baton = static_cast(req->data); Database* db = baton->db; db->closing = false; - Local argv[1]; + Napi::Value argv[1]; if (baton->status != SQLITE_OK) { EXCEPTION(baton->message, baton->status, exception); argv[0] = exception; @@ -263,22 +264,22 @@ void Database::Work_AfterClose(uv_work_t* req) { 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 = Napi::New(env, baton->callback); // Fire callbacks. if (!cb.IsEmpty() && cb->IsFunction()) { TRY_CATCH_CALL(db->handle(), cb, 1, argv); } else if (db->open) { - Local info[] = { Nan::New("error").ToLocalChecked(), argv[0] }; + Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; EMIT_EVENT(db->handle(), 2, info); } if (!db->open) { - Local info[] = { Nan::New("close").ToLocalChecked(), argv[0] }; + Napi::Value info[] = { Napi::String::New(env, "close"), argv[0] }; EMIT_EVENT(db->handle(), 1, info); db->Process(); } @@ -286,8 +287,8 @@ void Database::Work_AfterClose(uv_work_t* req) { delete baton; } -NAN_METHOD(Database::Serialize) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; @@ -300,11 +301,11 @@ NAN_METHOD(Database::Serialize) { 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) { + Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); bool before = db->serialize; @@ -317,61 +318,64 @@ NAN_METHOD(Database::Parallelize) { 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) { + 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( + return Napi::ThrowError(Exception::Error(String::Concat( #if V8_MAJOR_VERSION > 6 info.GetIsolate(), #endif - Nan::To(info[0]).ToLocalChecked(), - Nan::New(" is not a valid configuration option").ToLocalChecked() + info[0].To(), + Napi::String::New(env, " is not a valid configuration option") ))); } 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) { + 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,11 +416,11 @@ 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::HandleScope scope(env); - Local argv[] = { - Nan::New("trace").ToLocalChecked(), - Nan::New(sql->c_str()).ToLocalChecked() + Napi::Value argv[] = { + Napi::String::New(env, "trace"), + Napi::New(env, sql->c_str()) }; EMIT_EVENT(db->handle(), 2, argv); delete sql; @@ -452,12 +456,12 @@ void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) } void Database::ProfileCallback(Database *db, ProfileInfo* info) { - Nan::HandleScope scope; + 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::New(env, info->sql.c_str()), + Napi::Number::New(env, (double)info->nsecs / 1000000.0) }; EMIT_EVENT(db->handle(), 3, argv); delete info; @@ -496,20 +500,20 @@ void Database::UpdateCallback(void* db, int type, const char* database, } void Database::UpdateCallback(Database *db, UpdateInfo* info) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); - 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::Value argv[] = { + Napi::New(env, sqlite_authorizer_string(info->type)), + Napi::New(env, info->database.c_str()), + Napi::New(env, info->table.c_str()), + Napi::Number::New(env, info->rowid), }; EMIT_EVENT(db->handle(), 4, argv); delete info; } -NAN_METHOD(Database::Exec) { - Database* db = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Database::Exec(const Napi::CallbackInfo& info) { + Database* db = this; REQUIRE_ARGUMENT_STRING(0, sql); OPTIONAL_ARGUMENT_FUNCTION(1, callback); @@ -517,7 +521,7 @@ NAN_METHOD(Database::Exec) { Baton* baton = new ExecBaton(db, callback, *sql); db->Schedule(Work_BeginExec, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginExec(Baton* baton) { @@ -549,27 +553,27 @@ void Database::Work_Exec(uv_work_t* req) { } void Database::Work_AfterExec(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); ExecBaton* baton = static_cast(req->data); Database* db = baton->db; - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (baton->status != SQLITE_OK) { EXCEPTION(baton->message, baton->status, exception); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; + Napi::Value argv[] = { exception }; TRY_CATCH_CALL(db->handle(), cb, 1, argv); } else { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(db->handle(), 2, info); } } else if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(db->handle(), cb, 1, argv); } @@ -578,28 +582,28 @@ 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) { + 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::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); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(baton->db->handle(), cb, 1, argv); } @@ -608,8 +612,8 @@ 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) { + Database* db = this; REQUIRE_ARGUMENT_STRING(0, filename); OPTIONAL_ARGUMENT_FUNCTION(1, callback); @@ -617,7 +621,7 @@ NAN_METHOD(Database::LoadExtension) { Baton* baton = new LoadExtensionBaton(db, callback, *filename); db->Schedule(Work_BeginLoadExtension, baton, true); - info.GetReturnValue().Set(info.This()); + return info.This(); } void Database::Work_BeginLoadExtension(Baton* baton) { @@ -652,26 +656,26 @@ void Database::Work_LoadExtension(uv_work_t* req) { } void Database::Work_AfterLoadExtension(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); LoadExtensionBaton* baton = static_cast(req->data); Database* db = baton->db; - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (baton->status != SQLITE_OK) { EXCEPTION(baton->message, baton->status, exception); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; + Napi::Value argv[] = { exception }; TRY_CATCH_CALL(db->handle(), cb, 1, argv); } else { - Local info[] = { Nan::New("error").ToLocalChecked(), exception }; + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(db->handle(), 2, info); } } else if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(db->handle(), cb, 1, argv); } diff --git a/src/database.h b/src/database.h index 1ec101548..5b2c96e58 100644 --- a/src/database.h +++ b/src/database.h @@ -7,37 +7,39 @@ #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 Napi::New(env, constructor)->HasInstance(obj); } 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; @@ -52,19 +54,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_) {} }; @@ -101,7 +103,7 @@ class Database : public Nan::ObjectWrap { friend class Backup; protected: - Database() : Nan::ObjectWrap(), + Database() : Napi::ObjectWrap(), _handle(NULL), open(false), closing(false), @@ -120,40 +122,40 @@ class Database : public Nan::ObjectWrap { open = false; } - static NAN_METHOD(New); + static Napi::Value New(const Napi::CallbackInfo& info); 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); + static 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); + static Napi::Value Wait(const Napi::CallbackInfo& info); static void Work_Wait(Baton* baton); - static NAN_METHOD(Close); + static 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); + static 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); + static Napi::Value Serialize(const Napi::CallbackInfo& info); + static Napi::Value Parallelize(const Napi::CallbackInfo& info); - static NAN_METHOD(Configure); + static Napi::Value Configure(const Napi::CallbackInfo& info); - static NAN_METHOD(Interrupt); + static Napi::Value Interrupt(const Napi::CallbackInfo& info); static void SetBusyTimeout(Baton* baton); diff --git a/src/macros.h b/src/macros.h index 9c0136cc5..254775cf2 100644 --- a/src/macros.h +++ b/src/macros.h @@ -7,43 +7,49 @@ const char* sqlite_authorizer_string(int type); #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,67 +58,68 @@ 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(); + return env.Null(); \ } #define DEFINE_CONSTANT_INTEGER(target, constant, name) \ - Nan::ForceSet(target, \ - Nan::New(#name).ToLocalChecked(), \ - Nan::New(constant), \ - static_cast(ReadOnly | DontDelete) \ + target->DefineProperty( \ + Napi::New(env, #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) \ + target->DefineProperty( \ + Napi::New(env, #name), \ + Napi::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)); + Napi::SetAccessor((target)->InstanceTemplate(), \ + Napi::New(env, name), (function)); #define NODE_SET_SETTER(target, name, getter, setter) \ - Nan::SetAccessor((target)->InstanceTemplate(), \ - Nan::New(name).ToLocalChecked(), getter, setter); + Napi::SetAccessor((target)->InstanceTemplate(), \ + Napi::New(env, name), getter, setter); #define GET_STRING(source, name, property) \ - Nan::Utf8String name(Nan::Get(source, \ - Nan::New(prop).ToLocalChecked()).ToLocalChecked()); + std::string name = (source).Get(\ + Napi::New(env, prop.As())); #define GET_INTEGER(source, name, prop) \ - int name = Nan::To(Nan::Get(source, \ - Nan::New(property).ToLocalChecked()).ToLocalChecked()).FromJust(); + int name = Napi::To((source).Get(\ + Napi::New(env, property))); #define EXCEPTION(msg, errno, name) \ - Local name = Exception::Error(Nan::New( \ + Napi::Value name = Exception::Error(Napi::New(env, \ 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::Object name ##_obj = name.As(); \ + (name ##_obj).Set(Napi::String::New(env, "errno"), Napi::New(env, errno));\ + (name ##_obj).Set(Napi::String::New(env, "code"), \ + Napi::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(\ + Napi::String::New(env, "emit")).As(),\ argc, argv \ ); #define TRY_CATCH_CALL(context, callback, argc, argv) \ - Nan::MakeCallback((context), (callback), (argc), (argv)) + (callback).MakeCallback((context), (argc), (argv)) #define WORK_DEFINITION(name) \ - static NAN_METHOD(name); \ + static 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); diff --git a/src/node_sqlite3.cc b/src/node_sqlite3.cc index 10f88ea68..e43f95875 100644 --- a/src/node_sqlite3.cc +++ b/src/node_sqlite3.cc @@ -13,12 +13,12 @@ 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, target, module); + Statement::Init(env, target, module); + Backup::Init(env, target, module); DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READONLY, OPEN_READONLY); DEFINE_CONSTANT_INTEGER(target, SQLITE_OPEN_READWRITE, OPEN_READWRITE); @@ -108,4 +108,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..cf35cd64c 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -1,5 +1,6 @@ #include -#include +#include +#include #include #include @@ -9,27 +10,27 @@ 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::FunctionReference t = Napi::Function::New(env, New); - t->InstanceTemplate()->SetInternalFieldCount(1); - t->SetClassName(Nan::New("Statement").ToLocalChecked()); - 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); + t->SetClassName(Napi::String::New(env, "Statement")); - constructor_template.Reset(t); - Nan::Set(target, Nan::New("Statement").ToLocalChecked(), - Nan::GetFunction(t).ToLocalChecked()); + InstanceMethod("bind", &Bind), + InstanceMethod("get", &Get), + InstanceMethod("run", &Run), + InstanceMethod("all", &All), + InstanceMethod("each", &Each), + InstanceMethod("reset", &Reset), + InstanceMethod("finalize", &Finalize), + + constructor.Reset(t); + (target).Set(Napi::String::New(env, "Statement"), + Napi::GetFunction(t)); } void Statement::Process() { @@ -60,56 +61,60 @@ void Statement::Schedule(Work_Callback callback, Baton* baton) { } template void Statement::Error(T* baton) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); Statement* stmt = baton->stmt; // Fail hard on logic errors. assert(stmt->status != 0); EXCEPTION(stmt->message, stmt->status, exception); - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { exception }; + Napi::Value argv[] = { exception }; TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); } else { - Local argv[] = { Nan::New("error").ToLocalChecked(), exception }; + Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(stmt->handle(), 2, argv); } } // { Database db, String sql, Array params, Function callback } -NAN_METHOD(Statement::New) { +Napi::Value Statement::New(const Napi::CallbackInfo& info) { if (!info.IsConstructCall()) { - return Nan::ThrowTypeError("Use the new operator to create new Statement objects"); + Napi::TypeError::New(env, "Use the new operator to create new Statement objects").ThrowAsJavaScriptException(); + return env.Null(); } 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 env.Null(); } - 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 env.Null(); } - 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 env.Null(); } - Database* db = Nan::ObjectWrap::Unwrap(info[0].As()); - Local sql = Local::Cast(info[1]); + Database* db = info[0].As().Unwrap(); + Napi::String sql = info[1].As(); - Nan::ForceSet(info.This(),Nan::New("sql").ToLocalChecked(), sql, ReadOnly); + info.This().DefineProperty(Napi::String::New(env, "sql"), sql, ReadOnly); Statement* stmt = new Statement(db); stmt->Wrap(info.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()); + return info.This(); } void Statement::Work_BeginPrepare(Database::Baton* baton) { @@ -145,7 +150,7 @@ void Statement::Work_Prepare(uv_work_t* req) { } void Statement::Work_AfterPrepare(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(PrepareBaton); @@ -155,9 +160,9 @@ void Statement::Work_AfterPrepare(uv_work_t* req) { } else { stmt->prepared = true; - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); } } @@ -166,77 +171,77 @@ 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(); + 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::Integer(pos, source.As().Int32Value()); } - else if (source->IsNumber()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + else if (source.IsNumber()) { + return new Values::Float(pos, source.As().DoubleValue()); } else if (source->IsBoolean()) { - return new Values::Integer(pos, Nan::To(source).FromJust() ? 1 : 0); + return new Values::Integer(pos, source.As().Value() ? 1 : 0); } else if (source->IsNull()) { return new Values::Null(pos); } - else if (Buffer::HasInstance(source)) { - Local buffer = Nan::To(source).ToLocalChecked(); + else if (source.IsBuffer()) { + Napi::Object buffer = source.To(); return new Values::Blob(pos, Buffer::Length(buffer), Buffer::Data(buffer)); } else if (source->IsDate()) { - return new Values::Float(pos, Nan::To(source).FromJust()); + return new Values::Float(pos, source.As().DoubleValue()); } 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::HandleScope scope(env); if (last < 0) last = info.Length(); - Local callback; + Napi::Function callback; if (last > start && info[last - 1]->IsFunction()) { - callback = Local::Cast(info[last - 1]); + 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]); + 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(); + else if (info[start].IsObject()) { + Napi::Object object = info[start].As(); + Napi::Array array = Napi::GetPropertyNames(object); 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 +310,17 @@ 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) { + 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,7 +338,7 @@ void Statement::Work_Bind(uv_work_t* req) { } void Statement::Work_AfterBind(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(Baton); @@ -341,9 +347,9 @@ void Statement::Work_AfterBind(uv_work_t* req) { } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); } } @@ -353,16 +359,17 @@ 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) { + 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,7 +402,7 @@ void Statement::Work_Get(uv_work_t* req) { } void Statement::Work_AfterGet(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(RowBaton); @@ -404,15 +411,15 @@ void Statement::Work_AfterGet(uv_work_t* req) { } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { if (stmt->status == SQLITE_ROW) { // Create the result array from the data we acquired. - Local argv[] = { Nan::Null(), RowToJS(&baton->row) }; + Napi::Value argv[] = { env.Null(), RowToJS(&baton->row) }; TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); } else { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); } } @@ -421,16 +428,17 @@ 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) { + 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,7 +473,7 @@ void Statement::Work_Run(uv_work_t* req) { } void Statement::Work_AfterRun(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(RunBaton); @@ -474,12 +482,12 @@ void Statement::Work_AfterRun(uv_work_t* req) { } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, 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)); + (stmt->handle()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id)); + (stmt->handle()).Set(Napi::String::New(env, "changes"), Napi::New(env, baton->changes)); - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); } } @@ -487,16 +495,17 @@ void Statement::Work_AfterRun(uv_work_t* req) { STATEMENT_END(); } -NAN_METHOD(Statement::All) { - Statement* stmt = Nan::ObjectWrap::Unwrap(info.This()); +Napi::Value Statement::All(const Napi::CallbackInfo& info) { + 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,7 +540,7 @@ void Statement::Work_All(uv_work_t* req) { } void Statement::Work_AfterAll(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(RowsBaton); @@ -540,26 +549,26 @@ void Statement::Work_AfterAll(uv_work_t* req) { } else { // Fire callbacks. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && 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(*it)); delete *it; } - Local argv[] = { Nan::Null(), result }; + Napi::Value argv[] = { env.Null(), result }; TRY_CATCH_CALL(stmt->handle(), 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); } @@ -569,24 +578,25 @@ 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) { + Statement* stmt = this; int last = info.Length(); - Local completed; + Napi::Function completed; if (last >= 2 && info[last - 1]->IsFunction() && info[last - 2]->IsFunction()) { - completed = Local::Cast(info[--last]); + 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); stmt->Schedule(Work_BeginEach, baton); - info.GetReturnValue().Set(info.This()); + return info.This(); } } @@ -652,7 +662,7 @@ void Statement::CloseCallback(uv_handle_t* handle) { } void Statement::AsyncEach(uv_async_t* handle, int status) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); Async* async = static_cast(handle->data); @@ -667,10 +677,10 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { break; } - Local cb = Nan::New(async->item_cb); + Napi::Function cb = Napi::New(env, async->item_cb); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[2]; - argv[0] = Nan::Null(); + Napi::Value argv[2]; + argv[0] = env.Null(); Rows::const_iterator it = rows.begin(); Rows::const_iterator end = rows.end(); @@ -683,13 +693,13 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { } } - Local cb = Nan::New(async->completed_cb); + Napi::Function cb = Napi::New(env, async->completed_cb); if (async->completed) { if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { - Nan::Null(), - Nan::New(async->retrieved) + Napi::Value argv[] = { + env.Null(), + Napi::New(env, async->retrieved) }; TRY_CATCH_CALL(async->stmt->handle(), cb, 2, argv); } @@ -698,7 +708,7 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { } void Statement::Work_AfterEach(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(EachBaton); @@ -709,15 +719,15 @@ 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) { + 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 +742,51 @@ void Statement::Work_Reset(uv_work_t* req) { } void Statement::Work_AfterReset(uv_work_t* req) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); STATEMENT_INIT(Baton); // Fire callbacks. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { - Local argv[] = { Nan::Null() }; + Napi::Value argv[] = { env.Null() }; TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); } STATEMENT_END(); } -Local Statement::RowToJS(Row* row) { - Nan::EscapableHandleScope scope; +Napi::Object Statement::RowToJS(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::New(env, field->name.c_str()), value); DELETE_FIELD(field); } @@ -816,23 +826,23 @@ 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) { + 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->handle(); } void Statement::Finalize(Baton* baton) { - Nan::HandleScope scope; + Napi::HandleScope scope(env); baton->stmt->Finalize(); // Fire callback in case there was one. - Local cb = Nan::New(baton->callback); + Napi::Function cb = Napi::New(env, baton->callback); if (!cb.IsEmpty() && cb->IsFunction()) { TRY_CATCH_CALL(baton->stmt->handle(), cb, 0, NULL); } @@ -852,13 +862,13 @@ void Statement::Finalize() { } void Statement::CleanQueue() { - Nan::HandleScope scope; + 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 }; + Napi::Value argv[] = { exception }; bool called = false; // Clear out the queue so that this object can get GC'ed. @@ -866,7 +876,7 @@ void Statement::CleanQueue() { Call* call = queue.front(); queue.pop(); - Local cb = Nan::New(call->baton->callback); + Napi::Function cb = Napi::New(env, call->baton->callback); if (prepared && !cb.IsEmpty() && cb->IsFunction()) { @@ -883,7 +893,7 @@ 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 }; + Napi::Value info[] = { Napi::String::New(env, "error"), exception }; EMIT_EVENT(handle(), 2, info); } } diff --git a/src/statement.h b/src/statement.h index 90d295b70..9dbcfb5b9 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,20 +72,20 @@ 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; 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_); @@ -100,20 +101,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 +122,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 +135,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 +167,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,7 +186,7 @@ class Statement : public Nan::ObjectWrap { } }; - Statement(Database* db_) : Nan::ObjectWrap(), + Statement(Database* db_) : Napi::ObjectWrap(), db(db_), _handle(NULL), status(SQLITE_OK), @@ -206,7 +207,7 @@ class Statement : public Nan::ObjectWrap { WORK_DEFINITION(Each); WORK_DEFINITION(Reset); - static NAN_METHOD(Finalize); + static Napi::Value Finalize(const Napi::CallbackInfo& info); protected: static void Work_BeginPrepare(Database::Baton* baton); @@ -219,12 +220,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::Object RowToJS(Row* row); void Schedule(Work_Callback callback, Baton* baton); void Process(); void CleanQueue(); From fc09ced95c81bd66e5a961f7d07c43c7693b13d1 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 30 Sep 2019 21:30:01 +0400 Subject: [PATCH 02/26] Fix macros and binding.gyp --- binding.gyp | 2 +- src/macros.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/binding.gyp b/binding.gyp index 536a0309b..f43df0064 100644 --- a/binding.gyp +++ b/binding.gyp @@ -17,7 +17,7 @@ "VCCLCompilerTool": { "ExceptionHandling": 1 }, }, "include_dirs": [ - "(); @@ -22,7 +22,7 @@ const char* sqlite_authorizer_string(int type); #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ if (info.Length() <= (i) || !info[i].IsFunction()) { \ - Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ Napi::Function var = info[i].As(); @@ -30,14 +30,14 @@ const char* sqlite_authorizer_string(int type); #define REQUIRE_ARGUMENT_STRING(i, var) \ if (info.Length() <= (i) || !info[i].IsString()) { \ - Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ std::string var = info[i].As(); #define REQUIRE_ARGUMENT_INTEGER(i, var) \ if (info.Length() <= (i) || !info[i].IsNumber()) { \ - Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ int var(info[i].As().Int32Value()); @@ -46,7 +46,7 @@ const char* sqlite_authorizer_string(int type); 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(); + Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ return env.Null(); \ } \ var = info[i].As(); \ @@ -62,7 +62,7 @@ const char* sqlite_authorizer_string(int type); var = info[i].As().Int32Value(); \ } \ else { \ - Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); + Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ return env.Null(); \ } From dd3ef522088bb5cafede25b9fe661f892b6f10ba Mon Sep 17 00:00:00 2001 From: Jim Schlight Date: Mon, 8 Jul 2019 14:55:40 -0700 Subject: [PATCH 03/26] Fix compilation --- src/backup.cc | 156 ++++++++++---------- src/backup.h | 30 ++-- src/database.cc | 206 +++++++++++++------------- src/database.h | 47 +++--- src/macros.h | 111 +++++++------- src/node_sqlite3.cc | 84 ++++++----- src/statement.cc | 290 +++++++++++++++++++++---------------- src/statement.h | 29 ++-- test/database_fail.test.js | 4 +- 9 files changed, 515 insertions(+), 442 deletions(-) diff --git a/src/backup.cc b/src/backup.cc index de34d61c4..bb2a9e465 100644 --- a/src/backup.cc +++ b/src/backup.cc @@ -16,25 +16,22 @@ Napi::FunctionReference Backup::constructor; Napi::Object Backup::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); - Napi::FunctionReference t = Napi::Function::New(env, New); - - - t->SetClassName(Napi::String::New(env, "Backup")); - - InstanceMethod("step", &Step), - InstanceMethod("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.Reset(t); - (target).Set(Napi::String::New(env, "Backup"), - Napi::GetFunction(t)); + 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), + }); + + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); + + exports.Set("Backup", t); + return exports; } void Backup::Process() { @@ -65,32 +62,34 @@ void Backup::Schedule(Work_Callback callback, Baton* baton) { } template void Backup::Error(T* baton) { + 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); - Napi::Function cb = Napi::New(env, baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { + if (!cb.IsEmpty() && cb.IsFunction()) { Napi::Value argv[] = { exception }; - TRY_CATCH_CALL(backup->handle(), cb, 1, argv); + TRY_CATCH_CALL(backup->Value(), cb, 1, argv); } else { Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(backup->handle(), 2, argv); + EMIT_EVENT(backup->Value(), 2, argv); } } void Backup::CleanQueue() { + 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); + EXCEPTION(Napi::String::New(env, "Backup is already finished"), SQLITE_MISUSE, exception); Napi::Value argv[] = { exception }; bool called = false; @@ -99,11 +98,11 @@ void Backup::CleanQueue() { Call* call = queue.front(); queue.pop(); - Napi::Function cb = Napi::New(env, 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; } @@ -117,7 +116,7 @@ void Backup::CleanQueue() { // Backup object. if (!called) { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(handle(), 2, info); + EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { @@ -133,61 +132,59 @@ void Backup::CleanQueue() { } } -Napi::Value Backup::New(const Napi::CallbackInfo& info) { +Backup::Backup(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) { + Napi::Env env = info.Env(); if (!info.IsConstructCall()) { Napi::TypeError::New(env, "Use the new operator to create new Backup objects").ThrowAsJavaScriptException(); - return env.Null(); + return; } int length = info.Length(); if (length <= 0 || !Database::HasInstance(info[0])) { Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length <= 1 || !info[1].IsString()) { Napi::TypeError::New(env, "Filename expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length <= 2 || !info[2].IsString()) { Napi::TypeError::New(env, "Source database name expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length <= 3 || !info[3].IsString()) { Napi::TypeError::New(env, "Destination database name expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length <= 4 || !info[4].IsBoolean()) { Napi::TypeError::New(env, "Direction flag expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length > 5 && !info[5].IsUndefined() && !info[5].IsFunction()) { Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } - Database* db = info[0].As().Unwrap(); + 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.This().DefineProperty(Napi::String::New(env, "filename"), filename, ReadOnly); - info.This().DefineProperty(Napi::String::New(env, "sourceName"), sourceName, ReadOnly); - info.This().DefineProperty(Napi::String::New(env, "destName"), destName, ReadOnly); - info.This().DefineProperty(Napi::String::New(env, "filenameIsDest"), filenameIsDest, ReadOnly); + 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)); - Backup* backup = new Backup(db); - backup->Wrap(info.This()); + init(db); - InitializeBaton* baton = new InitializeBaton(db, info[5].As(), backup); - baton->filename = std::string(filename->As().Utf8Value().c_str()); - baton->sourceName = std::string(sourceName->As().Utf8Value().c_str()); - baton->destName = std::string(destName->As().Utf8Value().c_str()); - baton->filenameIsDest = filenameIsDest.As().Value(); + 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); - - return info.This(); } void Backup::Work_BeginInitialize(Database::Baton* baton) { @@ -228,20 +225,21 @@ void Backup::Work_Initialize(uv_work_t* req) { } void Backup::Work_AfterInitialize(uv_work_t* req) { - Napi::HandleScope scope(env); - 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; - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsEmpty() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(backup->handle(), cb, 1, argv); + TRY_CATCH_CALL(backup->Value(), cb, 1, argv); } } BACKUP_END(); @@ -249,6 +247,7 @@ void Backup::Work_AfterInitialize(uv_work_t* req) { 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); @@ -288,10 +287,11 @@ void Backup::Work_Step(uv_work_t* req) { } void Backup::Work_AfterStep(uv_work_t* req) { - Napi::HandleScope scope(env); - BACKUP_INIT(StepBaton); + Napi::Env env = backup->Env(); + Napi::HandleScope scope(env); + if (backup->status == SQLITE_DONE) { backup->completed = true; } else if (!backup->_handle) { @@ -303,10 +303,10 @@ void Backup::Work_AfterStep(uv_work_t* req) { } else { // Fire callbacks. - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - Napi::Value argv[] = { env.Null(), Napi::New(env, 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); } } @@ -315,6 +315,7 @@ void Backup::Work_AfterStep(uv_work_t* req) { Napi::Value Backup::Finish(const Napi::CallbackInfo& info) { Backup* backup = this; + Napi::Env env = backup->Env(); OPTIONAL_ARGUMENT_FUNCTION(0, callback); @@ -333,15 +334,17 @@ void Backup::Work_Finish(uv_work_t* req) { } void Backup::Work_AfterFinish(uv_work_t* req) { + BACKUP_INIT(Baton); + + Napi::Env env = backup->Env(); Napi::HandleScope scope(env); - BACKUP_INIT(Baton); backup->FinishAll(); // Fire callback in case there was one. - Napi::Function cb = Napi::New(env, 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(); @@ -373,39 +376,40 @@ void Backup::FinishSqlite() { Napi::Value Backup::IdleGetter(const Napi::CallbackInfo& info) { Backup* backup = this; bool idle = backup->inited && !backup->locked && backup->queue.empty(); - return idle; + return Napi::Boolean::New(this->Env(), idle); } Napi::Value Backup::CompletedGetter(const Napi::CallbackInfo& info) { Backup* backup = this; - return backup->completed; + return Napi::Boolean::New(this->Env(), backup->completed); } Napi::Value Backup::FailedGetter(const Napi::CallbackInfo& info) { Backup* backup = this; - return backup->failed; + return Napi::Boolean::New(this->Env(), backup->failed); } Napi::Value Backup::RemainingGetter(const Napi::CallbackInfo& info) { Backup* backup = this; - return backup->remaining; + return Napi::Number::New(this->Env(), backup->remaining); } Napi::Value Backup::PageCountGetter(const Napi::CallbackInfo& info) { Backup* backup = this; - return backup->pageCount; + return Napi::Number::New(this->Env(), backup->pageCount); } Napi::Value Backup::RetryErrorGetter(const Napi::CallbackInfo& info) { Backup* backup = this; - return Napi::New(env, backup->retryErrors); + return backup->retryErrors.Value(); } void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& value) { Backup* backup = this; - if (!value->IsArray()) { + Napi::Env env = backup->Env(); + if (!value.IsArray()) { Napi::Error::New(env, "retryErrors must be an array").ThrowAsJavaScriptException(); - return env.Null(); + return; } Napi::Array array = value.As(); backup->retryErrors.Reset(array); @@ -413,8 +417,8 @@ void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& void Backup::GetRetryErrors(std::set& retryErrorsSet) { retryErrorsSet.clear(); - Napi::Array array = Napi::New(env, retryErrors); - int length = array->Length(); + Napi::Array array = retryErrors.Value(); + int length = array.Length(); for (int i = 0; i < length; i++) { Napi::Value code = (array).Get(i); if (code.IsNumber()) { diff --git a/src/backup.h b/src/backup.h index 8aa39b9f5..740b5824a 100644 --- a/src/backup.h +++ b/src/backup.h @@ -11,7 +11,6 @@ #include #include -using namespace Napi; using namespace Napi; namespace node_sqlite3 { @@ -98,7 +97,6 @@ class Backup : public Napi::ObjectWrap { static Napi::FunctionReference constructor; static Napi::Object Init(Napi::Env env, Napi::Object exports); - static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { uv_work_t request; @@ -150,21 +148,23 @@ class Backup : public Napi::ObjectWrap { Baton* baton; }; - Backup(Database* db_) : Napi::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(); @@ -216,7 +216,7 @@ class Backup : public Napi::ObjectWrap { bool finished; std::queue queue; - Napi::Persistent retryErrors; + Napi::Reference retryErrors; }; } diff --git a/src/database.cc b/src/database.cc index d5ccb6dd3..4f5fbb56b 100644 --- a/src/database.cc +++ b/src/database.cc @@ -11,42 +11,40 @@ Napi::FunctionReference Database::constructor; Napi::Object Database::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); - Napi::FunctionReference t = Napi::Function::New(env, 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) + }); + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); - t->SetClassName(Napi::String::New(env, "Database")); - - InstanceMethod("close", &Close), - InstanceMethod("exec", &Exec), - InstanceMethod("wait", &Wait), - InstanceMethod("loadExtension", &LoadExtension), - InstanceMethod("serialize", &Serialize), - InstanceMethod("parallelize", &Parallelize), - InstanceMethod("configure", &Configure), - InstanceMethod("interrupt", &Interrupt), - - NODE_SET_GETTER(t, "open", OpenGetter); - - constructor.Reset(t); - - (target).Set(Napi::String::New(env, "Database"), - Napi::GetFunction(t)); + exports.Set("Database", t); + return exports; } void Database::Process() { + Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (!open && locked && !queue.empty()) { - EXCEPTION("Database handle is closed", SQLITE_MISUSE, 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(); - Napi::Function cb = Napi::New(env, 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(); @@ -60,7 +58,7 @@ void Database::Process() { // Database object. if (!called) { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(handle(), 2, info); + EMIT_EVENT(Value(), 2, info); } return; } @@ -82,18 +80,19 @@ void Database::Process() { } void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { + Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (!open && locked) { - EXCEPTION("Database is closed", SQLITE_MISUSE, exception); - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + 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(handle(), cb, 1, argv); + TRY_CATCH_CALL(Value(), cb, 1, argv); } else { Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(handle(), 2, argv); + EMIT_EVENT(Value(), 2, argv); } return; } @@ -107,19 +106,18 @@ void Database::Schedule(Work_Callback callback, Baton* baton, bool exclusive) { } } -Napi::Value Database::New(const Napi::CallbackInfo& info) { - if (!info.IsConstructCall()) { - Napi::TypeError::New(env, "Use the new operator to create new Database objects").ThrowAsJavaScriptException(); - return env.Null(); - } +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].IsNumber()) { + if (info.Length() >= pos && info[pos].IsNumber() && OtherIsInt(info[pos].As())) { mode = info[pos++].As().Int32Value(); - } else { + } + else { mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX; } @@ -128,17 +126,12 @@ Napi::Value Database::New(const Napi::CallbackInfo& info) { callback = info[pos++].As(); } - Database* db = new Database(); - db->Wrap(info.This()); - - info.This().DefineProperty(Napi::String::New(env, "filename"), info[0].As(), ReadOnly); - info.This().DefineProperty(Napi::String::New(env, "mode"), Napi::New(env, 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); - - return info.This(); } void Database::Work_BeginOpen(Baton* baton) { @@ -170,14 +163,15 @@ void Database::Work_Open(uv_work_t* req) { } void Database::Work_AfterOpen(uv_work_t* req) { - Napi::HandleScope scope(env); - OpenBaton* baton = static_cast(req->data); Database* db = baton->db; + 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 { @@ -185,19 +179,19 @@ void Database::Work_AfterOpen(uv_work_t* req) { argv[0] = env.Null(); } - Napi::Function cb = Napi::New(env, 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) { Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; - EMIT_EVENT(db->handle(), 2, info); + EMIT_EVENT(db->Value(), 2, info); } if (db->open) { Napi::Value info[] = { Napi::String::New(env, "open") }; - EMIT_EVENT(db->handle(), 1, info); + EMIT_EVENT(db->Value(), 1, info); db->Process(); } @@ -205,11 +199,13 @@ void Database::Work_AfterOpen(uv_work_t* req) { } Napi::Value Database::OpenGetter(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); Database* db = this; - return db->open; + return Napi::Boolean::New(env, db->open); } Napi::Value Database::Close(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); @@ -248,16 +244,17 @@ void Database::Work_Close(uv_work_t* req) { } void Database::Work_AfterClose(uv_work_t* req) { - Napi::HandleScope scope(env); - Baton* baton = static_cast(req->data); Database* db = baton->db; + Napi::Env env = db->Env(); + Napi::HandleScope scope(env); + db->closing = false; 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 { @@ -267,20 +264,20 @@ void Database::Work_AfterClose(uv_work_t* req) { argv[0] = env.Null(); } - Napi::Function cb = Napi::New(env, 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) { Napi::Value info[] = { Napi::String::New(env, "error"), argv[0] }; - EMIT_EVENT(db->handle(), 2, info); + EMIT_EVENT(db->Value(), 2, info); } if (!db->open) { Napi::Value info[] = { Napi::String::New(env, "close"), argv[0] }; - EMIT_EVENT(db->handle(), 1, info); + EMIT_EVENT(db->Value(), 1, info); db->Process(); } @@ -288,13 +285,14 @@ void Database::Work_AfterClose(uv_work_t* req) { } 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; } @@ -305,13 +303,14 @@ Napi::Value Database::Serialize(const Napi::CallbackInfo& info) { } 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; } @@ -322,6 +321,7 @@ Napi::Value Database::Parallelize(const Napi::CallbackInfo& info) { } Napi::Value Database::Configure(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); Database* db = this; REQUIRE_ARGUMENTS(2); @@ -347,13 +347,14 @@ Napi::Value Database::Configure(const Napi::CallbackInfo& info) { db->Schedule(SetBusyTimeout, baton); } else { - return Napi::ThrowError(Exception::Error(String::Concat( + Napi::TypeError::New(env, (StringConcat( #if V8_MAJOR_VERSION > 6 info.GetIsolate(), #endif - info[0].To(), + info[0].As(), Napi::String::New(env, " is not a valid configuration option") - ))); + )).Utf8Value().c_str()).ThrowAsJavaScriptException(); + return env.Null(); } db->Process(); @@ -362,6 +363,7 @@ Napi::Value Database::Configure(const Napi::CallbackInfo& info) { } Napi::Value Database::Interrupt(const Napi::CallbackInfo& info) { + Napi::Env env = this->Env(); Database* db = this; if (!db->open) { @@ -416,13 +418,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. + Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[] = { Napi::String::New(env, "trace"), - Napi::New(env, sql->c_str()) + Napi::String::New(env, sql->c_str()) }; - EMIT_EVENT(db->handle(), 2, argv); + EMIT_EVENT(db->Value(), 2, argv); delete sql; } @@ -456,14 +459,15 @@ void Database::ProfileCallback(void* db, const char* sql, sqlite3_uint64 nsecs) } void Database::ProfileCallback(Database *db, ProfileInfo* info) { + Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[] = { Napi::String::New(env, "profile"), - Napi::New(env, info->sql.c_str()), + 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; } @@ -500,25 +504,27 @@ void Database::UpdateCallback(void* db, int type, const char* database, } void Database::UpdateCallback(Database *db, UpdateInfo* info) { + Napi::Env env = db->Env(); Napi::HandleScope scope(env); Napi::Value argv[] = { - Napi::New(env, sqlite_authorizer_string(info->type)), - Napi::New(env, info->database.c_str()), - Napi::New(env, info->table.c_str()), + 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; } 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); return info.This(); @@ -553,28 +559,29 @@ void Database::Work_Exec(uv_work_t* req) { } void Database::Work_AfterExec(uv_work_t* req) { - Napi::HandleScope scope(env); - ExecBaton* baton = static_cast(req->data); Database* db = baton->db; - Napi::Function cb = Napi::New(env, 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()) { + if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(db->handle(), 2, info); + EMIT_EVENT(db->Value(), 2, info); } } - else if (!cb.IsEmpty() && cb->IsFunction()) { + else if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); @@ -583,6 +590,7 @@ void Database::Work_AfterExec(uv_work_t* req) { } Napi::Value Database::Wait(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Database* db = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); @@ -594,6 +602,7 @@ Napi::Value Database::Wait(const Napi::CallbackInfo& info) { } void Database::Work_Wait(Baton* baton) { + Napi::Env env = baton->db->Env(); Napi::HandleScope scope(env); assert(baton->db->locked); @@ -601,10 +610,10 @@ void Database::Work_Wait(Baton* baton) { assert(baton->db->_handle); assert(baton->db->pending == 0); - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(baton->db->handle(), cb, 1, argv); + TRY_CATCH_CALL(baton->db->Value(), cb, 1, argv); } baton->db->Process(); @@ -613,12 +622,13 @@ void Database::Work_Wait(Baton* baton) { } 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); return info.This(); @@ -656,27 +666,29 @@ void Database::Work_LoadExtension(uv_work_t* req) { } void Database::Work_AfterLoadExtension(uv_work_t* req) { - Napi::HandleScope scope(env); - LoadExtensionBaton* baton = static_cast(req->data); Database* db = baton->db; - Napi::Function cb = Napi::New(env, 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()) { + if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } else { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(db->handle(), 2, info); + EMIT_EVENT(db->Value(), 2, info); } } - else if (!cb.IsEmpty() && cb->IsFunction()) { + else if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(db->handle(), cb, 1, argv); + TRY_CATCH_CALL(db->Value(), cb, 1, argv); } db->Process(); diff --git a/src/database.h b/src/database.h index 5b2c96e58..0dd82b0d3 100644 --- a/src/database.h +++ b/src/database.h @@ -29,7 +29,7 @@ class Database : public Napi::ObjectWrap { Napi::HandleScope scope(env); if (!val.IsObject()) return false; Napi::Object obj = val.As(); - return Napi::New(env, constructor)->HasInstance(obj); + return obj.InstanceOf(constructor.Value()); } struct Baton { @@ -43,7 +43,9 @@ class Database : public Napi::ObjectWrap { 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(); @@ -102,19 +104,20 @@ class Database : public Napi::ObjectWrap { friend class Statement; friend class Backup; -protected: - Database() : Napi::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); @@ -122,7 +125,7 @@ class Database : public Napi::ObjectWrap { open = false; } - static Napi::Value New(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); @@ -132,30 +135,30 @@ class Database : public Napi::ObjectWrap { void Schedule(Work_Callback callback, Baton* baton, bool exclusive = false); void Process(); - static Napi::Value Exec(const Napi::CallbackInfo& info); + 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 Napi::Value Wait(const Napi::CallbackInfo& info); + Napi::Value Wait(const Napi::CallbackInfo& info); static void Work_Wait(Baton* baton); - static Napi::Value Close(const Napi::CallbackInfo& info); + 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 Napi::Value LoadExtension(const Napi::CallbackInfo& info); + 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 Napi::Value Serialize(const Napi::CallbackInfo& info); - static Napi::Value Parallelize(const Napi::CallbackInfo& info); + Napi::Value Serialize(const Napi::CallbackInfo& info); + Napi::Value Parallelize(const Napi::CallbackInfo& info); - static Napi::Value Configure(const Napi::CallbackInfo& info); + Napi::Value Configure(const Napi::CallbackInfo& info); - static Napi::Value Interrupt(const Napi::CallbackInfo& info); + Napi::Value Interrupt(const Napi::CallbackInfo& info); static void SetBusyTimeout(Baton* baton); diff --git a/src/macros.h b/src/macros.h index bfee24a55..b80735acb 100644 --- a/src/macros.h +++ b/src/macros.h @@ -3,35 +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() ); +} + +// 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)) { \ Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ - return env.Null(); \ } #define REQUIRE_ARGUMENT_EXTERNAL(i, var) \ - if (info.Length() <= (i) || !info[i].IsExternal()) { \ + if (info.Length() <= (i) || !info[i].IsExternal()) { \ Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \ - return env.Null(); \ } \ Napi::External var = info[i].As(); #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ - if (info.Length() <= (i) || !info[i].IsFunction()) { \ + if (info.Length() <= (i) || !info[i].IsFunction()) { \ Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ - return env.Null(); \ } \ Napi::Function var = info[i].As(); #define REQUIRE_ARGUMENT_STRING(i, var) \ - if (info.Length() <= (i) || !info[i].IsString()) { \ + if (info.Length() <= (i) || !info[i].IsString()) { \ Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ - return env.Null(); \ } \ std::string var = info[i].As(); @@ -43,13 +57,12 @@ const char* sqlite_authorizer_string(int type); int var(info[i].As().Int32Value()); #define OPTIONAL_ARGUMENT_FUNCTION(i, var) \ - Napi::Function var; \ - if (info.Length() > i && !info[i].IsUndefined()) { \ - if (!info[i].IsFunction()) { \ + 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 = info[i].As(); \ + var = info[i].As(); \ } @@ -59,67 +72,57 @@ const char* sqlite_authorizer_string(int type); var = (default); \ } \ else if (info[i].IsNumber()) { \ - var = info[i].As().Int32Value(); \ + if (OtherIsInt(info[i].As())) { \ + var = info[i].As().Int32Value(); \ + } \ } \ else { \ Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ - return env.Null(); \ } #define DEFINE_CONSTANT_INTEGER(target, constant, name) \ - target->DefineProperty( \ - Napi::New(env, #name), \ - Napi::Number::New(env, constant), \ - static_cast(napi_enumerable | napi_configurable) \ - ); + Napi::PropertyDescriptor::Value(#name, Napi::Number::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define DEFINE_CONSTANT_STRING(target, constant, name) \ - target->DefineProperty( \ - Napi::New(env, #name), \ - Napi::New(env, constant), \ - static_cast(napi_enumerable | napi_configurable) \ - ); - - -#define NODE_SET_GETTER(target, name, function) \ - Napi::SetAccessor((target)->InstanceTemplate(), \ - Napi::New(env, name), (function)); - -#define NODE_SET_SETTER(target, name, getter, setter) \ - Napi::SetAccessor((target)->InstanceTemplate(), \ - Napi::New(env, name), getter, setter); - -#define GET_STRING(source, name, property) \ - std::string name = (source).Get(\ - Napi::New(env, prop.As())); - -#define GET_INTEGER(source, name, prop) \ - int name = Napi::To((source).Get(\ - Napi::New(env, property))); + Napi::PropertyDescriptor::Value(#name, Napi::String::New(env, constant), \ + static_cast(napi_enumerable | napi_configurable)), #define EXCEPTION(msg, errno, name) \ - Napi::Value name = Exception::Error(Napi::New(env, \ - std::string(sqlite_code_string(errno)) + \ - std::string(": ") + std::string(msg) \ - )); \ - Napi::Object name ##_obj = name.As(); \ - (name ##_obj).Set(Napi::String::New(env, "errno"), Napi::New(env, errno));\ - (name ##_obj).Set(Napi::String::New(env, "code"), \ - Napi::New(env, sqlite_code_string(errno))); + 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), \ - (obj).Get(\ - Napi::String::New(env, "emit")).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) \ - (callback).MakeCallback((context), (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 Napi::Value name(const Napi::CallbackInfo& info); \ + 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); diff --git a/src/node_sqlite3.cc b/src/node_sqlite3.cc index e43f95875..b101b451f 100644 --- a/src/node_sqlite3.cc +++ b/src/node_sqlite3.cc @@ -16,50 +16,54 @@ namespace { Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); - Database::Init(env, target, module); - Statement::Init(env, target, module); - Backup::Init(env, target, module); + 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; } } diff --git a/src/statement.cc b/src/statement.cc index cf35cd64c..3a2d9f35b 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -11,26 +11,49 @@ using namespace node_sqlite3; Napi::FunctionReference Statement::constructor; +static Napi::FunctionReference date; +static Napi::FunctionReference regexp; Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); - Napi::FunctionReference t = Napi::Function::New(env, 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_), + }); + constructor = Napi::Persistent(t); + constructor.SuppressDestruct(); - t->SetClassName(Napi::String::New(env, "Statement")); + exports.Set("Statement", t); + return exports; +} + +// A Napi InstanceOf for Javascript Objects "Date" and "RegExp" +bool OtherInstanceOf(Napi::Object source, char* object_type) { + if (date.IsEmpty()) { + Napi::Function date_func = source.Env().Global().Get("Date").As(); + Napi::Function regexp_func = source.Env().Global().Get("RegExp").As(); - InstanceMethod("bind", &Bind), - InstanceMethod("get", &Get), - InstanceMethod("run", &Run), - InstanceMethod("all", &All), - InstanceMethod("each", &Each), - InstanceMethod("reset", &Reset), - InstanceMethod("finalize", &Finalize), + date = Napi::Persistent(date_func); + date.SuppressDestruct(); + + regexp = Napi::Persistent(regexp_func); + regexp.SuppressDestruct(); + } - constructor.Reset(t); - (target).Set(Napi::String::New(env, "Statement"), - Napi::GetFunction(t)); + if (object_type == "Date") { + return source.InstanceOf(date.Value()); + } else if (object_type == "RegExp") { + return source.InstanceOf(regexp.Value()); + } + + return false; } void Statement::Process() { @@ -61,60 +84,56 @@ void Statement::Schedule(Work_Callback callback, Baton* baton) { } template void Statement::Error(T* baton) { + Statement* stmt = baton->stmt; + + Napi::Env env = stmt->Env(); Napi::HandleScope scope(env); - Statement* stmt = baton->stmt; // 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); - Napi::Function cb = Napi::New(env, baton->callback); + Napi::Function cb = baton->callback.Value(); - if (!cb.IsEmpty() && cb->IsFunction()) { + if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { exception }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } else { Napi::Value argv[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(stmt->handle(), 2, argv); + EMIT_EVENT(stmt->Value(), 2, argv); } } // { Database db, String sql, Array params, Function callback } -Napi::Value Statement::New(const Napi::CallbackInfo& info) { - if (!info.IsConstructCall()) { - Napi::TypeError::New(env, "Use the new operator to create new Statement objects").ThrowAsJavaScriptException(); - return env.Null(); - } - +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])) { Napi::TypeError::New(env, "Database object expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length <= 1 || !info[1].IsString()) { Napi::TypeError::New(env, "SQL query expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } else if (length > 2 && !info[2].IsUndefined() && !info[2].IsFunction()) { Napi::TypeError::New(env, "Callback expected").ThrowAsJavaScriptException(); - return env.Null(); + return; } - Database* db = info[0].As().Unwrap(); + Database* db = Napi::ObjectWrap::Unwrap(info[0].As()); Napi::String sql = info[1].As(); - info.This().DefineProperty(Napi::String::New(env, "sql"), 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, info[2].As(), stmt); - baton->sql = std::string(sql->As().Utf8Value().c_str()); + baton->sql = std::string(sql.As().Utf8Value().c_str()); db->Schedule(Work_BeginPrepare, baton); - - return info.This(); } void Statement::Work_BeginPrepare(Database::Baton* baton) { @@ -150,20 +169,21 @@ void Statement::Work_Prepare(uv_work_t* req) { } void Statement::Work_AfterPrepare(uv_work_t* req) { - Napi::HandleScope scope(env); - 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; - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + if (!baton->callback.IsEmpty() && baton->callback.Value().IsFunction()) { + Napi::Function cb = baton->callback.Value(); Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -172,28 +192,37 @@ void Statement::Work_AfterPrepare(uv_work_t* req) { template Values::Field* Statement::BindParameter(const Napi::Value source, T pos) { - if (source.IsString() || source->IsRegExp()) { - std::string val = source.As(); - return new Values::Text(pos, val.Length(), *val); + if (source.IsString()) { + std::string val = source.As().Utf8Value(); + return new Values::Text(pos, val.length(), val.c_str()); } - else if (source.IsNumber()) { - return new Values::Integer(pos, source.As().Int32Value()); + 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()) { - return new Values::Float(pos, source.As().DoubleValue()); + 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()) { + 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 (source.IsBuffer()) { - Napi::Object buffer = source.To(); - return new Values::Blob(pos, Buffer::Length(buffer), Buffer::Data(buffer)); + Napi::Buffer buffer = source.As>(); + return new Values::Blob(pos, buffer.Length(), buffer.Data()); } - else if (source->IsDate()) { - return new Values::Float(pos, source.As().DoubleValue()); + 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; @@ -201,11 +230,12 @@ template Values::Field* } 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(); Napi::Function callback; - if (last > start && info[last - 1]->IsFunction()) { + if (last > start && info[last - 1].IsFunction()) { callback = info[last - 1].As(); last--; } @@ -215,13 +245,13 @@ template T* Statement::Bind(const Napi::CallbackInfo& info, int start, if (start < last) { if (info[start].IsArray()) { Napi::Array array = info[start].As(); - int length = array->Length(); + 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((array).Get(i), pos)); } } - else if (!info[start].IsObject() || info[start].IsRegExp() || info[start].IsDate() || info[start].IsBuffer()) { + 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++) { @@ -230,8 +260,8 @@ template T* Statement::Bind(const Napi::CallbackInfo& info, int start, } else if (info[start].IsObject()) { Napi::Object object = info[start].As(); - Napi::Array array = Napi::GetPropertyNames(object); - int length = array->Length(); + Napi::Array array = object.GetPropertyNames(); + int length = array.Length(); for (int i = 0; i < length; i++) { Napi::Value name = (array).Get(i); @@ -241,7 +271,7 @@ template T* Statement::Bind(const Napi::CallbackInfo& info, int start, } else { baton->parameters.push_back(BindParameter((object).Get(name), - name->As().Utf8Value().c_str())); + name.As().Utf8Value().c_str())); } } } @@ -268,7 +298,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; } @@ -311,6 +341,7 @@ bool Statement::Bind(const Parameters & parameters) { } Napi::Value Statement::Bind(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); @@ -338,19 +369,20 @@ void Statement::Work_Bind(uv_work_t* req) { } void Statement::Work_AfterBind(uv_work_t* req) { - Napi::HandleScope scope(env); - STATEMENT_INIT(Baton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_OK) { Error(baton); } else { // Fire callbacks. - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -360,6 +392,7 @@ void Statement::Work_AfterBind(uv_work_t* req) { Napi::Value Statement::Get(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); @@ -402,25 +435,26 @@ void Statement::Work_Get(uv_work_t* req) { } void Statement::Work_AfterGet(uv_work_t* req) { - Napi::HandleScope scope(env); - 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. - Napi::Function cb = Napi::New(env, 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. - Napi::Value argv[] = { env.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 { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } } @@ -429,6 +463,7 @@ void Statement::Work_AfterGet(uv_work_t* req) { } Napi::Value Statement::Run(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); @@ -473,22 +508,23 @@ void Statement::Work_Run(uv_work_t* req) { } void Statement::Work_AfterRun(uv_work_t* req) { - Napi::HandleScope scope(env); - 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. - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { - (stmt->handle()).Set(Napi::String::New(env, "lastID"), Napi::Number::New(env, baton->inserted_id)); - (stmt->handle()).Set(Napi::String::New(env, "changes"), Napi::New(env, 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)); Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } } @@ -496,6 +532,7 @@ void Statement::Work_AfterRun(uv_work_t* req) { } Napi::Value Statement::All(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Statement* stmt = this; Baton* baton = stmt->Bind(info); @@ -540,29 +577,30 @@ void Statement::Work_All(uv_work_t* req) { } void Statement::Work_AfterAll(uv_work_t* req) { - Napi::HandleScope scope(env); - STATEMENT_INIT(RowsBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_DONE) { Error(baton); } else { // Fire callbacks. - Napi::Function cb = Napi::New(env, 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. 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++) { - (result).Set(i, RowToJS(*it)); + (result).Set(i, RowToJS(env,*it)); delete *it; } Napi::Value argv[] = { env.Null(), result }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } else { // There were no result rows. @@ -570,7 +608,7 @@ void Statement::Work_AfterAll(uv_work_t* req) { env.Null(), Napi::Array::New(env, 0) }; - TRY_CATCH_CALL(stmt->handle(), cb, 2, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 2, argv); } } } @@ -579,12 +617,13 @@ void Statement::Work_AfterAll(uv_work_t* req) { } Napi::Value Statement::Each(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Statement* stmt = this; int last = info.Length(); Napi::Function completed; - if (last >= 2 && info[last - 1]->IsFunction() && info[last - 2]->IsFunction()) { + if (last >= 2 && info[last - 1].IsFunction() && info[last - 2].IsFunction()) { completed = info[--last].As(); } @@ -605,8 +644,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); } @@ -662,10 +701,11 @@ void Statement::CloseCallback(uv_handle_t* handle) { } void Statement::AsyncEach(uv_async_t* handle, int status) { - Napi::HandleScope scope(env); - 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; @@ -677,41 +717,42 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { break; } - Napi::Function cb = Napi::New(env, async->item_cb); - if (!cb.IsEmpty() && cb->IsFunction()) { + 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; } } } - Napi::Function cb = Napi::New(env, async->completed_cb); + Napi::Function cb = async->completed_cb.Value(); if (async->completed) { if (!cb.IsEmpty() && - cb->IsFunction()) { + cb.IsFunction()) { Napi::Value argv[] = { env.Null(), - Napi::New(env, async->retrieved) + 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) { - Napi::HandleScope scope(env); - STATEMENT_INIT(EachBaton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + if (stmt->status != SQLITE_DONE) { Error(baton); } @@ -720,6 +761,7 @@ void Statement::Work_AfterEach(uv_work_t* req) { } Napi::Value Statement::Reset(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); Statement* stmt = this; OPTIONAL_ARGUMENT_FUNCTION(0, callback); @@ -742,21 +784,22 @@ void Statement::Work_Reset(uv_work_t* req) { } void Statement::Work_AfterReset(uv_work_t* req) { - Napi::HandleScope scope(env); - STATEMENT_INIT(Baton); + Napi::Env env = stmt->Env(); + Napi::HandleScope scope(env); + // Fire callbacks. - Napi::Function cb = Napi::New(env, baton->callback); - if (!cb.IsEmpty() && cb->IsFunction()) { + Napi::Function cb = baton->callback.Value(); + if (!cb.IsUndefined() && cb.IsFunction()) { Napi::Value argv[] = { env.Null() }; - TRY_CATCH_CALL(stmt->handle(), cb, 1, argv); + TRY_CATCH_CALL(stmt->Value(), cb, 1, argv); } STATEMENT_END(); } -Napi::Object Statement::RowToJS(Row* row) { +Napi::Value Statement::RowToJS(Napi::Env env, Row* row) { Napi::EscapableHandleScope scope(env); Napi::Object result = Napi::Object::New(env); @@ -779,14 +822,14 @@ Napi::Object Statement::RowToJS(Row* row) { value = Napi::String::New(env, ((Values::Text*)field)->value.c_str(), ((Values::Text*)field)->value.size()); } break; case SQLITE_BLOB: { - value = Napi::Buffer::Copy(env, ((Values::Blob*)field)->value, ((Values::Blob*)field)->length); + value = Napi::Buffer::Copy(env, ((Values::Blob*)field)->value, ((Values::Blob*)field)->length); } break; case SQLITE_NULL: { value = env.Null(); } break; } - (result).Set(Napi::New(env, field->name.c_str()), value); + (result).Set(Napi::String::New(env, field->name.c_str()), value); DELETE_FIELD(field); } @@ -826,31 +869,33 @@ void Statement::GetRow(Row* row, sqlite3_stmt* stmt) { } } -Napi::Value Statement::Finalize(const Napi::CallbackInfo& info) { +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); - return stmt->db->handle(); + return stmt->db->Value(); } -void Statement::Finalize(Baton* baton) { +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. - Napi::Function cb = Napi::New(env, 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(); @@ -862,12 +907,13 @@ void Statement::Finalize() { } void Statement::CleanQueue() { + 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); + EXCEPTION(Napi::String::New(env, "Statement is already finalized"), SQLITE_MISUSE, exception); Napi::Value argv[] = { exception }; bool called = false; @@ -876,11 +922,11 @@ void Statement::CleanQueue() { Call* call = queue.front(); queue.pop(); - Napi::Function cb = Napi::New(env, 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; } @@ -894,7 +940,7 @@ void Statement::CleanQueue() { // Statement object. if (!called) { Napi::Value info[] = { Napi::String::New(env, "error"), exception }; - EMIT_EVENT(handle(), 2, info); + EMIT_EVENT(Value(), 2, info); } } else while (!queue.empty()) { diff --git a/src/statement.h b/src/statement.h index 9dbcfb5b9..719ef9a59 100644 --- a/src/statement.h +++ b/src/statement.h @@ -15,7 +15,6 @@ #include #include -using namespace Napi; using namespace Napi; namespace node_sqlite3 { @@ -144,7 +143,7 @@ class Statement : public Napi::ObjectWrap { if (!db->IsOpen() && db->IsLocked()) { // The database handle was closed before the statement could be // prepared. - stmt->Finalize(); + stmt->Finalize_(); } } }; @@ -186,18 +185,20 @@ class Statement : public Napi::ObjectWrap { } }; - Statement(Database* db_) : Napi::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); @@ -207,7 +208,7 @@ class Statement : public Napi::ObjectWrap { WORK_DEFINITION(Each); WORK_DEFINITION(Reset); - static Napi::Value Finalize(const Napi::CallbackInfo& info); + Napi::Value Finalize_(const Napi::CallbackInfo& info); protected: static void Work_BeginPrepare(Database::Baton* baton); @@ -217,15 +218,15 @@ class Statement : public Napi::ObjectWrap { static void AsyncEach(uv_async_t* handle, int status); 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 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 Napi::Object 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) { From 421b8ab0d17ce8367e079bc58032b6ef8f556d4d Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 1 Oct 2019 01:51:57 +0400 Subject: [PATCH 04/26] Disable N-API C++ exceptions --- binding.gyp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/binding.gyp b/binding.gyp index f43df0064..7379c3a44 100644 --- a/binding.gyp +++ b/binding.gyp @@ -48,7 +48,8 @@ "src/database.cc", "src/node_sqlite3.cc", "src/statement.cc" - ] + ], + "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] }, { "target_name": "action_after_build", @@ -69,7 +70,8 @@ "files": [ "<(PRODUCT_DIR)/<(module_name).node" ], "destination": "<(module_path)" } - ] + ], + "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] } ] } From 4177fb798b228c1e4706801e6bc893cac95d1d00 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 1 Oct 2019 01:48:34 +0400 Subject: [PATCH 05/26] Fix tests failing --- src/statement.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/statement.cc b/src/statement.cc index 3a2d9f35b..625cd52f1 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -264,10 +264,11 @@ template T* Statement::Bind(const Napi::CallbackInfo& info, int start, int length = array.Length(); for (int i = 0; i < length; i++) { Napi::Value name = (array).Get(i); + Napi::Number num = name.ToNumber(); - if (name.IsNumber()) { + if (num.Int32Value() == num.DoubleValue()) { baton->parameters.push_back( - BindParameter((object).Get(name), name.As().Int32Value())); + BindParameter((object).Get(name), num.Int32Value())); } else { baton->parameters.push_back(BindParameter((object).Get(name), From 3806e0cbd6bde8735836c11e7c352b4f0ffff9ef Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 30 Sep 2019 21:02:31 +0400 Subject: [PATCH 06/26] Use N-API for async work --- src/async.h | 1 + src/backup.cc | 21 ++++++++------ src/backup.h | 8 ++---- src/database.cc | 73 ++++++++++++++++++++++++++++++++---------------- src/database.h | 20 ++++++------- src/macros.h | 32 +++++++++++++-------- src/statement.cc | 38 ++++++++++++++----------- src/statement.h | 7 ++--- 8 files changed, 118 insertions(+), 82 deletions(-) diff --git a/src/async.h b/src/async.h index 5232c127d..617379982 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/backup.cc b/src/backup.cc index bb2a9e465..14f9e0556 100644 --- a/src/backup.cc +++ b/src/backup.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -190,12 +189,16 @@ Backup::Backup(const Napi::CallbackInfo& info) : Napi::ObjectWrap(info) 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 @@ -224,7 +227,7 @@ void Backup::Work_Initialize(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Backup::Work_AfterInitialize(uv_work_t* req) { +void Backup::Work_AfterInitialize(napi_env e, napi_status status, void* data) { BACKUP_INIT(InitializeBaton); Napi::Env env = backup->Env(); @@ -262,7 +265,7 @@ 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); @@ -286,7 +289,7 @@ void Backup::Work_Step(uv_work_t* req) { } } -void Backup::Work_AfterStep(uv_work_t* req) { +void Backup::Work_AfterStep(napi_env e, napi_status status, void* data) { BACKUP_INIT(StepBaton); Napi::Env env = backup->Env(); @@ -328,12 +331,12 @@ 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) { +void Backup::Work_AfterFinish(napi_env e, napi_status status, void* data) { BACKUP_INIT(Baton); Napi::Env env = backup->Env(); diff --git a/src/backup.h b/src/backup.h index 740b5824a..f991a2484 100644 --- a/src/backup.h +++ b/src/backup.h @@ -9,7 +9,6 @@ #include #include -#include using namespace Napi; @@ -99,13 +98,12 @@ class Backup : public Napi::ObjectWrap { static Napi::Object Init(Napi::Env env, Napi::Object exports); struct Baton { - uv_work_t request; + napi_async_work request; Backup* backup; Napi::FunctionReference callback; Baton(Backup* backup_, Napi::Function cb_) : backup(backup_) { backup->Ref(); - request.data = this; callback.Reset(cb_); } virtual ~Baton() { @@ -187,8 +185,8 @@ class Backup : public Napi::ObjectWrap { 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(); diff --git a/src/database.cc b/src/database.cc index 4f5fbb56b..642ea7157 100644 --- a/src/database.cc +++ b/src/database.cc @@ -135,13 +135,17 @@ Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap( } 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( @@ -162,8 +166,9 @@ void Database::Work_Open(uv_work_t* req) { } } -void Database::Work_AfterOpen(uv_work_t* req) { - OpenBaton* baton = static_cast(req->data); +void Database::Work_AfterOpen(napi_env e, napi_status status, void* data) { + OpenBaton* baton = static_cast(data); + Database* db = baton->db; Napi::Env env = db->Env(); @@ -195,6 +200,7 @@ void Database::Work_AfterOpen(uv_work_t* req) { db->Process(); } + napi_delete_async_work(e, baton->request); delete baton; } @@ -224,13 +230,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); @@ -243,8 +254,9 @@ void Database::Work_Close(uv_work_t* req) { } } -void Database::Work_AfterClose(uv_work_t* req) { - Baton* baton = static_cast(req->data); +void Database::Work_AfterClose(napi_env e, napi_status status, void* data) { + Baton* baton = static_cast(data); + Database* db = baton->db; Napi::Env env = db->Env(); @@ -281,6 +293,7 @@ void Database::Work_AfterClose(uv_work_t* req) { db->Process(); } + napi_delete_async_work(e, baton->request); delete baton; } @@ -535,13 +548,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( @@ -558,8 +575,9 @@ void Database::Work_Exec(uv_work_t* req) { } } -void Database::Work_AfterExec(uv_work_t* req) { - ExecBaton* baton = static_cast(req->data); +void Database::Work_AfterExec(napi_env e, napi_status status, void* data) { + ExecBaton* baton = static_cast(data); + Database* db = baton->db; Napi::Env env = db->Env(); @@ -586,6 +604,7 @@ void Database::Work_AfterExec(uv_work_t* req) { db->Process(); + napi_delete_async_work(e, baton->request); delete baton; } @@ -639,13 +658,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); @@ -665,8 +688,9 @@ void Database::Work_LoadExtension(uv_work_t* req) { } } -void Database::Work_AfterLoadExtension(uv_work_t* req) { - LoadExtensionBaton* baton = static_cast(req->data); +void Database::Work_AfterLoadExtension(napi_env e, napi_status status, void* data) { + LoadExtensionBaton* baton = static_cast(data); + Database* db = baton->db; Napi::Env env = db->Env(); @@ -693,6 +717,7 @@ void Database::Work_AfterLoadExtension(uv_work_t* req) { db->Process(); + napi_delete_async_work(e, baton->request); delete baton; } diff --git a/src/database.h b/src/database.h index 0dd82b0d3..8a509daa9 100644 --- a/src/database.h +++ b/src/database.h @@ -8,7 +8,6 @@ #include #include -#include #include "async.h" @@ -33,7 +32,7 @@ class Database : public Napi::ObjectWrap { } struct Baton { - uv_work_t request; + napi_async_work request; Database* db; Napi::FunctionReference callback; int status; @@ -42,7 +41,6 @@ class Database : public Napi::ObjectWrap { Baton(Database* db_, Napi::Function cb_) : db(db_), status(SQLITE_OK) { db->Ref(); - request.data = this; if (!cb_.IsUndefined() && cb_.IsFunction()) { callback.Reset(cb_, 1); } @@ -127,8 +125,8 @@ class Database : public Napi::ObjectWrap { 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); Napi::Value OpenGetter(const Napi::CallbackInfo& info); @@ -137,21 +135,21 @@ class Database : public Napi::ObjectWrap { 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); Napi::Value Wait(const Napi::CallbackInfo& info); static void Work_Wait(Baton* baton); 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); 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); Napi::Value Serialize(const Napi::CallbackInfo& info); Napi::Value Parallelize(const Napi::CallbackInfo& info); diff --git a/src/macros.h b/src/macros.h index b80735acb..d308bf962 100644 --- a/src/macros.h +++ b/src/macros.h @@ -124,8 +124,8 @@ inline bool OtherIsInt(Napi::Number source) { #define WORK_DEFINITION(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); \ @@ -135,13 +135,16 @@ inline bool OtherIsInt(Napi::Number source) { 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() \ @@ -151,6 +154,7 @@ inline bool OtherIsInt(Napi::Number source) { stmt->db->pending--; \ stmt->Process(); \ stmt->db->Process(); \ + napi_delete_async_work(e, baton->request); \ delete baton; #define BACKUP_BEGIN(type) \ @@ -161,13 +165,16 @@ inline bool OtherIsInt(Napi::Number source) { 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() \ @@ -177,6 +184,7 @@ inline bool OtherIsInt(Napi::Number source) { 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/statement.cc b/src/statement.cc index 625cd52f1..d453a189d 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -139,12 +139,16 @@ Statement::Statement(const Napi::CallbackInfo& info) : Napi::ObjectWrapdb->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 @@ -168,7 +172,7 @@ void Statement::Work_Prepare(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterPrepare(uv_work_t* req) { +void Statement::Work_AfterPrepare(napi_env e, napi_status status, void* data) { STATEMENT_INIT(PrepareBaton); Napi::Env env = stmt->Env(); @@ -360,7 +364,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); @@ -369,7 +373,7 @@ void Statement::Work_Bind(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterBind(uv_work_t* req) { +void Statement::Work_AfterBind(napi_env e, napi_status status, void* data) { STATEMENT_INIT(Baton); Napi::Env env = stmt->Env(); @@ -411,7 +415,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()) { @@ -435,7 +439,7 @@ void Statement::Work_Get(uv_work_t* req) { } } -void Statement::Work_AfterGet(uv_work_t* req) { +void Statement::Work_AfterGet(napi_env e, napi_status status, void* data) { STATEMENT_INIT(RowBaton); Napi::Env env = stmt->Env(); @@ -482,7 +486,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); @@ -508,7 +512,7 @@ void Statement::Work_Run(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterRun(uv_work_t* req) { +void Statement::Work_AfterRun(napi_env e, napi_status status, void* data) { STATEMENT_INIT(RunBaton); Napi::Env env = stmt->Env(); @@ -551,7 +555,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); @@ -577,7 +581,7 @@ void Statement::Work_All(uv_work_t* req) { sqlite3_mutex_leave(mtx); } -void Statement::Work_AfterAll(uv_work_t* req) { +void Statement::Work_AfterAll(napi_env e, napi_status status, void* data) { STATEMENT_INIT(RowsBaton); Napi::Env env = stmt->Env(); @@ -651,7 +655,7 @@ void Statement::Work_BeginEach(Baton* baton) { 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; @@ -748,7 +752,7 @@ void Statement::AsyncEach(uv_async_t* handle, int status) { } } -void Statement::Work_AfterEach(uv_work_t* req) { +void Statement::Work_AfterEach(napi_env e, napi_status status, void* data) { STATEMENT_INIT(EachBaton); Napi::Env env = stmt->Env(); @@ -777,14 +781,14 @@ 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) { +void Statement::Work_AfterReset(napi_env e, napi_status status, void* data) { STATEMENT_INIT(Baton); Napi::Env env = stmt->Env(); @@ -908,7 +912,7 @@ void Statement::Finalize_() { } void Statement::CleanQueue() { - Napi::Env env = this->Env(); + Napi::Env env = this->Env(); Napi::HandleScope scope(env); if (prepared && !queue.empty()) { diff --git a/src/statement.h b/src/statement.h index 719ef9a59..60ca2971f 100644 --- a/src/statement.h +++ b/src/statement.h @@ -79,14 +79,13 @@ class Statement : public Napi::ObjectWrap { static Napi::Value New(const Napi::CallbackInfo& info); struct Baton { - uv_work_t request; + napi_async_work request; Statement* stmt; Napi::FunctionReference callback; Parameters parameters; Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { stmt->Ref(); - request.data = this; callback.Reset(cb_); } virtual ~Baton() { @@ -212,8 +211,8 @@ class Statement : public Napi::ObjectWrap { 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 CloseCallback(uv_handle_t* handle); From bbec1fa119de0af46055d96d7efad2a32f868af3 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 1 Oct 2019 02:22:20 +0400 Subject: [PATCH 07/26] Add assert.h --- src/database.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/database.h b/src/database.h index 8a509daa9..b31f68136 100644 --- a/src/database.h +++ b/src/database.h @@ -3,6 +3,7 @@ #define NODE_SQLITE3_SRC_DATABASE_H +#include #include #include From 70766c74b25204858937be7351491692a949b6cf Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 1 Oct 2019 22:26:32 +0400 Subject: [PATCH 08/26] Improve usage of libuv --- src/async.h | 9 ++++++--- src/statement.cc | 2 +- src/statement.h | 6 ++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/async.h b/src/async.h index 617379982..e623c99ec 100644 --- a/src/async.h +++ b/src/async.h @@ -3,6 +3,7 @@ #include "threading.h" #include +#include #include #if defined(NODE_SQLITE3_BOOST_THREADING) @@ -27,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) @@ -52,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/statement.cc b/src/statement.cc index d453a189d..8d8465155 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -705,7 +705,7 @@ void Statement::CloseCallback(uv_handle_t* handle) { delete async; } -void Statement::AsyncEach(uv_async_t* handle, int status) { +void Statement::AsyncEach(uv_async_t* handle) { Async* async = static_cast(handle->data); Napi::Env env = async->stmt->Env(); diff --git a/src/statement.h b/src/statement.h index 60ca2971f..f09bac0c1 100644 --- a/src/statement.h +++ b/src/statement.h @@ -173,7 +173,9 @@ class Statement : public Napi::ObjectWrap { 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() { @@ -214,7 +216,7 @@ class Statement : public Napi::ObjectWrap { 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); From ae9a005b253d4adddea618354c817e3c2022e430 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 1 Oct 2019 23:32:20 +0400 Subject: [PATCH 09/26] Support worker threads --- src/statement.cc | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/statement.cc b/src/statement.cc index 8d8465155..d4d764ac3 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -11,8 +11,6 @@ using namespace node_sqlite3; Napi::FunctionReference Statement::constructor; -static Napi::FunctionReference date; -static Napi::FunctionReference regexp; Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); @@ -36,21 +34,10 @@ Napi::Object Statement::Init(Napi::Env env, Napi::Object exports) { // A Napi InstanceOf for Javascript Objects "Date" and "RegExp" bool OtherInstanceOf(Napi::Object source, char* object_type) { - if (date.IsEmpty()) { - Napi::Function date_func = source.Env().Global().Get("Date").As(); - Napi::Function regexp_func = source.Env().Global().Get("RegExp").As(); - - date = Napi::Persistent(date_func); - date.SuppressDestruct(); - - regexp = Napi::Persistent(regexp_func); - regexp.SuppressDestruct(); - } - if (object_type == "Date") { - return source.InstanceOf(date.Value()); + return source.InstanceOf(source.Env().Global().Get("Date").As()); } else if (object_type == "RegExp") { - return source.InstanceOf(regexp.Value()); + return source.InstanceOf(source.Env().Global().Get("RegExp").As()); } return false; From d0142a7e467daee9568c1c4173352864644f4694 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 24 Feb 2020 06:57:12 +0400 Subject: [PATCH 10/26] Fix build on Windows --- binding.gyp | 4 ++-- src/async.h | 3 ++- src/statement.h | 7 +++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/binding.gyp b/binding.gyp index 7379c3a44..368812d40 100644 --- a/binding.gyp +++ b/binding.gyp @@ -37,7 +37,7 @@ }, { "dependencies": [ - " #include #include +#include "threading.h" + #if defined(NODE_SQLITE3_BOOST_THREADING) #include #endif diff --git a/src/statement.h b/src/statement.h index f09bac0c1..60ad8bc6f 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 @@ -15,6 +11,9 @@ #include #include +#include "database.h" +#include "threading.h" + using namespace Napi; namespace node_sqlite3 { From 3aa2e88190d150619d201a7a06803c848e6dd4b2 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 11 Apr 2020 13:59:23 -0700 Subject: [PATCH 11/26] fix travis config --- .travis.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 46b9fc39c..9ab961dba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ -sudo: false - language: generic dist: precise @@ -131,11 +129,11 @@ matrix: env: NODE_VERSION="4" # node abi 46 # electron Linux - os: linux - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" - dist: trusty - addons: - apt: + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" + dist: trusty + 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 @@ -248,8 +246,8 @@ matrix: packages: [ 'clang-3.5'] # electron MacOs - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" - os: osx compiler: clang env: NODE_VERSION="8" ELECTRON_VERSION="7.1.0" From 99d7126b37d368a52ddb48aafdc0b9ab4686dff6 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Thu, 16 Apr 2020 14:26:30 +0400 Subject: [PATCH 12/26] Remove unsupported Node.js versions from CI --- .travis.yml | 30 ------------------------------ appveyor.yml | 24 ------------------------ 2 files changed, 54 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ab961dba..ba160c77f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,13 +60,6 @@ matrix: 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" @@ -74,20 +67,6 @@ matrix: 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 @@ -115,18 +94,9 @@ matrix: - 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 diff --git a/appveyor.yml b/appveyor.yml index cd2c8f685..fd3efa29a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,23 +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 @@ -153,16 +139,6 @@ environment: 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 From 6d40993da079c04f2278ed48503e6273fae4472a Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Thu, 16 Apr 2020 14:52:06 +0400 Subject: [PATCH 13/26] Add missing return --- src/database.cc | 7 ++++++- src/macros.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/database.cc b/src/database.cc index 642ea7157..7b33193ae 100644 --- a/src/database.cc +++ b/src/database.cc @@ -110,7 +110,12 @@ Database::Database(const Napi::CallbackInfo& info) : Napi::ObjectWrap( init(); Napi::Env env = info.Env(); - REQUIRE_ARGUMENT_STRING(0, filename); + if (info.Length() <= 0 || !info[0].IsString()) { + Napi::TypeError::New(env, "String expected").ThrowAsJavaScriptException(); + return; + } + std::string filename = info[0].As(); + unsigned int pos = 1; int mode; diff --git a/src/macros.h b/src/macros.h index d308bf962..ce9448403 100644 --- a/src/macros.h +++ b/src/macros.h @@ -26,12 +26,14 @@ inline bool OtherIsInt(Napi::Number source) { #define REQUIRE_ARGUMENTS(n) \ if (info.Length() < (n)) { \ Napi::TypeError::New(env, "Expected " #n "arguments").ThrowAsJavaScriptException(); \ + return env.Null(); \ } #define REQUIRE_ARGUMENT_EXTERNAL(i, var) \ if (info.Length() <= (i) || !info[i].IsExternal()) { \ Napi::TypeError::New(env, "Argument " #i " invalid").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ Napi::External var = info[i].As(); @@ -39,6 +41,7 @@ inline bool OtherIsInt(Napi::Number source) { #define REQUIRE_ARGUMENT_FUNCTION(i, var) \ if (info.Length() <= (i) || !info[i].IsFunction()) { \ Napi::TypeError::New(env, "Argument " #i " must be a function").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ Napi::Function var = info[i].As(); @@ -46,6 +49,7 @@ inline bool OtherIsInt(Napi::Number source) { #define REQUIRE_ARGUMENT_STRING(i, var) \ if (info.Length() <= (i) || !info[i].IsString()) { \ Napi::TypeError::New(env, "Argument " #i " must be a string").ThrowAsJavaScriptException(); \ + return env.Null(); \ } \ std::string var = info[i].As(); @@ -61,6 +65,7 @@ inline bool OtherIsInt(Napi::Number source) { 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 = info[i].As(); \ } @@ -78,6 +83,7 @@ inline bool OtherIsInt(Napi::Number source) { } \ else { \ Napi::TypeError::New(env, "Argument " #i " must be an integer").ThrowAsJavaScriptException(); \ + return env.Null(); \ } From bf4770a1ddb8c8700b2760e7d92d0ae6a3d2b94d Mon Sep 17 00:00:00 2001 From: Jim Schlight Date: Thu, 16 Apr 2020 10:34:54 -0700 Subject: [PATCH 14/26] Change causes test fail on Node.js 12, macOS --- test/affected.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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(); }); }); From 8c8083cadabea39f83277e2c5a4266a5b86e7789 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Fri, 17 Apr 2020 03:35:22 +0400 Subject: [PATCH 15/26] Fix references --- src/backup.cc | 3 +-- src/backup.h | 2 +- src/statement.cc | 2 +- src/statement.h | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/backup.cc b/src/backup.cc index 14f9e0556..4ca952b37 100644 --- a/src/backup.cc +++ b/src/backup.cc @@ -415,7 +415,7 @@ void Backup::RetryErrorSetter(const Napi::CallbackInfo& info, const Napi::Value& return; } Napi::Array array = value.As(); - backup->retryErrors.Reset(array); + backup->retryErrors.Reset(array, 1); } void Backup::GetRetryErrors(std::set& retryErrorsSet) { @@ -429,4 +429,3 @@ void Backup::GetRetryErrors(std::set& retryErrorsSet) { } } } - diff --git a/src/backup.h b/src/backup.h index f991a2484..95edc1ab6 100644 --- a/src/backup.h +++ b/src/backup.h @@ -104,7 +104,7 @@ class Backup : public Napi::ObjectWrap { Baton(Backup* backup_, Napi::Function cb_) : backup(backup_) { backup->Ref(); - callback.Reset(cb_); + callback.Reset(cb_, 1); } virtual ~Baton() { backup->Unref(); diff --git a/src/statement.cc b/src/statement.cc index d4d764ac3..4dbc2f391 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -625,7 +625,7 @@ Napi::Value Statement::Each(const Napi::CallbackInfo& info) { return env.Null(); } else { - baton->completed.Reset(completed); + baton->completed.Reset(completed, 1); stmt->Schedule(Work_BeginEach, baton); return info.This(); } diff --git a/src/statement.h b/src/statement.h index 60ad8bc6f..04a572153 100644 --- a/src/statement.h +++ b/src/statement.h @@ -85,7 +85,7 @@ class Statement : public Napi::ObjectWrap { Baton(Statement* stmt_, Napi::Function cb_) : stmt(stmt_) { stmt->Ref(); - callback.Reset(cb_); + callback.Reset(cb_, 1); } virtual ~Baton() { for (unsigned int i = 0; i < parameters.size(); i++) { From 5fc6fed89574ec0cbdc3bba290ed40c7646cfd17 Mon Sep 17 00:00:00 2001 From: Jim Schlight Date: Fri, 17 Apr 2020 08:13:11 -0700 Subject: [PATCH 16/26] Add define for NAPI_VERSION=3 to binding.gyp --- binding.gyp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/binding.gyp b/binding.gyp index 368812d40..380c58897 100644 --- a/binding.gyp +++ b/binding.gyp @@ -49,7 +49,7 @@ "src/node_sqlite3.cc", "src/statement.cc" ], - "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] + "defines": [ "NAPI_VERSION=3", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] }, { "target_name": "action_after_build", @@ -71,7 +71,7 @@ "destination": "<(module_path)" } ], - "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] + "defines": [ "NAPI_VERSION=3", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] } ] } From b0332e78ae92fd0534916389541a00b7d12b16e5 Mon Sep 17 00:00:00 2001 From: Jim Schlight Date: Fri, 17 Apr 2020 11:37:04 -0700 Subject: [PATCH 17/26] Update package.json for N-API builds --- .gitignore | 3 ++- binding.gyp | 4 ++-- package.json | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) 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/binding.gyp b/binding.gyp index 380c58897..f7a3b3b8c 100644 --- a/binding.gyp +++ b/binding.gyp @@ -49,7 +49,7 @@ "src/node_sqlite3.cc", "src/statement.cc" ], - "defines": [ "NAPI_VERSION=3", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] + "defines": [ "NAPI_VERSION=<(napi_build_version)", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] }, { "target_name": "action_after_build", @@ -71,7 +71,7 @@ "destination": "<(module_path)" } ], - "defines": [ "NAPI_VERSION=3", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] + "defines": [ "NAPI_VERSION=<(napi_build_version)", "NAPI_DISABLE_CPP_EXCEPTIONS=1" ] } ] } diff --git a/package.json b/package.json index 5e5e10626..04ff756dc 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,13 @@ }, "binary": { "module_name": "node_sqlite3", - "module_path": "./lib/binding/{node_abi}-{platform}-{arch}", + "module_path": "./lib/binding/napi-v{napi_build_version}-{platform}-{arch}", "host": "https://mapbox-node-binary.s3.amazonaws.com", "remote_path": "./{name}/v{version}/{toolset}/", - "package_name": "{node_abi}-{platform}-{arch}.tar.gz" + "package_name": "napi-v{napi_build_version}-{platform}-{arch}.tar.gz", + "napi_versions": [ + 3 + ] }, "contributors": [ "Konstantin Käfer ", From 7b85fb3119838ccbf1813fd668f9769d85cc4a56 Mon Sep 17 00:00:00 2001 From: Jim Schlight Date: Sat, 18 Apr 2020 10:20:25 -0700 Subject: [PATCH 18/26] Remove unsupported Electron versions --- .travis.yml | 51 --------------------------------------------------- appveyor.yml | 44 -------------------------------------------- 2 files changed, 95 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba160c77f..8f2554b2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -178,42 +178,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="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 @@ -245,21 +209,6 @@ matrix: - os: osx compiler: clang env: NODE_VERSION="6" ELECTRON_VERSION="3.0.6" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="2.0.1" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.8.4" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.7.12" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.6.2" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.3.14" env: global: diff --git a/appveyor.yml b/appveyor.yml index fd3efa29a..bbc09f0ef 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -119,50 +119,6 @@ environment: 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: 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 From af31fb87730cc3ebadf8ba65c6e68bae2eec4f1f Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sat, 18 Apr 2020 22:26:46 +0400 Subject: [PATCH 19/26] Fix warnings --- src/async.h | 1 - src/backup.cc | 2 -- src/statement.cc | 2 -- 3 files changed, 5 deletions(-) diff --git a/src/async.h b/src/async.h index aa70f39b8..9c4d8926a 100644 --- a/src/async.h +++ b/src/async.h @@ -1,7 +1,6 @@ #ifndef NODE_SQLITE3_SRC_ASYNC_H #define NODE_SQLITE3_SRC_ASYNC_H -#include #include #include diff --git a/src/backup.cc b/src/backup.cc index 4ca952b37..470d8f5f4 100644 --- a/src/backup.cc +++ b/src/backup.cc @@ -1,7 +1,5 @@ #include #include -#include -#include #include "macros.h" #include "database.h" diff --git a/src/statement.cc b/src/statement.cc index 4dbc2f391..15d6f91a1 100644 --- a/src/statement.cc +++ b/src/statement.cc @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include "macros.h" #include "database.h" From e3d900616272fd3dc74faee88112ecfaa746b4d3 Mon Sep 17 00:00:00 2001 From: Kewde Date: Sun, 19 Apr 2020 14:03:13 +0000 Subject: [PATCH 20/26] ci: fix travis & add latest electron builds --- .travis.yml | 125 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc7654701..f92e44d52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,5 @@ +sudo: false + language: generic dist: precise @@ -60,6 +62,13 @@ matrix: 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" @@ -67,6 +76,20 @@ matrix: 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 @@ -94,32 +117,41 @@ matrix: - 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" - dist: trusty - addons: - apt: + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.2.0" + dist: trusty + 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="8" ELECTRON_VERSION="8.1.0" - dist: trusty - addons: - apt: + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.1.0" + dist: trusty + 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="8" ELECTRON_VERSION="8.0.0" - dist: trusty - addons: - apt: + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" + dist: trusty + 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 @@ -194,16 +226,52 @@ 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="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" + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.2.0" - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.1.0" + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.1.0" - os: osx - compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" + compiler: clang + env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" - os: osx compiler: clang env: NODE_VERSION="8" ELECTRON_VERSION="7.1.0" @@ -231,6 +299,21 @@ matrix: - os: osx compiler: clang env: NODE_VERSION="6" ELECTRON_VERSION="3.0.6" + - os: osx + compiler: clang + env: NODE_VERSION="6" ELECTRON_VERSION="2.0.1" + - os: osx + compiler: clang + env: NODE_VERSION="6" ELECTRON_VERSION="1.8.4" + - os: osx + compiler: clang + env: NODE_VERSION="6" ELECTRON_VERSION="1.7.12" + - os: osx + compiler: clang + env: NODE_VERSION="6" ELECTRON_VERSION="1.6.2" + - os: osx + compiler: clang + env: NODE_VERSION="6" ELECTRON_VERSION="1.3.14" env: global: From ad56e175dc7e152ed0938fc55bcf679a5b42f501 Mon Sep 17 00:00:00 2001 From: Kewde Date: Sun, 19 Apr 2020 14:07:15 +0000 Subject: [PATCH 21/26] ci: reduce matrix of prebuilts (to Node >= v10 & electron >= v6) (linux) --- .travis.yml | 174 ---------------------------------------------------- 1 file changed, 174 deletions(-) diff --git a/.travis.yml b/.travis.yml index f92e44d52..357d7c051 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,24 +61,6 @@ 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 @@ -186,82 +118,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="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" - 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 @@ -284,36 +140,6 @@ matrix: - 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" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="3.0.6" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="2.0.1" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.8.4" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.7.12" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.6.2" - - os: osx - compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="1.3.14" env: global: From 80edf6fbeb1e779e027f7abcb088da77a0de5f03 Mon Sep 17 00:00:00 2001 From: Kewde Date: Sun, 19 Apr 2020 14:09:30 +0000 Subject: [PATCH 22/26] ci: reduce matrix of prebuilts (to Node >= v10 & electron >= v6) (windows) --- appveyor.yml | 52 ---------------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 45b9be3d9..26d7eefb8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,17 +1,5 @@ environment: matrix: - - nodejs_version: 6 - platform: x64 - - nodejs_version: 6 - 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 @@ -99,46 +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 os: Visual Studio 2015 From 766ee8f9874e3713af1d8a763594b78ca201e999 Mon Sep 17 00:00:00 2001 From: Jim Schlight Date: Mon, 20 Apr 2020 11:30:52 -0700 Subject: [PATCH 23/26] Add N-API v3 badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b1f4571a8..a80bb5752 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ 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 From 5b38ca4262c72eff7e03750a1548ebcc7aaddc0b Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 21 Apr 2020 00:07:10 +0400 Subject: [PATCH 24/26] Update Travis electron node versions --- .travis.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.travis.yml b/.travis.yml index 357d7c051..3a506145b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,7 +64,7 @@ matrix: # 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: @@ -72,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: @@ -80,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: @@ -88,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: @@ -96,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: @@ -104,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: @@ -112,7 +112,7 @@ matrix: packages: [ 'clang-3.5', 'libstdc++-4.9-dev'] - os: linux compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.0.0" + 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: @@ -121,25 +121,25 @@ matrix: # electron MacOs - os: osx compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.2.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.2.0" - os: osx compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.1.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.1.0" - os: osx compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="8.0.0" + env: NODE_VERSION="12" ELECTRON_VERSION="8.0.0" - os: osx compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="7.1.0" + env: NODE_VERSION="12" ELECTRON_VERSION="7.1.0" - os: osx compiler: clang - env: NODE_VERSION="8" ELECTRON_VERSION="7.0.0" + env: NODE_VERSION="12" ELECTRON_VERSION="7.0.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.1.0" + env: NODE_VERSION="12" ELECTRON_VERSION="6.1.0" - os: osx compiler: clang - env: NODE_VERSION="6" ELECTRON_VERSION="6.0.0" + env: NODE_VERSION="12" ELECTRON_VERSION="6.0.0" env: global: From 78dd0d97e75e296fbfe2695c4f5c39407f0ff0b8 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 20 Apr 2020 23:58:36 +0400 Subject: [PATCH 25/26] Update README with supported versions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a80bb5752..50ec37935 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Asynchronous, non-blocking [SQLite3](https://sqlite.org/) bindings for [Node.js] ## Supported platforms -The `sqlite3` module works with Node.js v4.x, v6.x, v8.x, v10.x, v11.x and v12.x. +The `sqlite3` module works with Node.js v10.x, v11.x, v12.x and v13.x. Binaries for most Node versions and platforms are provided by default via [node-pre-gyp](https://github.com/mapbox/node-pre-gyp). From 18c37ae84270a6931f12689f034d3390355a3358 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Tue, 21 Apr 2020 00:18:34 +0400 Subject: [PATCH 26/26] Simplify binding.gyp --- binding.gyp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/binding.gyp b/binding.gyp index f7a3b3b8c..f1336f6f7 100644 --- a/binding.gyp +++ b/binding.gyp @@ -53,25 +53,14 @@ }, { "target_name": "action_after_build", - "cflags!": [ "-fno-exceptions" ], - "cflags_cc!": [ "-fno-exceptions" ], - "xcode_settings": { "GCC_ENABLE_CPP_EXCEPTIONS": "YES", - "CLANG_CXX_LIBRARY": "libc++", - "MACOSX_DEPLOYMENT_TARGET": "10.7", - }, - "msvs_settings": { - "VCCLCompilerTool": { "ExceptionHandling": 1 }, - }, "type": "none", - "dependencies": [ - "