From b72cd163753546f2c8cd5e85ae9676b5d02068a7 Mon Sep 17 00:00:00 2001 From: xzyfer Date: Mon, 9 Jul 2018 01:04:51 +1000 Subject: [PATCH] Convert binding to N-API --- binding.gyp | 8 +- lib/extensions.js | 2 +- package.json | 4 +- src/binding.cpp | 470 +++++++++++++++++++--------- src/callback_bridge.h | 190 +++++++---- src/common.h | 11 + src/create_string.cpp | 34 +- src/create_string.h | 6 +- src/custom_function_bridge.cpp | 13 +- src/custom_function_bridge.h | 7 +- src/custom_importer_bridge.cpp | 114 ++++--- src/custom_importer_bridge.h | 11 +- src/sass_context_wrapper.cpp | 25 +- src/sass_context_wrapper.h | 13 +- src/sass_types/boolean.cpp | 115 ++++--- src/sass_types/boolean.h | 19 +- src/sass_types/color.cpp | 126 ++++---- src/sass_types/color.h | 30 +- src/sass_types/error.cpp | 20 +- src/sass_types/error.h | 8 +- src/sass_types/factory.cpp | 62 ++-- src/sass_types/factory.h | 8 +- src/sass_types/list.cpp | 122 ++++---- src/sass_types/list.h | 18 +- src/sass_types/map.cpp | 127 +++----- src/sass_types/map.h | 18 +- src/sass_types/null.cpp | 68 ++-- src/sass_types/null.h | 18 +- src/sass_types/number.cpp | 71 ++--- src/sass_types/number.h | 17 +- src/sass_types/sass_value_wrapper.h | 324 +++++++++++++++---- src/sass_types/string.cpp | 47 ++- src/sass_types/string.h | 11 +- src/sass_types/value.h | 34 +- test/api.js | 6 +- test/binding.js | 8 +- test/cli.js | 18 +- 37 files changed, 1324 insertions(+), 879 deletions(-) create mode 100644 src/common.h diff --git a/binding.gyp b/binding.gyp index f4507e6b8..d18373534 100644 --- a/binding.gyp +++ b/binding.gyp @@ -27,16 +27,22 @@ 'SetChecksum': 'true' } }, + 'cflags!': [ '-fno-exceptions' ], + 'cflags_cc!': [ '-fno-exceptions' ], 'xcode_settings': { 'OTHER_CPLUSPLUSFLAGS': [ '-std=c++11' ], + 'CLANG_CXX_LIBRARY': 'libc++', 'OTHER_LDFLAGS': [], 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', 'MACOSX_DEPLOYMENT_TARGET': '10.7' }, + "dependencies": [ + ' #include #include "sass_context_wrapper.h" #include "custom_function_bridge.h" #include "create_string.h" #include "sass_types/factory.h" +#include Sass_Import_List sass_importer(const char* cur_path, Sass_Importer_Entry cb, struct Sass_Compiler* comp) { @@ -25,28 +25,30 @@ union Sass_Value* sass_custom_function(const union Sass_Value* s_args, Sass_Func CustomFunctionBridge& bridge = *(static_cast(cookie)); std::vector argv; - for (unsigned l = sass_list_get_length(s_args), i = 0; i < l; i++) { + for (size_t l = sass_list_get_length(s_args), i = 0; i < l; i++) { argv.push_back((void*)sass_list_get_value(s_args, i)); } return bridge(argv); } -int ExtractOptions(v8::Local options, void* cptr, sass_context_wrapper* ctx_w, bool is_file, bool is_sync) { - Nan::HandleScope scope; +int ExtractOptions(napi_env e, napi_value options, void* cptr, sass_context_wrapper* ctx_w, bool is_file, bool is_sync) { + // TODO: Enable napi_close_handle_scope during a pending exception state. + //Napi::HandleScope scope(e); struct Sass_Context* ctx; - v8::Local result_ = Nan::Get( - options, - Nan::New("result").ToLocalChecked() - ).ToLocalChecked(); - if (!result_->IsObject()) { - Nan::ThrowTypeError("\"result\" element is not an object"); + napi_value result_; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "result", &result_)); + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(e, result_, &t)); + + if (t != napi_object) { + CHECK_NAPI_RESULT(napi_throw_type_error(e, nullptr, "\"result\" element is not an object")); return -1; } - ctx_w->result.Reset(result_.As()); + CHECK_NAPI_RESULT(napi_create_reference(e, result_, 1, &ctx_w->result)); if (is_file) { ctx_w->fctx = (struct Sass_File_Context*) cptr; @@ -65,62 +67,124 @@ int ExtractOptions(v8::Local options, void* cptr, sass_context_wrapp ctx_w->request.data = ctx_w; // async (callback) style - v8::Local success_callback = v8::Local::Cast(Nan::Get(options, Nan::New("success").ToLocalChecked()).ToLocalChecked()); - v8::Local error_callback = v8::Local::Cast(Nan::Get(options, Nan::New("error").ToLocalChecked()).ToLocalChecked()); + napi_value success_cb; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "success", &success_cb)); + napi_valuetype cb_type; + CHECK_NAPI_RESULT(napi_typeof(e, success_cb, &cb_type)); + if (cb_type == napi_function) { + CHECK_NAPI_RESULT(napi_create_reference(e, success_cb, 1, &ctx_w->success_callback)); + } - ctx_w->success_callback = new Nan::Callback(success_callback); - ctx_w->error_callback = new Nan::Callback(error_callback); + napi_value error_cb; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "error", &error_cb)); + CHECK_NAPI_RESULT(napi_typeof(e, error_cb, &cb_type)); + if (cb_type == napi_function) { + CHECK_NAPI_RESULT(napi_create_reference(e, error_cb, 1, &ctx_w->error_callback)); + } } if (!is_file) { - ctx_w->file = create_string(Nan::Get(options, Nan::New("file").ToLocalChecked())); + napi_value propertyFile; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "file", &propertyFile)); + + ctx_w->file = create_string(e, propertyFile); sass_option_set_input_path(sass_options, ctx_w->file); } - int indent_len = Nan::To( - Nan::Get( - options, - Nan::New("indentWidth").ToLocalChecked() - ).ToLocalChecked()).FromJust(); + napi_value propertyIndentWidth; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "indentWidth", &propertyIndentWidth)); + CHECK_NAPI_RESULT(napi_coerce_to_number(e, propertyIndentWidth, &propertyIndentWidth)); + int32_t indent_width_len; + CHECK_NAPI_RESULT(napi_get_value_int32(e, propertyIndentWidth, &indent_width_len)); + + ctx_w->indent = (char*)malloc(indent_width_len + 1); - ctx_w->indent = (char*)malloc(indent_len + 1); + napi_value propertyIndentType; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "indentType", &propertyIndentType)); + CHECK_NAPI_RESULT(napi_coerce_to_number(e, propertyIndentType, &propertyIndentType)); + int32_t indent_type_len; + CHECK_NAPI_RESULT(napi_get_value_int32(e, propertyIndentType, &indent_type_len)); strcpy(ctx_w->indent, std::string( - indent_len, - Nan::To( - Nan::Get( - options, - Nan::New("indentType").ToLocalChecked() - ).ToLocalChecked()).FromJust() == 1 ? '\t' : ' ' - ).c_str()); - - ctx_w->linefeed = create_string(Nan::Get(options, Nan::New("linefeed").ToLocalChecked())); - ctx_w->include_path = create_string(Nan::Get(options, Nan::New("includePaths").ToLocalChecked())); - ctx_w->out_file = create_string(Nan::Get(options, Nan::New("outFile").ToLocalChecked())); - ctx_w->source_map = create_string(Nan::Get(options, Nan::New("sourceMap").ToLocalChecked())); - ctx_w->source_map_root = create_string(Nan::Get(options, Nan::New("sourceMapRoot").ToLocalChecked())); + indent_width_len, + indent_type_len == 1 ? '\t' : ' ' + ).c_str()); + + napi_value propertyLinefeed; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "linefeed", &propertyLinefeed)); + napi_value propertyIncludePaths; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "includePaths", &propertyIncludePaths)); + napi_value propertyOutFile; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "outFile", &propertyOutFile)); + napi_value propertySourceMap; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "sourceMap", &propertySourceMap)); + napi_value propertySourceMapRoot; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "sourceMapRoot", &propertySourceMapRoot)); + + ctx_w->linefeed = create_string(e, propertyLinefeed); + ctx_w->include_path = create_string(e, propertyIncludePaths); + ctx_w->out_file = create_string(e, propertyOutFile); + ctx_w->source_map = create_string(e, propertySourceMap); + ctx_w->source_map_root = create_string(e, propertySourceMapRoot); + + napi_value propertyStyle; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "style", &propertyStyle)); + napi_value propertyIdentedSyntax; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "indentedSyntax", &propertyIdentedSyntax)); + napi_value propertySourceComments; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "sourceComments", &propertySourceComments)); + napi_value propertyOmitSourceMapUrl; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "omitSourceMapUrl", &propertyOmitSourceMapUrl)); + napi_value propertySourceMapEmbed; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "sourceMapEmbed", &propertySourceMapEmbed)); + napi_value propertySourceMapContents; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "sourceMapContents", &propertySourceMapContents)); + napi_value propertyPrecision; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "precision", &propertyPrecision)); + napi_value propertyImporter; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "importer", &propertyImporter)); + napi_value propertyFunctions; + CHECK_NAPI_RESULT(napi_get_named_property(e, options, "functions", &propertyFunctions)); + + int32_t styleVal; + CHECK_NAPI_RESULT(napi_get_value_int32(e, propertyStyle, &styleVal)); + bool indentedSyntaxVal; + CHECK_NAPI_RESULT(napi_coerce_to_bool(e, propertyIdentedSyntax, &propertyIdentedSyntax)); + CHECK_NAPI_RESULT(napi_get_value_bool(e, propertyIdentedSyntax, &indentedSyntaxVal)); + bool sourceCommentsVal; + CHECK_NAPI_RESULT(napi_coerce_to_bool(e, propertySourceComments, &propertySourceComments)); + CHECK_NAPI_RESULT(napi_get_value_bool(e, propertySourceComments, &sourceCommentsVal)); + bool omitSourceMapUrlVal; + CHECK_NAPI_RESULT(napi_coerce_to_bool(e, propertyOmitSourceMapUrl, &propertyOmitSourceMapUrl)); + CHECK_NAPI_RESULT(napi_get_value_bool(e, propertyOmitSourceMapUrl, &omitSourceMapUrlVal)); + bool sourceMapEmbedVal; + CHECK_NAPI_RESULT(napi_coerce_to_bool(e, propertySourceMapEmbed, &propertySourceMapEmbed)); + CHECK_NAPI_RESULT(napi_get_value_bool(e, propertySourceMapEmbed, &sourceMapEmbedVal)); + bool sourceMapContentsVal; + CHECK_NAPI_RESULT(napi_coerce_to_bool(e, propertySourceMapContents, &propertySourceMapContents)); + CHECK_NAPI_RESULT(napi_get_value_bool(e, propertySourceMapContents, &sourceMapContentsVal)); + int32_t precisionVal; + CHECK_NAPI_RESULT(napi_get_value_int32(e, propertyPrecision, &precisionVal)); sass_option_set_output_path(sass_options, ctx_w->out_file); - sass_option_set_output_style(sass_options, (Sass_Output_Style)Nan::To(Nan::Get(options, Nan::New("style").ToLocalChecked()).ToLocalChecked()).FromJust()); - sass_option_set_is_indented_syntax_src(sass_options, Nan::To(Nan::Get(options, Nan::New("indentedSyntax").ToLocalChecked()).ToLocalChecked()).FromJust()); - sass_option_set_source_comments(sass_options, Nan::To(Nan::Get(options, Nan::New("sourceComments").ToLocalChecked()).ToLocalChecked()).FromJust()); - sass_option_set_omit_source_map_url(sass_options, Nan::To(Nan::Get(options, Nan::New("omitSourceMapUrl").ToLocalChecked()).ToLocalChecked()).FromJust()); - sass_option_set_source_map_embed(sass_options, Nan::To(Nan::Get(options, Nan::New("sourceMapEmbed").ToLocalChecked()).ToLocalChecked()).FromJust()); - sass_option_set_source_map_contents(sass_options, Nan::To(Nan::Get(options, Nan::New("sourceMapContents").ToLocalChecked()).ToLocalChecked()).FromJust()); + sass_option_set_output_style(sass_options, (Sass_Output_Style)styleVal); + sass_option_set_is_indented_syntax_src(sass_options, indentedSyntaxVal); + sass_option_set_source_comments(sass_options, sourceCommentsVal); + sass_option_set_omit_source_map_url(sass_options, omitSourceMapUrlVal); + sass_option_set_source_map_embed(sass_options, sourceMapEmbedVal); + sass_option_set_source_map_contents(sass_options, sourceMapContentsVal); sass_option_set_source_map_file(sass_options, ctx_w->source_map); sass_option_set_source_map_root(sass_options, ctx_w->source_map_root); sass_option_set_include_path(sass_options, ctx_w->include_path); - sass_option_set_precision(sass_options, Nan::To(Nan::Get(options, Nan::New("precision").ToLocalChecked()).ToLocalChecked()).FromJust()); + sass_option_set_precision(sass_options, precisionVal); sass_option_set_indent(sass_options, ctx_w->indent); sass_option_set_linefeed(sass_options, ctx_w->linefeed); sass_option_push_import_extension(sass_options, ".css"); - v8::Local importer_callback = Nan::Get(options, Nan::New("importer").ToLocalChecked()).ToLocalChecked(); + CHECK_NAPI_RESULT(napi_typeof(e, propertyImporter, &t)); - if (importer_callback->IsFunction()) { - v8::Local importer = importer_callback.As(); - - CustomImporterBridge *bridge = new CustomImporterBridge(importer, ctx_w->is_sync); + if (t == napi_function) { + CustomImporterBridge *bridge = new CustomImporterBridge(e, propertyImporter, ctx_w->is_sync); ctx_w->importer_bridges.push_back(bridge); Sass_Importer_List c_importers = sass_make_importer_list(1); @@ -128,38 +192,51 @@ int ExtractOptions(v8::Local options, void* cptr, sass_context_wrapp sass_option_set_c_importers(sass_options, c_importers); } - else if (importer_callback->IsArray()) { - v8::Local importers = importer_callback.As(); - Sass_Importer_List c_importers = sass_make_importer_list(importers->Length()); + else { + bool isArray; + CHECK_NAPI_RESULT(napi_is_array(e, propertyImporter, &isArray)); - for (size_t i = 0; i < importers->Length(); ++i) { - v8::Local callback = v8::Local::Cast(Nan::Get(importers, static_cast(i)).ToLocalChecked()); + if (isArray) { + uint32_t len; + CHECK_NAPI_RESULT(napi_get_array_length(e, propertyImporter, &len)); - CustomImporterBridge *bridge = new CustomImporterBridge(callback, ctx_w->is_sync); - ctx_w->importer_bridges.push_back(bridge); + Sass_Importer_List c_importers = sass_make_importer_list(len); - c_importers[i] = sass_make_importer(sass_importer, importers->Length() - i - 1, bridge); - } + for (uint32_t i = 0; i < len; ++i) { + napi_value callback; + CHECK_NAPI_RESULT(napi_get_element(e, propertyImporter, i, &callback)); - sass_option_set_c_importers(sass_options, c_importers); + CustomImporterBridge *bridge = new CustomImporterBridge(e, callback, ctx_w->is_sync); + ctx_w->importer_bridges.push_back(bridge); + + c_importers[i] = sass_make_importer(sass_importer, len - i - 1, bridge); + } + + sass_option_set_c_importers(sass_options, c_importers); + } } - v8::Local custom_functions = Nan::Get(options, Nan::New("functions").ToLocalChecked()).ToLocalChecked(); + CHECK_NAPI_RESULT(napi_typeof(e, propertyFunctions, &t)); + + if (t == napi_object) { + // TODO: this should be napi_get_own_propertynames + napi_value signatures; + CHECK_NAPI_RESULT(napi_get_property_names(e, propertyFunctions, &signatures)); + uint32_t num_signatures; + CHECK_NAPI_RESULT(napi_get_array_length(e, signatures, &num_signatures)); - if (custom_functions->IsObject()) { - v8::Local functions = custom_functions.As(); - v8::Local signatures = Nan::GetOwnPropertyNames(functions).ToLocalChecked(); - unsigned num_signatures = signatures->Length(); Sass_Function_List fn_list = sass_make_function_list(num_signatures); - for (unsigned i = 0; i < num_signatures; i++) { - v8::Local signature = v8::Local::Cast(Nan::Get(signatures, Nan::New(i)).ToLocalChecked()); - v8::Local callback = v8::Local::Cast(Nan::Get(functions, signature).ToLocalChecked()); + for (uint32_t i = 0; i < num_signatures; i++) { + napi_value signature; + CHECK_NAPI_RESULT(napi_get_element(e, signatures, i, &signature)); + napi_value callback; + CHECK_NAPI_RESULT(napi_get_property(e, propertyFunctions, signature, &callback)); - CustomFunctionBridge *bridge = new CustomFunctionBridge(callback, ctx_w->is_sync); + CustomFunctionBridge *bridge = new CustomFunctionBridge(e, callback, ctx_w->is_sync); ctx_w->function_bridges.push_back(bridge); - char* sig = create_string(signature); + char* sig = create_string(e, signature); Sass_Function_Entry fn = sass_make_function(sig, sass_custom_function, bridge); free(sig); sass_function_set_list_entry(fn_list, i, fn); @@ -170,79 +247,93 @@ int ExtractOptions(v8::Local options, void* cptr, sass_context_wrapp return 0; } -void GetStats(sass_context_wrapper* ctx_w, Sass_Context* ctx) { - Nan::HandleScope scope; +void GetStats(napi_env env, sass_context_wrapper* ctx_w, Sass_Context* ctx) { + // TODO: Enable napi_close_handle_scope during a pending exception state. + //Napi::HandleScope scope; char** included_files = sass_context_get_included_files(ctx); - v8::Local arr = Nan::New(); + napi_value arr; + CHECK_NAPI_RESULT(napi_create_array(env, &arr)); if (included_files) { for (int i = 0; included_files[i] != nullptr; ++i) { - Nan::Set(arr, i, Nan::New(included_files[i]).ToLocalChecked()); + const char* s = included_files[i]; + int len = (int)strlen(s); + napi_value str; + CHECK_NAPI_RESULT(napi_create_string_utf8(env, s, len, &str)); + CHECK_NAPI_RESULT(napi_set_element(env, arr, i, str)); } } - v8::Local result = Nan::New(ctx_w->result); - assert(result->IsObject()); - - v8::Local stats = Nan::Get( - result, - Nan::New("stats").ToLocalChecked() - ).ToLocalChecked(); - if (stats->IsObject()) { - Nan::Set( - stats.As(), - Nan::New("includedFiles").ToLocalChecked(), - arr - ); + napi_value result; + CHECK_NAPI_RESULT(napi_get_reference_value(env, ctx_w->result, &result)); + assert(result != nullptr); + + napi_value propertyStats; + CHECK_NAPI_RESULT(napi_get_named_property(env, result, "stats", &propertyStats)); + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, propertyStats, &t)); + + if (t == napi_object) { + CHECK_NAPI_RESULT(napi_set_named_property(env, propertyStats, "includedFiles", arr)); } else { - Nan::ThrowTypeError("\"result.stats\" element is not an object"); + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "\"result.stats\" element is not an object")); } } -int GetResult(sass_context_wrapper* ctx_w, Sass_Context* ctx, bool is_sync = false) { - Nan::HandleScope scope; - v8::Local result; - +int GetResult(napi_env env, sass_context_wrapper* ctx_w, Sass_Context* ctx, bool is_sync = false) { + // TODO: Enable napi_close_handle_scope during a pending exception state. + //Napi::HandleScope scope(env); int status = sass_context_get_error_status(ctx); - result = Nan::New(ctx_w->result); - assert(result->IsObject()); + napi_value result; + CHECK_NAPI_RESULT(napi_get_reference_value(env, ctx_w->result, &result)); + assert(result != nullptr); if (status == 0) { const char* css = sass_context_get_output_string(ctx); const char* map = sass_context_get_source_map_string(ctx); + size_t css_len = strlen(css); - Nan::Set(result, Nan::New("css").ToLocalChecked(), Nan::CopyBuffer(css, static_cast(strlen(css))).ToLocalChecked()); + napi_value cssBuffer; + CHECK_NAPI_RESULT(napi_create_buffer_copy(env, css_len, css, NULL, &cssBuffer)); + CHECK_NAPI_RESULT(napi_set_named_property(env, result, "css", cssBuffer)); - GetStats(ctx_w, ctx); + GetStats(env, ctx_w, ctx); if (map) { - Nan::Set(result, Nan::New("map").ToLocalChecked(), Nan::CopyBuffer(map, static_cast(strlen(map))).ToLocalChecked()); + size_t map_len = strlen(map); + napi_value mapBuffer; + CHECK_NAPI_RESULT(napi_create_buffer_copy(env, map_len, map, NULL, &mapBuffer)); + CHECK_NAPI_RESULT(napi_set_named_property(env, result, "map", mapBuffer)); } } else if (is_sync) { - Nan::Set(result, Nan::New("error").ToLocalChecked(), Nan::New(sass_context_get_error_json(ctx)).ToLocalChecked()); + const char* err = sass_context_get_error_json(ctx); + size_t err_len = strlen(err); + napi_value str; + CHECK_NAPI_RESULT(napi_create_string_utf8(env, err, err_len, &str)); + CHECK_NAPI_RESULT(napi_set_named_property(env, result, "error", str)); } return status; } -void PerformCall(sass_context_wrapper* ctx_w, Nan::Callback* callback, int argc, v8::Local argv[]) { - if (ctx_w->is_sync) { - Nan::Call(*callback, argc, argv); - } else { - callback->Call(argc, argv, ctx_w->async_resource); - } -} +// ASYNC +// void PerformCall(sass_context_wrapper* ctx_w, Nan::Callback* callback, int argc, v8::Local argv[]) { +// if (ctx_w->is_sync) { +// Nan::Call(*callback, argc, argv); +// } else { +// callback->Call(argc, argv, ctx_w->async_resource); +// } +// } void MakeCallback(uv_work_t* req) { - Nan::HandleScope scope; - - Nan::TryCatch try_catch; sass_context_wrapper* ctx_w = static_cast(req->data); struct Sass_Context* ctx; + Napi::HandleScope scope(ctx_w->env); + if (ctx_w->dctx) { ctx = sass_data_context_get_context(ctx_w->dctx); } @@ -250,110 +341,191 @@ void MakeCallback(uv_work_t* req) { ctx = sass_file_context_get_context(ctx_w->fctx); } - int status = GetResult(ctx_w, ctx); + int status = GetResult(ctx_w->env, ctx_w, ctx); + + napi_value global; + CHECK_NAPI_RESULT(napi_get_global(ctx_w->env, &global)); if (status == 0 && ctx_w->success_callback) { // if no error, do callback(null, result) - PerformCall(ctx_w, ctx_w->success_callback, 0, 0); + napi_value success_cb; + CHECK_NAPI_RESULT(napi_get_reference_value(ctx_w->env, ctx_w->success_callback, &success_cb)); + assert(success_cb != nullptr); + + napi_value unused; + CHECK_NAPI_RESULT(napi_make_callback(ctx_w->env, nullptr, global, success_cb, 0, nullptr, &unused)); } else if (ctx_w->error_callback) { // if error, do callback(error) const char* err = sass_context_get_error_json(ctx); - v8::Local argv[] = { - Nan::New(err).ToLocalChecked() + int len = (int)strlen(err); + napi_value str; + CHECK_NAPI_RESULT(napi_create_string_utf8(ctx_w->env, err, len, &str)); + + napi_value argv[] = { + str }; - PerformCall(ctx_w, ctx_w->error_callback, 1, argv); + + napi_value error_cb; + CHECK_NAPI_RESULT(napi_get_reference_value(ctx_w->env, ctx_w->error_callback, &error_cb)); + assert(error_cb != nullptr); + + napi_value unused; + CHECK_NAPI_RESULT(napi_make_callback(ctx_w->env, nullptr, global, error_cb, 1, argv, &unused)); } - if (try_catch.HasCaught()) { - Nan::FatalException(try_catch); + + bool isPending; + CHECK_NAPI_RESULT(napi_is_exception_pending(ctx_w->env, &isPending)); + if (isPending) { + napi_value result; + CHECK_NAPI_RESULT(napi_get_and_clear_last_exception(ctx_w->env, &result)); + CHECK_NAPI_RESULT(napi_fatal_exception(ctx_w->env, result)); } sass_free_context_wrapper(ctx_w); } -NAN_METHOD(render) { +napi_value render(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value options; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &options, nullptr, nullptr)); + + napi_value propertyData; + CHECK_NAPI_RESULT(napi_get_named_property(env, options, "data", &propertyData)); + char* source_string = create_string(env, propertyData); - v8::Local options = Nan::To(info[0]).ToLocalChecked(); - char* source_string = create_string(Nan::Get(options, Nan::New("data").ToLocalChecked())); struct Sass_Data_Context* dctx = sass_make_data_context(source_string); - sass_context_wrapper* ctx_w = sass_make_context_wrapper(); + sass_context_wrapper* ctx_w = sass_make_context_wrapper(env); - ctx_w->async_resource = new Nan::AsyncResource("node-sass:sass_context_wrapper:render"); + // ASYNC + // ctx_w->async_resource = new Nan::AsyncResource("node-sass:sass_context_wrapper:render"); - if (ExtractOptions(options, dctx, ctx_w, false, false) >= 0) { + if (ExtractOptions(env, options, dctx, ctx_w, false, false) >= 0) { int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); assert(status == 0); } + + napi_value ret; + CHECK_NAPI_RESULT(napi_get_null(env, &ret)); + return ret; } -NAN_METHOD(render_sync) { +napi_value render_sync(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value options; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &options, nullptr, nullptr)); + + napi_value propertyData; + CHECK_NAPI_RESULT(napi_get_named_property(env, options, "data", &propertyData)); + char* source_string = create_string(env, propertyData); - v8::Local options = Nan::To(info[0]).ToLocalChecked(); - char* source_string = create_string(Nan::Get(options, Nan::New("data").ToLocalChecked())); struct Sass_Data_Context* dctx = sass_make_data_context(source_string); struct Sass_Context* ctx = sass_data_context_get_context(dctx); - sass_context_wrapper* ctx_w = sass_make_context_wrapper(); + sass_context_wrapper* ctx_w = sass_make_context_wrapper(env); int result = -1; - if ((result = ExtractOptions(options, dctx, ctx_w, false, true)) >= 0) { + if ((result = ExtractOptions(env, options, dctx, ctx_w, false, true)) >= 0) { compile_data(dctx); - result = GetResult(ctx_w, ctx, true); + result = GetResult(env, ctx_w, ctx, true); } sass_free_context_wrapper(ctx_w); - - info.GetReturnValue().Set(result == 0); + + bool isExceptionPending; + CHECK_NAPI_RESULT(napi_is_exception_pending(env, &isExceptionPending)); + if (!isExceptionPending) { + napi_value boolResult; + CHECK_NAPI_RESULT(napi_get_boolean(env, result == 0, &boolResult)); + return boolResult; + } + + return nullptr; } -NAN_METHOD(render_file) { +napi_value render_file(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value options; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &options, nullptr, nullptr)); + + napi_value propertyFile; + CHECK_NAPI_RESULT(napi_get_named_property(env, options, "file", &propertyFile)); + char* input_path = create_string(env, propertyFile); - v8::Local options = Nan::To(info[0]).ToLocalChecked(); - char* input_path = create_string(Nan::Get(options, Nan::New("file").ToLocalChecked())); struct Sass_File_Context* fctx = sass_make_file_context(input_path); - sass_context_wrapper* ctx_w = sass_make_context_wrapper(); + sass_context_wrapper* ctx_w = sass_make_context_wrapper(env); - ctx_w->async_resource = new Nan::AsyncResource("node-sass:sass_context_wrapper:render_file"); + // ASYNC + // ctx_w->async_resource = new Nan::AsyncResource("node-sass:sass_context_wrapper:render_file"); - if (ExtractOptions(options, fctx, ctx_w, true, false) >= 0) { + if (ExtractOptions(env, options, fctx, ctx_w, true, false) >= 0) { int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); assert(status == 0); } + + napi_value ret; + CHECK_NAPI_RESULT(napi_get_null(env, &ret)); + return ret; } -NAN_METHOD(render_file_sync) { +napi_value render_file_sync(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value options; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &options, nullptr, nullptr)); + + napi_value propertyFile; + CHECK_NAPI_RESULT(napi_get_named_property(env, options, "file", &propertyFile)); + char* input_path = create_string(env, propertyFile); - v8::Local options = Nan::To(info[0]).ToLocalChecked(); - char* input_path = create_string(Nan::Get(options, Nan::New("file").ToLocalChecked())); struct Sass_File_Context* fctx = sass_make_file_context(input_path); struct Sass_Context* ctx = sass_file_context_get_context(fctx); - sass_context_wrapper* ctx_w = sass_make_context_wrapper(); + sass_context_wrapper* ctx_w = sass_make_context_wrapper(env); int result = -1; - if ((result = ExtractOptions(options, fctx, ctx_w, true, true)) >= 0) { + if ((result = ExtractOptions(env, options, fctx, ctx_w, true, true)) >= 0) { compile_file(fctx); - result = GetResult(ctx_w, ctx, true); + result = GetResult(env, ctx_w, ctx, true); }; free(input_path); sass_free_context_wrapper(ctx_w); - info.GetReturnValue().Set(result == 0); + napi_value b; + CHECK_NAPI_RESULT(napi_get_boolean(env, result == 0, &b)); + return b; } -NAN_METHOD(libsass_version) { - info.GetReturnValue().Set(Nan::New(libsass_version()).ToLocalChecked()); +napi_value libsass_version(napi_env env, napi_callback_info info) { + const char* ver = libsass_version(); + int len = (int)strlen(ver); + napi_value str; + + CHECK_NAPI_RESULT(napi_create_string_utf8(env, ver, len, &str)); + return str; } -NAN_MODULE_INIT(RegisterModule) { - Nan::SetMethod(target, "render", render); - Nan::SetMethod(target, "renderSync", render_sync); - Nan::SetMethod(target, "renderFile", render_file); - Nan::SetMethod(target, "renderFileSync", render_file_sync); - Nan::SetMethod(target, "libsassVersion", libsass_version); - SassTypes::Factory::initExports(target); +napi_value Init(napi_env env, napi_value target) { + napi_value functionRender; + CHECK_NAPI_RESULT(napi_create_function(env, "render", NAPI_AUTO_LENGTH, render, nullptr, &functionRender)); + napi_value functionRenderSync; + CHECK_NAPI_RESULT(napi_create_function(env, "renderSync", NAPI_AUTO_LENGTH, render_sync, nullptr, &functionRenderSync)); + napi_value functionRenderFile; + CHECK_NAPI_RESULT(napi_create_function(env, "renderFile", NAPI_AUTO_LENGTH, render_file, nullptr, &functionRenderFile)); + napi_value functionRenderFileSync; + CHECK_NAPI_RESULT(napi_create_function(env, "renderFileSync", NAPI_AUTO_LENGTH, render_file_sync, nullptr, &functionRenderFileSync)); + napi_value functionLibsassVersion; + CHECK_NAPI_RESULT(napi_create_function(env, "libsassVersion", NAPI_AUTO_LENGTH, libsass_version, nullptr, &functionLibsassVersion)); + + CHECK_NAPI_RESULT(napi_set_named_property(env, target, "render", functionRender)); + CHECK_NAPI_RESULT(napi_set_named_property(env, target, "renderSync", functionRenderSync)); + CHECK_NAPI_RESULT(napi_set_named_property(env, target, "renderFile", functionRenderFile)); + CHECK_NAPI_RESULT(napi_set_named_property(env, target, "renderFileSync", functionRenderFileSync)); + CHECK_NAPI_RESULT(napi_set_named_property(env, target, "libsassVersion", functionLibsassVersion)); + + SassTypes::Factory::initExports(env, target); + return target; } -NODE_MODULE(binding, RegisterModule); +NAPI_MODULE(binding, Init) diff --git a/src/callback_bridge.h b/src/callback_bridge.h index 25f62e140..c965fcaa7 100644 --- a/src/callback_bridge.h +++ b/src/callback_bridge.h @@ -2,30 +2,32 @@ #define CALLBACK_BRIDGE_H #include -#include #include #include - -#define COMMA , +#include "common.h" +#include template class CallbackBridge { public: - CallbackBridge(v8::Local, bool); + CallbackBridge(napi_env, napi_value, bool); virtual ~CallbackBridge(); // Executes the callback T operator()(std::vector); + // Needed for napi_wrap + napi_value NewInstance(napi_env env); + protected: // We will expose a bridge object to the JS callback that wraps this instance so we don't loose context. // This is the V8 constructor for such objects. - static Nan::MaybeLocal get_wrapper_constructor(); + static napi_ref get_wrapper_constructor(napi_env env); static void async_gone(uv_handle_t *handle); - static NAN_METHOD(New); - static NAN_METHOD(ReturnCallback); - static Nan::Persistent wrapper_constructor; - Nan::Persistent wrapper; + static napi_value New(napi_env env, napi_callback_info info); + static napi_value ReturnCallback(napi_env env, napi_callback_info info); + static napi_ref wrapper_constructor; + napi_ref wrapper; // The callback that will get called in the main thread after the worker thread used for the sass // compilation step makes a call to uv_async_send() @@ -34,13 +36,16 @@ class CallbackBridge { // The V8 values sent to our ReturnCallback must be read on the main thread not the sass worker thread. // This gives a chance to specialized subclasses to transform those values into whatever makes sense to // sass before we resume the worker thread. - virtual T post_process_return_value(v8::Local) const =0; + virtual T post_process_return_value(napi_env, napi_value) const = 0; + + virtual std::vector pre_process_args(napi_env, std::vector) const = 0; - virtual std::vector> pre_process_args(std::vector) const =0; + // ASYNC + // Nan::AsyncResource* async_resource; - Nan::Callback* callback; - Nan::AsyncResource* async_resource; + napi_env e; + napi_ref callback; bool is_sync; uv_mutex_t cv_mutex; @@ -52,45 +57,57 @@ class CallbackBridge { }; template -Nan::Persistent CallbackBridge::wrapper_constructor; +napi_ref CallbackBridge::wrapper_constructor = nullptr; + +template +napi_value CallbackBridge::NewInstance(napi_env env) { + Napi::EscapableHandleScope scope(env); + + napi_value instance; + napi_value constructorHandle; + CHECK_NAPI_RESULT(napi_get_reference_value(env, get_wrapper_constructor(env), &constructorHandle)); + CHECK_NAPI_RESULT(napi_new_instance(env, constructorHandle, 0, nullptr, &instance)); + + return scope.Escape(instance); +} template -CallbackBridge::CallbackBridge(v8::Local callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) { +CallbackBridge::CallbackBridge(napi_env env, napi_value callback, bool is_sync) : e(env), is_sync(is_sync) { /* * This is invoked from the main JavaScript thread. * V8 context is available. */ - Nan::HandleScope scope; + Napi::HandleScope scope(this->e); uv_mutex_init(&this->cv_mutex); uv_cond_init(&this->condition_variable); if (!is_sync) { this->async = new uv_async_t; this->async->data = (void*) this; uv_async_init(uv_default_loop(), this->async, (uv_async_cb) dispatched_async_uv_callback); - this->async_resource = new Nan::AsyncResource("node-sass:CallbackBridge"); } - v8::Local func = CallbackBridge::get_wrapper_constructor().ToLocalChecked(); - wrapper.Reset(Nan::NewInstance(func).ToLocalChecked()); - Nan::SetInternalFieldPointer(Nan::New(wrapper), 0, this); + CHECK_NAPI_RESULT(napi_create_reference(env, callback, 1, &this->callback)); + + napi_value instance = CallbackBridge::NewInstance(env); + CHECK_NAPI_RESULT(napi_wrap(env, instance, this, nullptr, nullptr, nullptr)); + CHECK_NAPI_RESULT(napi_create_reference(env, instance, 1, &this->wrapper)); } template CallbackBridge::~CallbackBridge() { - delete this->callback; - this->wrapper.Reset(); + CHECK_NAPI_RESULT(napi_delete_reference(e, this->callback)); + CHECK_NAPI_RESULT(napi_delete_reference(e, this->wrapper)); + uv_cond_destroy(&this->condition_variable); uv_mutex_destroy(&this->cv_mutex); if (!is_sync) { uv_close((uv_handle_t*)this->async, &async_gone); - delete this->async_resource; } } template T CallbackBridge::operator()(std::vector argv) { - // argv.push_back(wrapper); if (this->is_sync) { /* * This is invoked from the main JavaScript thread. @@ -100,18 +117,32 @@ T CallbackBridge::operator()(std::vector argv) { * from types invoked by pre_process_args() and * post_process_args(). */ - Nan::HandleScope scope; - Nan::TryCatch try_catch; - std::vector> argv_v8 = pre_process_args(argv); - if (try_catch.HasCaught()) { - Nan::FatalException(try_catch); + Napi::HandleScope scope(this->e); + + std::vector argv_v8 = pre_process_args(this->e, argv); + + bool isPending; + CHECK_NAPI_RESULT(napi_is_exception_pending(this->e, &isPending)); + + if (isPending) { + CHECK_NAPI_RESULT(napi_throw_error(this->e, nullptr, "Error processing arguments")); + // This should be a FatalException but we still have to return something, this value might be uninitialized + return this->return_value; } - argv_v8.push_back(Nan::New(wrapper)); + napi_value _this; + CHECK_NAPI_RESULT(napi_get_reference_value(this->e, this->wrapper, &_this)); + argv_v8.push_back(_this); + + napi_value cb; + CHECK_NAPI_RESULT(napi_get_reference_value(this->e, this->callback, &cb)); + assert(cb != nullptr); + + napi_value result; + // TODO: Is receiver set correctly ? + CHECK_NAPI_RESULT(napi_make_callback(this->e, nullptr, _this, cb, argv_v8.size(), &argv_v8[0], &result)); - return this->post_process_return_value( - Nan::Call(*this->callback, argv_v8.size(), &argv_v8[0]).ToLocalChecked() - ); + return this->post_process_return_value(this->e, result); } else { /* * This is invoked from the worker thread. @@ -153,24 +184,46 @@ void CallbackBridge::dispatched_async_uv_callback(uv_async_t *req) { * from types invoked by pre_process_args() and * post_process_args(). */ - Nan::HandleScope scope; - Nan::TryCatch try_catch; - - std::vector> argv_v8 = bridge->pre_process_args(bridge->argv); - if (try_catch.HasCaught()) { - Nan::FatalException(try_catch); + Napi::HandleScope scope(bridge->e); + std::vector argv_v8 = bridge->pre_process_args(bridge->e, bridge->argv); + bool isPending; + + CHECK_NAPI_RESULT(napi_is_exception_pending(bridge->e, &isPending)); + if (isPending) { + CHECK_NAPI_RESULT(napi_throw_error(bridge->e, nullptr, "Error processing arguments")); + // This should be a FatalException + return; } - argv_v8.push_back(Nan::New(bridge->wrapper)); - - bridge->callback->Call(argv_v8.size(), &argv_v8[0], bridge->async_resource); - if (try_catch.HasCaught()) { - Nan::FatalException(try_catch); + napi_value _this; + CHECK_NAPI_RESULT(napi_get_reference_value(bridge->e, bridge->wrapper, &_this)); + argv_v8.push_back(_this); + + napi_value cb; + CHECK_NAPI_RESULT(napi_get_reference_value(bridge->e, bridge->callback, &cb)); + assert(cb != nullptr); + + napi_value result; + // TODO: Is receiver set correctly ? + CHECK_NAPI_RESULT(napi_make_callback(bridge->e, nullptr, _this, cb, argv_v8.size(), &argv_v8[0], &result)); + CHECK_NAPI_RESULT(napi_is_exception_pending(bridge->e, &isPending)); + if (isPending) { + CHECK_NAPI_RESULT(napi_throw_error(bridge->e, nullptr, "Error thrown in callback")); + // This should be a FatalException + return; } } template -NAN_METHOD(CallbackBridge::ReturnCallback) { +napi_value CallbackBridge::ReturnCallback(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value arg; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr)); + + void* unwrapped; + CHECK_NAPI_RESULT(napi_unwrap(env, _this, &unwrapped)); + CallbackBridge* bridge = static_cast(unwrapped); /* * Callback function invoked by the user code. @@ -179,10 +232,7 @@ NAN_METHOD(CallbackBridge::ReturnCallback) { * * Implicit Local<> handle scope created by NAN_METHOD(.) */ - CallbackBridge* bridge = static_cast*>(Nan::GetInternalFieldPointer(info.This(), 0)); - Nan::TryCatch try_catch; - - bridge->return_value = bridge->post_process_return_value(info[0]); + bridge->return_value = bridge->post_process_return_value(env, arg); { uv_mutex_lock(&bridge->cv_mutex); @@ -192,32 +242,38 @@ NAN_METHOD(CallbackBridge::ReturnCallback) { uv_cond_broadcast(&bridge->condition_variable); - if (try_catch.HasCaught()) { - Nan::FatalException(try_catch); + bool isPending; + CHECK_NAPI_RESULT(napi_is_exception_pending(env, &isPending)); + + if (isPending) { + napi_value result; + CHECK_NAPI_RESULT(napi_get_and_clear_last_exception(env, &result)); + CHECK_NAPI_RESULT(napi_fatal_exception(env, result)); } + + return nullptr; } template -Nan::MaybeLocal CallbackBridge::get_wrapper_constructor() { - /* Uses handle scope created in the CallbackBridge constructor */ - if (wrapper_constructor.IsEmpty()) { - v8::Local tpl = Nan::New(New); - tpl->SetClassName(Nan::New("CallbackBridge").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - - Nan::SetPrototypeTemplate(tpl, "success", - Nan::New(ReturnCallback) - ); - - wrapper_constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked()); - } +napi_ref CallbackBridge::get_wrapper_constructor(napi_env env) { + // TODO: cache wrapper_constructor + + napi_property_descriptor methods[] = { + { "success", nullptr, CallbackBridge::ReturnCallback }, + }; + + napi_value ctor; + CHECK_NAPI_RESULT(napi_define_class(env, "CallbackBridge", NAPI_AUTO_LENGTH, CallbackBridge::New, nullptr, 1, methods, &ctor)); + CHECK_NAPI_RESULT(napi_create_reference(env, ctor, 1, &wrapper_constructor)); - return Nan::New(wrapper_constructor); + return wrapper_constructor; } template -NAN_METHOD(CallbackBridge::New) { - info.GetReturnValue().Set(info.This()); +napi_value CallbackBridge::New(napi_env env, napi_callback_info info) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + return _this; } template diff --git a/src/common.h b/src/common.h new file mode 100644 index 000000000..192937fa4 --- /dev/null +++ b/src/common.h @@ -0,0 +1,11 @@ +#ifndef COMMON_H +#define COMMON_H + +#define NAPI_DISABLE_CPP_EXCEPTIONS 1 + +#include + +#define CHECK_NAPI_RESULT(condition) \ + do { napi_status status = (condition); assert(status == napi_ok || status == napi_pending_exception); } while(0) + +#endif // COMMON_H diff --git a/src/create_string.cpp b/src/create_string.cpp index 1b35b125f..536cc9d62 100644 --- a/src/create_string.cpp +++ b/src/create_string.cpp @@ -1,21 +1,29 @@ -#include #include #include #include "create_string.h" +#include -char* create_string(Nan::MaybeLocal maybevalue) { - v8::Local value; - - if (maybevalue.ToLocal(&value)) { - if (value->IsNull() || !value->IsString()) { - return 0; - } - } else { - return 0; +#define CHECK_NAPI_RESULT_RETURN_NULL(condition) do { if((condition) != napi_ok) { return nullptr; } } while(0) + +char* create_string(napi_env e, napi_value v) { + napi_valuetype t; + CHECK_NAPI_RESULT_RETURN_NULL(napi_typeof(e, v, &t)); + + if (t != napi_string) { + return nullptr; + } + + size_t len; + CHECK_NAPI_RESULT_RETURN_NULL(napi_get_value_string_utf8(e, v, NULL, 0, &len)); + + char* str = (char *)malloc(len + 1); + size_t written; + CHECK_NAPI_RESULT_RETURN_NULL(napi_get_value_string_utf8(e, v, str, len + 1, &written)); + + if (len != written) { + free(str); + return nullptr; } - v8::String::Utf8Value string(value); - char *str = (char *)malloc(string.length() + 1); - strcpy(str, *string); return str; } diff --git a/src/create_string.h b/src/create_string.h index 03c7c927d..234b8f9b0 100644 --- a/src/create_string.h +++ b/src/create_string.h @@ -1,8 +1,10 @@ #ifndef CREATE_STRING_H #define CREATE_STRING_H -#include +#define NAPI_DISABLE_CPP_EXCEPTIONS 1 -char* create_string(Nan::MaybeLocal); +#include + +char* create_string(napi_env e, napi_value v); #endif diff --git a/src/custom_function_bridge.cpp b/src/custom_function_bridge.cpp index f27c69545..6066b6f90 100644 --- a/src/custom_function_bridge.cpp +++ b/src/custom_function_bridge.cpp @@ -1,11 +1,10 @@ -#include #include #include "custom_function_bridge.h" #include "sass_types/factory.h" #include "sass_types/value.h" -Sass_Value* CustomFunctionBridge::post_process_return_value(v8::Local _val) const { - SassTypes::Value *value = SassTypes::Factory::unwrap(_val); +Sass_Value* CustomFunctionBridge::post_process_return_value(napi_env env, napi_value v) const { + SassTypes::Value *value = SassTypes::Factory::unwrap(env, v); if (value) { return value->get_sass_value(); } else { @@ -13,14 +12,14 @@ Sass_Value* CustomFunctionBridge::post_process_return_value(v8::Local } } -std::vector> CustomFunctionBridge::pre_process_args(std::vector in) const { - std::vector> argv = std::vector>(); +std::vector CustomFunctionBridge::pre_process_args(napi_env env, std::vector in) const { + std::vector argv; for (void* value : in) { Sass_Value* x = static_cast(value); - SassTypes::Value* y = SassTypes::Factory::create(x); + SassTypes::Value* y = SassTypes::Factory::create(env, x); - argv.push_back(y->get_js_object()); + argv.push_back(y->get_js_object(env)); } return argv; diff --git a/src/custom_function_bridge.h b/src/custom_function_bridge.h index 99c83ead3..5f375ee64 100644 --- a/src/custom_function_bridge.h +++ b/src/custom_function_bridge.h @@ -1,18 +1,17 @@ #ifndef CUSTOM_FUNCTION_BRIDGE_H #define CUSTOM_FUNCTION_BRIDGE_H -#include #include #include #include "callback_bridge.h" class CustomFunctionBridge : public CallbackBridge { public: - CustomFunctionBridge(v8::Local cb, bool is_sync) : CallbackBridge(cb, is_sync) {} + CustomFunctionBridge(napi_env env, napi_value cb, bool is_sync) : CallbackBridge(env, cb, is_sync) {} private: - Sass_Value* post_process_return_value(v8::Local) const; - std::vector> pre_process_args(std::vector) const; + Sass_Value* post_process_return_value(napi_env, napi_value) const; + std::vector pre_process_args(napi_env, std::vector) const; }; #endif diff --git a/src/custom_importer_bridge.cpp b/src/custom_importer_bridge.cpp index 38d737c26..cf8324993 100644 --- a/src/custom_importer_bridge.cpp +++ b/src/custom_importer_bridge.cpp @@ -1,102 +1,128 @@ -#include #include #include "custom_importer_bridge.h" #include "create_string.h" -SassImportList CustomImporterBridge::post_process_return_value(v8::Local returned_value) const { +SassImportList CustomImporterBridge::post_process_return_value(napi_env env, napi_value returned_value) const { SassImportList imports = 0; - Nan::HandleScope scope; + Napi::HandleScope scope(env); - if (returned_value->IsArray()) { - v8::Local array = returned_value.As(); + bool isArray; + bool isError; + CHECK_NAPI_RESULT(napi_is_array(env, returned_value, &isArray)); + CHECK_NAPI_RESULT(napi_is_error(env, returned_value, &isError)); - imports = sass_make_import_list(array->Length()); + if (isArray) { + uint32_t length; + CHECK_NAPI_RESULT(napi_get_array_length(env, returned_value, &length)); + imports = sass_make_import_list(length); - for (size_t i = 0; i < array->Length(); ++i) { - v8::Local value = Nan::Get(array, static_cast(i)).ToLocalChecked(); + for (uint32_t i = 0; i < length; ++i) { + napi_value value; + CHECK_NAPI_RESULT(napi_get_element(env, returned_value, i, &value)); - if (!value->IsObject()) { + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, value, &t)); + + if (t != napi_object) { auto entry = sass_make_import_entry(0, 0, 0); sass_import_set_error(entry, "returned array must only contain object literals", -1, -1); continue; } - v8::Local object = value.As(); + CHECK_NAPI_RESULT(napi_is_error(env, value, &isError)); - if (value->IsNativeError()) { - char* message = create_string(Nan::Get(object, Nan::New("message").ToLocalChecked())); + if (isError) { + napi_value propertyMessage; + CHECK_NAPI_RESULT(napi_get_named_property(env, value, "message", &propertyMessage)); + char* message = create_string(env, propertyMessage); imports[i] = sass_make_import_entry(0, 0, 0); sass_import_set_error(imports[i], message, -1, -1); free(message); } else { - imports[i] = get_importer_entry(object); + imports[i] = get_importer_entry(env, value); } } } - else if (returned_value->IsNativeError()) { + else if (isError) { imports = sass_make_import_list(1); - v8::Local object = returned_value.As(); - char* message = create_string(Nan::Get(object, Nan::New("message").ToLocalChecked())); + napi_value propertyMessage; + CHECK_NAPI_RESULT(napi_get_named_property(env, returned_value, "message", &propertyMessage)); + + char* message = create_string(env, propertyMessage); imports[0] = sass_make_import_entry(0, 0, 0); sass_import_set_error(imports[0], message, -1, -1); free(message); } - else if (returned_value->IsObject()) { - imports = sass_make_import_list(1); - imports[0] = get_importer_entry(returned_value.As()); + else { + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, returned_value, &t)); + + if (t == napi_object) { + imports = sass_make_import_list(1); + imports[0] = get_importer_entry(env, returned_value); + } } return imports; } -Sass_Import* CustomImporterBridge::check_returned_string(Nan::MaybeLocal value, const char *msg) const +Sass_Import* CustomImporterBridge::check_returned_string(napi_env env, napi_value value, const char *msg) const { - v8::Local checked; - if (value.ToLocal(&checked)) { - if (!checked->IsUndefined() && !checked->IsString()) { - goto err; - } else { - return nullptr; - } - } + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, value, &t)); + + if (t != napi_undefined && t != napi_string) { + goto err; + } else { + return nullptr; + } + err: - auto entry = sass_make_import_entry(0, 0, 0); - sass_import_set_error(entry, msg, -1, -1); - return entry; + auto entry = sass_make_import_entry(0, 0, 0); + sass_import_set_error(entry, msg, -1, -1); + return entry; } -Sass_Import* CustomImporterBridge::get_importer_entry(const v8::Local& object) const { - auto returned_file = Nan::Get(object, Nan::New("file").ToLocalChecked()); - auto returned_contents = Nan::Get(object, Nan::New("contents").ToLocalChecked()).ToLocalChecked(); - auto returned_map = Nan::Get(object, Nan::New("map").ToLocalChecked()); +Sass_Import* CustomImporterBridge::get_importer_entry(napi_env env, const napi_value& object) const { + napi_value returned_file; + CHECK_NAPI_RESULT(napi_get_named_property(env, object, "file", &returned_file)); + napi_value returned_contents; + CHECK_NAPI_RESULT(napi_get_named_property(env, object, "contents", &returned_contents)); + napi_value returned_map; + CHECK_NAPI_RESULT(napi_get_named_property(env, object, "map", &returned_map)); + Sass_Import *err; - if ((err = check_returned_string(returned_file, "returned value of `file` must be a string"))) + if ((err = check_returned_string(env, returned_file, "returned value of `file` must be a string"))) return err; - if ((err = check_returned_string(returned_contents, "returned value of `contents` must be a string"))) + if ((err = check_returned_string(env, returned_contents, "returned value of `contents` must be a string"))) return err; - if ((err = check_returned_string(returned_map, "returned value of `returned_map` must be a string"))) + if ((err = check_returned_string(env, returned_map, "returned value of `returned_map` must be a string"))) return err; - char* path = create_string(returned_file); - char* contents = create_string(returned_contents); - char* srcmap = create_string(returned_map); + char* path = create_string(env, returned_file); + char* contents = create_string(env, returned_contents); + char* srcmap = create_string(env, returned_map); return sass_make_import_entry(path, contents, srcmap); } -std::vector> CustomImporterBridge::pre_process_args(std::vector in) const { - std::vector> out; +std::vector CustomImporterBridge::pre_process_args(napi_env env, std::vector in) const { + std::vector out; for (void* ptr : in) { - out.push_back(Nan::New((char const*)ptr).ToLocalChecked()); + const char* s = (const char*)ptr; + int len = (int)strlen(s); + napi_value str; + CHECK_NAPI_RESULT(napi_create_string_utf8(env, s, len, &str)); + out.push_back(str); } return out; diff --git a/src/custom_importer_bridge.h b/src/custom_importer_bridge.h index 0cbd3e6d8..f57c5c0d1 100644 --- a/src/custom_importer_bridge.h +++ b/src/custom_importer_bridge.h @@ -1,7 +1,6 @@ #ifndef CUSTOM_IMPORTER_BRIDGE_H #define CUSTOM_IMPORTER_BRIDGE_H -#include #include #include #include "callback_bridge.h" @@ -10,13 +9,13 @@ typedef Sass_Import_List SassImportList; class CustomImporterBridge : public CallbackBridge { public: - CustomImporterBridge(v8::Local cb, bool is_sync) : CallbackBridge(cb, is_sync) {} + CustomImporterBridge(napi_env env, napi_value cb, bool is_sync) : CallbackBridge(env, cb, is_sync) {} private: - SassImportList post_process_return_value(v8::Local) const; - Sass_Import* check_returned_string(Nan::MaybeLocal value, const char *msg) const; - Sass_Import* get_importer_entry(const v8::Local&) const; - std::vector> pre_process_args(std::vector) const; + SassImportList post_process_return_value(napi_env, napi_value) const; + Sass_Import* check_returned_string(napi_env, napi_value, const char*) const; + Sass_Import* get_importer_entry(napi_env, const napi_value&) const; + std::vector pre_process_args(napi_env, std::vector) const; }; #endif diff --git a/src/sass_context_wrapper.cpp b/src/sass_context_wrapper.cpp index aa25c79b0..30755fa9c 100644 --- a/src/sass_context_wrapper.cpp +++ b/src/sass_context_wrapper.cpp @@ -22,8 +22,10 @@ extern "C" { sass_compile_file_context(fctx); } - sass_context_wrapper* sass_make_context_wrapper() { - return (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper)); + sass_context_wrapper* sass_make_context_wrapper(napi_env env) { + auto ret = (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper)); + ret->env = env; + return ret; } void sass_free_context_wrapper(sass_context_wrapper* ctx_w) { @@ -33,14 +35,19 @@ extern "C" { else if (ctx_w->fctx) { sass_delete_file_context(ctx_w->fctx); } - if (ctx_w->async_resource) { - delete ctx_w->async_resource; - } - - delete ctx_w->error_callback; - delete ctx_w->success_callback; + // if (ctx_w->async_resource) { + // delete ctx_w->async_resource; + // } - ctx_w->result.Reset(); + if (ctx_w->error_callback != nullptr) { + CHECK_NAPI_RESULT(napi_delete_reference(ctx_w->env, ctx_w->error_callback)); + } + if (ctx_w->success_callback != nullptr) { + CHECK_NAPI_RESULT(napi_delete_reference(ctx_w->env, ctx_w->success_callback)); + } + if (ctx_w->result != nullptr) { + CHECK_NAPI_RESULT(napi_delete_reference(ctx_w->env, ctx_w->result)); + } free(ctx_w->include_path); free(ctx_w->linefeed); diff --git a/src/sass_context_wrapper.h b/src/sass_context_wrapper.h index 4aa35684a..a17a7cd7f 100644 --- a/src/sass_context_wrapper.h +++ b/src/sass_context_wrapper.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include "custom_function_bridge.h" @@ -37,17 +36,17 @@ extern "C" { uv_async_t async; uv_work_t request; - // v8 and nan related - Nan::Persistent result; - Nan::AsyncResource* async_resource; - Nan::Callback* error_callback; - Nan::Callback* success_callback; + // v8 and napi related + napi_env env; + napi_ref result; + napi_ref error_callback; + napi_ref success_callback; std::vector function_bridges; std::vector importer_bridges; }; - struct sass_context_wrapper* sass_make_context_wrapper(void); + struct sass_context_wrapper* sass_make_context_wrapper(napi_env); void sass_free_context_wrapper(struct sass_context_wrapper*); #ifdef __cplusplus diff --git a/src/sass_types/boolean.cpp b/src/sass_types/boolean.cpp index 2d4793238..a69865cf0 100644 --- a/src/sass_types/boolean.cpp +++ b/src/sass_types/boolean.cpp @@ -1,73 +1,112 @@ -#include #include "boolean.h" namespace SassTypes { - Nan::Persistent Boolean::constructor; + napi_ref Boolean::constructor = nullptr; bool Boolean::constructor_locked = false; - Boolean::Boolean(bool _value) { - value = sass_make_boolean(_value); - } + Boolean::Boolean(bool v) : value(v), js_object(nullptr) {} Boolean& Boolean::get_singleton(bool v) { static Boolean instance_false(false), instance_true(true); return v ? instance_true : instance_false; } - v8::Local Boolean::get_constructor() { - Nan::EscapableHandleScope scope; - v8::Local conslocal; - if (constructor.IsEmpty()) { - v8::Local tpl = Nan::New(New); + napi_value Boolean::construct_and_wrap_instance(napi_env env, napi_value ctor, Boolean* b) { + Napi::EscapableHandleScope scope(env); + + napi_value instance; + CHECK_NAPI_RESULT(napi_new_instance(env, ctor, 0, nullptr, &instance)); + CHECK_NAPI_RESULT(napi_wrap(env, instance, b, nullptr, nullptr, nullptr)); + CHECK_NAPI_RESULT(napi_create_reference(env, instance, 1, &(b->js_object))); + + return scope.Escape(instance); + } + + napi_value Boolean::get_constructor(napi_env env) { + Napi::EscapableHandleScope scope(env); + napi_value ctor; - tpl->SetClassName(Nan::New("SassBoolean").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - Nan::SetPrototypeTemplate(tpl, "getValue", Nan::New(GetValue)); + if (Boolean::constructor) { + CHECK_NAPI_RESULT(napi_get_reference_value(env, Boolean::constructor, &ctor)); + } else { + napi_property_descriptor methods[] = { + { "getValue", nullptr, Boolean::GetValue }, + }; - conslocal = Nan::GetFunction(tpl).ToLocalChecked(); - constructor.Reset(conslocal); + CHECK_NAPI_RESULT(napi_define_class(env, "SassBoolean", NAPI_AUTO_LENGTH, Boolean::New, nullptr, 1, methods, &ctor)); + CHECK_NAPI_RESULT(napi_create_reference(env, ctor, 1, &Boolean::constructor)); - get_singleton(false).js_object.Reset(Nan::NewInstance(conslocal).ToLocalChecked()); - Nan::SetInternalFieldPointer(Nan::New(get_singleton(false).js_object), 0, &get_singleton(false)); - Nan::Set(conslocal, Nan::New("FALSE").ToLocalChecked(), Nan::New(get_singleton(false).js_object)); + Boolean& falseSingleton = get_singleton(false); + napi_value instance = construct_and_wrap_instance(env, ctor, &falseSingleton); + CHECK_NAPI_RESULT(napi_set_named_property(env, ctor, "FALSE", instance)); - get_singleton(true).js_object.Reset(Nan::NewInstance(conslocal).ToLocalChecked()); - Nan::SetInternalFieldPointer(Nan::New(get_singleton(true).js_object), 0, &get_singleton(true)); - Nan::Set(conslocal, Nan::New("TRUE").ToLocalChecked(), Nan::New(get_singleton(true).js_object)); + Boolean& trueSingleton = get_singleton(true); + instance = construct_and_wrap_instance(env, ctor, &trueSingleton); + CHECK_NAPI_RESULT(napi_set_named_property(env, ctor, "TRUE", instance)); constructor_locked = true; - } else { - conslocal = Nan::New(constructor); } - return scope.Escape(conslocal); + return scope.Escape(ctor); } - v8::Local Boolean::get_js_object() { - return Nan::New(this->js_object); + Sass_Value* Boolean::get_sass_value() { + return sass_make_boolean(value); } - v8::Local Boolean::get_js_boolean() { - return sass_boolean_get_value(this->value) ? Nan::True() : Nan::False(); + napi_value Boolean::get_js_object(napi_env env) { + Napi::EscapableHandleScope scope(env); + napi_value v; + CHECK_NAPI_RESULT(napi_get_reference_value(env, this->js_object, &v)); + return scope.Escape(v); } - NAN_METHOD(Boolean::New) { - if (info.IsConstructCall()) { + napi_value Boolean::New(napi_env env, napi_callback_info info) { + napi_value t; + CHECK_NAPI_RESULT(napi_get_new_target(env, info, &t)); + bool r = (t != nullptr); + + if (r) { if (constructor_locked) { - return Nan::ThrowTypeError("Cannot instantiate SassBoolean"); + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Cannot instantiate SassBoolean")); + return nullptr; } - } - else { - if (info.Length() != 1 || !info[0]->IsBoolean()) { - return Nan::ThrowTypeError("Expected one boolean argument"); + } else { + size_t argc = 1; + napi_value argv[1]; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); + + if (argc != 1) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected one boolean argument")); + return nullptr; + } + + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, argv[0], &t)); + + if (t != napi_boolean) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected one boolean argument")); + return nullptr; } - info.GetReturnValue().Set(get_singleton(Nan::To(info[0]).FromJust()).get_js_object()); + CHECK_NAPI_RESULT(napi_get_value_bool(env, argv[0], &r)); + napi_value obj = Boolean::get_singleton(r).get_js_object(env); + return obj; } + return nullptr; } - NAN_METHOD(Boolean::GetValue) { - info.GetReturnValue().Set(Boolean::Unwrap(info.This())->get_js_boolean()); + napi_value Boolean::GetValue(napi_env env, napi_callback_info info) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + Boolean *out = static_cast(Factory::unwrap(env, _this)); + if (out) { + napi_value b; + CHECK_NAPI_RESULT(napi_get_boolean(env, out->value, &b)); + return b; + } + return nullptr; } } diff --git a/src/sass_types/boolean.h b/src/sass_types/boolean.h index 721a41c02..ed63f3a71 100644 --- a/src/sass_types/boolean.h +++ b/src/sass_types/boolean.h @@ -1,30 +1,33 @@ #ifndef SASS_TYPES_BOOLEAN_H #define SASS_TYPES_BOOLEAN_H -#include #include "value.h" #include "sass_value_wrapper.h" +#include namespace SassTypes { class Boolean : public SassTypes::Value { public: static Boolean& get_singleton(bool); - static v8::Local get_constructor(); + static napi_value get_constructor(napi_env env); - v8::Local get_js_object(); + Sass_Value* get_sass_value(); + napi_value get_js_object(napi_env env); - static NAN_METHOD(New); - static NAN_METHOD(GetValue); + static napi_value New(napi_env env, napi_callback_info info); + static napi_value GetValue(napi_env env, napi_callback_info info); private: Boolean(bool); - Nan::Persistent js_object; + static napi_value construct_and_wrap_instance(napi_env env, napi_value ctor, Boolean* b); - static Nan::Persistent constructor; + bool value; + napi_ref js_object; + + static napi_ref constructor; static bool constructor_locked; - v8::Local get_js_boolean(); }; } diff --git a/src/sass_types/color.cpp b/src/sass_types/color.cpp index 40358a2c3..38d37f97f 100644 --- a/src/sass_types/color.cpp +++ b/src/sass_types/color.cpp @@ -1,21 +1,22 @@ -#include #include "color.h" namespace SassTypes { - Color::Color(Sass_Value* v) : SassValueWrapper(v) {} + Color::Color(napi_env env, Sass_Value* v) : SassValueWrapper(env, v) {} - Sass_Value* Color::construct(const std::vector> raw_val, Sass_Value **out) { + Sass_Value* Color::construct(napi_env env, const std::vector raw_val, Sass_Value **out) { double a = 1.0, r = 0, g = 0, b = 0; - unsigned argb; + napi_valuetype t; + uint32_t argb; switch (raw_val.size()) { case 1: - if (!raw_val[0]->IsNumber()) { + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + if (t != napi_number) { return fail("Only argument should be an integer.", out); } - argb = Nan::To(raw_val[0]).FromJust(); + CHECK_NAPI_RESULT(napi_get_value_uint32(env, raw_val[0], &argb)); a = (double)((argb >> 030) & 0xff) / 0xff; r = (double)((argb >> 020) & 0xff); g = (double)((argb >> 010) & 0xff); @@ -23,21 +24,30 @@ namespace SassTypes break; case 4: - if (!raw_val[3]->IsNumber()) { + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[3], &t)); + if (t != napi_number) { return fail("Constructor arguments should be numbers exclusively.", out); } - - a = Nan::To(raw_val[3]).FromJust(); - NODE_SASS_FALLTHROUGH; + CHECK_NAPI_RESULT(napi_get_value_double(env, raw_val[3], &a)); + // fall through vvv case 3: - if (!raw_val[0]->IsNumber() || !raw_val[1]->IsNumber() || !raw_val[2]->IsNumber()) { + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + if (t != napi_number) { + return fail("Constructor arguments should be numbers exclusively.", out); + } + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[1], &t)); + if (t != napi_number) { + return fail("Constructor arguments should be numbers exclusively.", out); + } + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[2], &t)); + if (t != napi_number) { return fail("Constructor arguments should be numbers exclusively.", out); } - r = Nan::To(raw_val[0]).FromJust(); - g = Nan::To(raw_val[1]).FromJust(); - b = Nan::To(raw_val[2]).FromJust(); + CHECK_NAPI_RESULT(napi_get_value_double(env, raw_val[0], &r)); + CHECK_NAPI_RESULT(napi_get_value_double(env, raw_val[1], &g)); + CHECK_NAPI_RESULT(napi_get_value_double(env, raw_val[2], &b)); break; case 0: @@ -50,78 +60,52 @@ namespace SassTypes return *out = sass_make_color(r, g, b, a); } - void Color::initPrototype(v8::Local proto) { - Nan::SetPrototypeMethod(proto, "getR", GetR); - Nan::SetPrototypeMethod(proto, "getG", GetG); - Nan::SetPrototypeMethod(proto, "getB", GetB); - Nan::SetPrototypeMethod(proto, "getA", GetA); - Nan::SetPrototypeMethod(proto, "setR", SetR); - Nan::SetPrototypeMethod(proto, "setG", SetG); - Nan::SetPrototypeMethod(proto, "setB", SetB); - Nan::SetPrototypeMethod(proto, "setA", SetA); + napi_value Color::getConstructor(napi_env env, napi_callback cb) { + napi_value ctor; + napi_property_descriptor descriptors[] = { + { "getR", nullptr, GetR }, + { "getG", nullptr, GetG }, + { "getB", nullptr, GetB }, + { "getA", nullptr, GetA }, + { "setR", nullptr, SetR }, + { "setG", nullptr, SetG }, + { "setB", nullptr, SetB }, + { "setA", nullptr, SetA }, + }; + + CHECK_NAPI_RESULT(napi_define_class(env, get_constructor_name(), NAPI_AUTO_LENGTH, cb, nullptr, 8, descriptors, &ctor)); + return ctor; } - NAN_METHOD(Color::GetR) { - info.GetReturnValue().Set(sass_color_get_r(Color::Unwrap(info.This())->value)); + napi_value Color::GetR(napi_env env, napi_callback_info info) { + return CommonGetNumber(env, info, sass_color_get_r); } - NAN_METHOD(Color::GetG) { - info.GetReturnValue().Set(sass_color_get_g(Color::Unwrap(info.This())->value)); + napi_value Color::GetG(napi_env env, napi_callback_info info) { + return CommonGetNumber(env, info, sass_color_get_g); } - NAN_METHOD(Color::GetB) { - info.GetReturnValue().Set(sass_color_get_b(Color::Unwrap(info.This())->value)); + napi_value Color::GetB(napi_env env, napi_callback_info info) { + return CommonGetNumber(env, info, sass_color_get_b); } - NAN_METHOD(Color::GetA) { - info.GetReturnValue().Set(sass_color_get_a(Color::Unwrap(info.This())->value)); + napi_value Color::GetA(napi_env env, napi_callback_info info) { + return CommonGetNumber(env, info, sass_color_get_a); } - NAN_METHOD(Color::SetR) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied value should be a number"); - } - - sass_color_set_r(Color::Unwrap(info.This())->value, Nan::To(info[0]).FromJust()); + napi_value Color::SetR(napi_env env, napi_callback_info info) { + return CommonSetNumber(env, info, sass_color_set_r); } - NAN_METHOD(Color::SetG) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied value should be a number"); - } - - sass_color_set_g(Color::Unwrap(info.This())->value, Nan::To(info[0]).FromJust()); + napi_value Color::SetG(napi_env env, napi_callback_info info) { + return CommonSetNumber(env, info, sass_color_set_g); } - NAN_METHOD(Color::SetB) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied value should be a number"); - } - - sass_color_set_b(Color::Unwrap(info.This())->value, Nan::To(info[0]).FromJust()); + napi_value Color::SetB(napi_env env, napi_callback_info info) { + return CommonSetNumber(env, info, sass_color_set_b); } - NAN_METHOD(Color::SetA) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied value should be a number"); - } - - sass_color_set_a(Color::Unwrap(info.This())->value, Nan::To(info[0]).FromJust()); + napi_value Color::SetA(napi_env env, napi_callback_info info) { + return CommonSetNumber(env, info, sass_color_set_a); } } diff --git a/src/sass_types/color.h b/src/sass_types/color.h index 1bf904307..e0b9c6119 100644 --- a/src/sass_types/color.h +++ b/src/sass_types/color.h @@ -1,33 +1,25 @@ #ifndef SASS_TYPES_COLOR_H #define SASS_TYPES_COLOR_H -#include #include "sass_value_wrapper.h" -#if defined(__GNUC__) && __GNUC__ >= 7 -#define NODE_SASS_FALLTHROUGH __attribute__ ((fallthrough)) -#else -#define NODE_SASS_FALLTHROUGH -#endif - namespace SassTypes { class Color : public SassValueWrapper { public: - Color(Sass_Value*); + Color(napi_env, Sass_Value*); static char const* get_constructor_name() { return "SassColor"; } - static Sass_Value* construct(const std::vector>, Sass_Value **); - - static void initPrototype(v8::Local); + static Sass_Value* construct(napi_env, const std::vector, Sass_Value **); + static napi_value getConstructor(napi_env, napi_callback); - static NAN_METHOD(GetR); - static NAN_METHOD(GetG); - static NAN_METHOD(GetB); - static NAN_METHOD(GetA); - static NAN_METHOD(SetR); - static NAN_METHOD(SetG); - static NAN_METHOD(SetB); - static NAN_METHOD(SetA); + static napi_value GetR(napi_env env, napi_callback_info info); + static napi_value GetG(napi_env env, napi_callback_info info); + static napi_value GetB(napi_env env, napi_callback_info info); + static napi_value GetA(napi_env env, napi_callback_info info); + static napi_value SetR(napi_env env, napi_callback_info info); + static napi_value SetG(napi_env env, napi_callback_info info); + static napi_value SetB(napi_env env, napi_callback_info info); + static napi_value SetA(napi_env env, napi_callback_info info); }; } diff --git a/src/sass_types/error.cpp b/src/sass_types/error.cpp index 03c6307c4..f3026e2cc 100644 --- a/src/sass_types/error.cpp +++ b/src/sass_types/error.cpp @@ -1,24 +1,30 @@ -#include #include "error.h" #include "../create_string.h" namespace SassTypes { - Error::Error(Sass_Value* v) : SassValueWrapper(v) {} + Error::Error(napi_env env, Sass_Value* v) : SassValueWrapper(env, v) {} - Sass_Value* Error::construct(const std::vector> raw_val, Sass_Value **out) { + Sass_Value* Error::construct(napi_env env, const std::vector raw_val, Sass_Value **out) { char const* value = ""; if (raw_val.size() >= 1) { - if (!raw_val[0]->IsString()) { - return fail("Argument should be a string.", out); + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + + if (t != napi_string) { + return fail("First argument should be a string.", out); } - value = create_string(raw_val[0]); + value = create_string(env, raw_val[0]); } return *out = sass_make_error(value); } - void Error::initPrototype(v8::Local) {} + napi_value Error::getConstructor(napi_env env, napi_callback cb) { + napi_value ctor; + CHECK_NAPI_RESULT(napi_define_class(env, get_constructor_name(), NAPI_AUTO_LENGTH, cb, nullptr, 0, nullptr, &ctor)); + return ctor; + } } diff --git a/src/sass_types/error.h b/src/sass_types/error.h index 01786fdaa..781b53cbb 100644 --- a/src/sass_types/error.h +++ b/src/sass_types/error.h @@ -1,18 +1,16 @@ #ifndef SASS_TYPES_ERROR_H #define SASS_TYPES_ERROR_H -#include #include "sass_value_wrapper.h" namespace SassTypes { class Error : public SassValueWrapper { public: - Error(Sass_Value*); + Error(napi_env, Sass_Value*); static char const* get_constructor_name() { return "SassError"; } - static Sass_Value* construct(const std::vector>, Sass_Value **); - - static void initPrototype(v8::Local); + static Sass_Value* construct(napi_env, const std::vector, Sass_Value **); + static napi_value getConstructor(napi_env, napi_callback); }; } diff --git a/src/sass_types/factory.cpp b/src/sass_types/factory.cpp index c650710c3..3ecf5e842 100644 --- a/src/sass_types/factory.cpp +++ b/src/sass_types/factory.cpp @@ -1,4 +1,3 @@ -#include #include "factory.h" #include "value.h" #include "number.h" @@ -9,64 +8,67 @@ #include "map.h" #include "null.h" #include "error.h" +#include "../common.h" namespace SassTypes { - SassTypes::Value* Factory::create(Sass_Value* v) { + SassTypes::Value* Factory::create(napi_env env, Sass_Value* v) { switch (sass_value_get_tag(v)) { case SASS_NUMBER: - return new Number(v); + return new Number(env, v); case SASS_STRING: - return new String(v); + return new String(env, v); case SASS_COLOR: - return new Color(v); + return new Color(env, v); case SASS_BOOLEAN: return &Boolean::get_singleton(sass_boolean_get_value(v)); case SASS_LIST: - return new List(v); + return new List(env, v); case SASS_MAP: - return new Map(v); + return new Map(env, v); case SASS_NULL: return &Null::get_singleton(); case SASS_ERROR: - return new Error(v); + return new Error(env, v); default: const char *msg = "Unknown type encountered."; - Nan::ThrowTypeError(msg); - return new Error(sass_make_error(msg)); + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, msg)); + return new Error(env, sass_make_error(msg)); } } - NAN_MODULE_INIT(Factory::initExports) { - Nan::HandleScope scope; - v8::Local types = Nan::New(); + void Factory::initExports(napi_env env, napi_value target) { + Napi::HandleScope scope(env); - Nan::Set(types, Nan::New("Number").ToLocalChecked(), Number::get_constructor()); - Nan::Set(types, Nan::New("String").ToLocalChecked(), String::get_constructor()); - Nan::Set(types, Nan::New("Color").ToLocalChecked(), Color::get_constructor()); - Nan::Set(types, Nan::New("Boolean").ToLocalChecked(), Boolean::get_constructor()); - Nan::Set(types, Nan::New("List").ToLocalChecked(), List::get_constructor()); - Nan::Set(types, Nan::New("Map").ToLocalChecked(), Map::get_constructor()); - Nan::Set(types, Nan::New("Null").ToLocalChecked(), Null::get_constructor()); - Nan::Set(types, Nan::New("Error").ToLocalChecked(), Error::get_constructor()); - Nan::Set(target, Nan::New("types").ToLocalChecked(), types); + napi_value types; + CHECK_NAPI_RESULT(napi_create_object(env, &types)); + + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "Number", Number::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "String", String::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "Color", Color::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "Boolean", Boolean::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "List", List::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "Map", Map::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "Null", Null::get_constructor(env))); + CHECK_NAPI_RESULT(napi_set_named_property(env, types, "Error", Error::get_constructor(env))); + + CHECK_NAPI_RESULT(napi_set_named_property(env, target, "types", types)); } - Value* Factory::unwrap(v8::Local obj) { - if (obj->IsObject()) { - v8::Local v8_obj = obj.As(); - if (v8_obj->InternalFieldCount() == 1) { - return SassTypes::Value::Unwrap(v8_obj); - } - } - return NULL; + Value* Factory::unwrap(napi_env env, napi_value obj) { + void* wrapped; + napi_status status = napi_unwrap(env, obj, &wrapped); + if (status != napi_ok) { + wrapped = nullptr; + } + return static_cast(wrapped); } } diff --git a/src/sass_types/factory.h b/src/sass_types/factory.h index 27b7e3fa4..51ada9a3c 100644 --- a/src/sass_types/factory.h +++ b/src/sass_types/factory.h @@ -1,9 +1,9 @@ #ifndef SASS_TYPES_FACTORY_H #define SASS_TYPES_FACTORY_H -#include #include #include "value.h" +#include namespace SassTypes { @@ -11,9 +11,9 @@ namespace SassTypes // to wrap a given Sass_Value object. class Factory { public: - static NAN_MODULE_INIT(initExports); - static Value* create(Sass_Value*); - static Value* unwrap(v8::Local); + static void initExports(napi_env, napi_value); + static Value* create(napi_env, Sass_Value*); + static Value* unwrap(napi_env, napi_value); }; } diff --git a/src/sass_types/list.cpp b/src/sass_types/list.cpp index 4c946ec90..ad5df466c 100644 --- a/src/sass_types/list.cpp +++ b/src/sass_types/list.cpp @@ -1,101 +1,103 @@ -#include #include "list.h" namespace SassTypes { - List::List(Sass_Value* v) : SassValueWrapper(v) {} + List::List(napi_env env, Sass_Value* v) : SassValueWrapper(env, v) {} - Sass_Value* List::construct(const std::vector> raw_val, Sass_Value **out) { - size_t length = 0; + Sass_Value* List::construct(napi_env env, const std::vector raw_val, Sass_Value **out) { + uint32_t length = 0; bool comma = true; bool is_bracketed = false; if (raw_val.size() >= 1) { - if (!raw_val[0]->IsNumber()) { + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + + if (t != napi_number) { return fail("First argument should be an integer.", out); } - length = Nan::To(raw_val[0]).FromJust(); + CHECK_NAPI_RESULT(napi_get_value_uint32(env, raw_val[0], &length)); if (raw_val.size() >= 2) { - if (!raw_val[1]->IsBoolean()) { + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[1], &t)); + + if (t != napi_boolean) { return fail("Second argument should be a boolean.", out); } - comma = Nan::To(raw_val[1]).FromJust(); + CHECK_NAPI_RESULT(napi_get_value_bool(env, raw_val[1], &comma)); } } return *out = sass_make_list(length, comma ? SASS_COMMA : SASS_SPACE, is_bracketed); } - void List::initPrototype(v8::Local proto) { - Nan::SetPrototypeMethod(proto, "getLength", GetLength); - Nan::SetPrototypeMethod(proto, "getSeparator", GetSeparator); - Nan::SetPrototypeMethod(proto, "setSeparator", SetSeparator); - Nan::SetPrototypeMethod(proto, "getValue", GetValue); - Nan::SetPrototypeMethod(proto, "setValue", SetValue); + napi_value List::getConstructor(napi_env env, napi_callback cb) { + napi_value ctor; + napi_property_descriptor descriptors[] = { + { "getLength", nullptr, GetLength }, + { "getSeparator", nullptr, GetSeparator }, + { "setSeparator", nullptr, SetSeparator }, + { "getValue", nullptr, GetValue }, + { "setValue", nullptr, SetValue }, + }; + + CHECK_NAPI_RESULT(napi_define_class(env, get_constructor_name(), NAPI_AUTO_LENGTH, cb, nullptr, 5, descriptors, &ctor)); + return ctor; } - NAN_METHOD(List::GetValue) { - - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied index should be an integer"); - } - - Sass_Value* list = List::Unwrap(info.This())->value; - size_t index = Nan::To(info[0]).FromJust(); - - - if (index >= sass_list_get_length(list)) { - return Nan::ThrowRangeError(Nan::New("Out of bound index").ToLocalChecked()); - } + napi_value List::GetValue(napi_env env, napi_callback_info info) { + return CommonGetIndexedValue(env, info, sass_list_get_length, sass_list_get_value); + } - info.GetReturnValue().Set(Factory::create(sass_list_get_value(list, Nan::To(info[0]).FromJust()))->get_js_object()); + napi_value List::SetValue(napi_env env, napi_callback_info info) { + return CommonSetIndexedValue(env, info, sass_list_set_value); } - NAN_METHOD(List::SetValue) { - if (info.Length() != 2) { - return Nan::ThrowTypeError("Expected two arguments"); - } + napi_value List::GetSeparator(napi_env env, napi_callback_info info) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied index should be an integer"); - } + bool v = sass_list_get_separator(unwrap(env, _this)->value) == SASS_COMMA; + napi_value ret; + CHECK_NAPI_RESULT(napi_get_boolean(env, v, &ret)); + return ret; + } - if (!info[1]->IsObject()) { - return Nan::ThrowTypeError("Supplied value should be a SassValue object"); - } + napi_value List::SetSeparator(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value arg; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr)); - Value* sass_value = Factory::unwrap(info[1]); - if (sass_value) { - sass_list_set_value(List::Unwrap(info.This())->value, Nan::To(info[0]).FromJust(), sass_value->get_sass_value()); - } else { - Nan::ThrowTypeError("A SassValue is expected as the list item"); + if (argc != 1) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected just one argument")); + return nullptr; } - } - NAN_METHOD(List::GetSeparator) { - info.GetReturnValue().Set(sass_list_get_separator(List::Unwrap(info.This())->value) == SASS_COMMA); - } + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, arg, &t)); - NAN_METHOD(List::SetSeparator) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); + if (t != napi_boolean) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Supplied value should be a boolean")); + return nullptr; } - if (!info[0]->IsBoolean()) { - return Nan::ThrowTypeError("Supplied value should be a boolean"); - } + bool b; + CHECK_NAPI_RESULT(napi_get_value_bool(env, arg, &b)); - sass_list_set_separator(List::Unwrap(info.This())->value, Nan::To(info[0]).FromJust() ? SASS_COMMA : SASS_SPACE); + sass_list_set_separator(unwrap(env, _this)->value, b ? SASS_COMMA : SASS_SPACE); + return nullptr; } - NAN_METHOD(List::GetLength) { - info.GetReturnValue().Set(Nan::New(sass_list_get_length(List::Unwrap(info.This())->value))); + napi_value List::GetLength(napi_env env, napi_callback_info info) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + size_t s = sass_list_get_length(unwrap(env, _this)->value); + napi_value ret; + CHECK_NAPI_RESULT(napi_create_double(env, (double)s, &ret)); + return ret; } } diff --git a/src/sass_types/list.h b/src/sass_types/list.h index c43b75484..b7de690fb 100644 --- a/src/sass_types/list.h +++ b/src/sass_types/list.h @@ -1,24 +1,22 @@ #ifndef SASS_TYPES_LIST_H #define SASS_TYPES_LIST_H -#include #include "sass_value_wrapper.h" namespace SassTypes { class List : public SassValueWrapper { public: - List(Sass_Value*); + List(napi_env, Sass_Value*); static char const* get_constructor_name() { return "SassList"; } - static Sass_Value* construct(const std::vector>, Sass_Value **); + static Sass_Value* construct(napi_env, const std::vector, Sass_Value **); + static napi_value getConstructor(napi_env, napi_callback); - static void initPrototype(v8::Local); - - static NAN_METHOD(GetValue); - static NAN_METHOD(SetValue); - static NAN_METHOD(GetSeparator); - static NAN_METHOD(SetSeparator); - static NAN_METHOD(GetLength); + static napi_value GetValue(napi_env env, napi_callback_info info); + static napi_value SetValue(napi_env env, napi_callback_info info); + static napi_value GetSeparator(napi_env env, napi_callback_info info); + static napi_value SetSeparator(napi_env env, napi_callback_info info); + static napi_value GetLength(napi_env env, napi_callback_info info); }; } diff --git a/src/sass_types/map.cpp b/src/sass_types/map.cpp index ae4a260bf..65cb77bcb 100644 --- a/src/sass_types/map.cpp +++ b/src/sass_types/map.cpp @@ -1,118 +1,63 @@ -#include #include "map.h" namespace SassTypes { - Map::Map(Sass_Value* v) : SassValueWrapper(v) {} + Map::Map(napi_env env, Sass_Value* v) : SassValueWrapper(env, v) {} - Sass_Value* Map::construct(const std::vector> raw_val, Sass_Value **out) { - size_t length = 0; + Sass_Value* Map::construct(napi_env env, const std::vector raw_val, Sass_Value **out) { + uint32_t length = 0; if (raw_val.size() >= 1) { - if (!raw_val[0]->IsNumber()) { + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + + if (t != napi_number) { return fail("First argument should be an integer.", out); } - length = Nan::To(raw_val[0]).FromJust(); + CHECK_NAPI_RESULT(napi_get_value_uint32(env, raw_val[0], &length)); } return *out = sass_make_map(length); } - void Map::initPrototype(v8::Local proto) { - Nan::SetPrototypeMethod(proto, "getLength", GetLength); - Nan::SetPrototypeMethod(proto, "getKey", GetKey); - Nan::SetPrototypeMethod(proto, "setKey", SetKey); - Nan::SetPrototypeMethod(proto, "getValue", GetValue); - Nan::SetPrototypeMethod(proto, "setValue", SetValue); + napi_value Map::getConstructor(napi_env env, napi_callback cb) { + napi_value ctor; + napi_property_descriptor descriptors[] = { + { "getLength", nullptr, GetLength }, + { "getKey", nullptr, GetKey }, + { "setKey", nullptr, SetKey }, + { "getValue", nullptr, GetValue }, + { "setValue", nullptr, SetValue }, + }; + + CHECK_NAPI_RESULT(napi_define_class(env, get_constructor_name(), NAPI_AUTO_LENGTH, cb, nullptr, 5, descriptors, &ctor)); + return ctor; } - NAN_METHOD(Map::GetValue) { - - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied index should be an integer"); - } - - Sass_Value* map = Map::Unwrap(info.This())->value; - size_t index = Nan::To(info[0]).FromJust(); - - - if (index >= sass_map_get_length(map)) { - return Nan::ThrowRangeError(Nan::New("Out of bound index").ToLocalChecked()); - } - - info.GetReturnValue().Set(Factory::create(sass_map_get_value(map, Nan::To(info[0]).FromJust()))->get_js_object()); + napi_value Map::GetValue(napi_env env, napi_callback_info info) { + return CommonGetIndexedValue(env, info, sass_map_get_length, sass_map_get_value); } - NAN_METHOD(Map::SetValue) { - if (info.Length() != 2) { - return Nan::ThrowTypeError("Expected two arguments"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied index should be an integer"); - } - - if (!info[1]->IsObject()) { - return Nan::ThrowTypeError("Supplied value should be a SassValue object"); - } - - Value* sass_value = Factory::unwrap(info[1]); - if (sass_value) { - sass_map_set_value(Map::Unwrap(info.This())->value, Nan::To(info[0]).FromJust(), sass_value->get_sass_value()); - } else { - Nan::ThrowTypeError("A SassValue is expected as a map value"); - } + napi_value Map::SetValue(napi_env env, napi_callback_info info) { + return CommonSetIndexedValue(env, info, sass_map_set_value); } - NAN_METHOD(Map::GetKey) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied index should be an integer"); - } - - Sass_Value* map = Map::Unwrap(info.This())->value; - size_t index = Nan::To(info[0]).FromJust(); - - - if (index >= sass_map_get_length(map)) { - return Nan::ThrowRangeError(Nan::New("Out of bound index").ToLocalChecked()); - } - - SassTypes::Value* obj = Factory::create(sass_map_get_key(map, Nan::To(info[0]).FromJust())); - v8::Local js_obj = obj->get_js_object(); - info.GetReturnValue().Set(js_obj); + napi_value Map::GetKey(napi_env env, napi_callback_info info) { + return CommonGetIndexedValue(env, info, sass_map_get_length, sass_map_get_key); } - NAN_METHOD(Map::SetKey) { - if (info.Length() != 2) { - return Nan::ThrowTypeError("Expected two arguments"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied index should be an integer"); - } - - if (!info[1]->IsObject()) { - return Nan::ThrowTypeError("Supplied value should be a SassValue object"); - } - - Value* sass_value = Factory::unwrap(info[1]); - if (sass_value) { - sass_map_set_key(Map::Unwrap(info.This())->value, Nan::To(info[0]).FromJust(), sass_value->get_sass_value()); - } else { - Nan::ThrowTypeError("A SassValue is expected as a map key"); - } + napi_value Map::SetKey(napi_env env, napi_callback_info info) { + return CommonSetIndexedValue(env, info, sass_map_set_key); } - NAN_METHOD(Map::GetLength) { - info.GetReturnValue().Set(Nan::New(sass_map_get_length(Map::Unwrap(info.This())->value))); + napi_value Map::GetLength(napi_env env, napi_callback_info info) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + size_t s = sass_map_get_length(unwrap(env, _this)->value); + napi_value ret; + CHECK_NAPI_RESULT(napi_create_double(env, (double)s, &ret)); + return ret; } } diff --git a/src/sass_types/map.h b/src/sass_types/map.h index 832585dd5..54c65c647 100644 --- a/src/sass_types/map.h +++ b/src/sass_types/map.h @@ -1,24 +1,22 @@ #ifndef SASS_TYPES_MAP_H #define SASS_TYPES_MAP_H -#include #include "sass_value_wrapper.h" namespace SassTypes { class Map : public SassValueWrapper { public: - Map(Sass_Value*); + Map(napi_env, Sass_Value*); static char const* get_constructor_name() { return "SassMap"; } - static Sass_Value* construct(const std::vector>, Sass_Value **); + static Sass_Value* construct(napi_env, const std::vector, Sass_Value **); + static napi_value getConstructor(napi_env, napi_callback); - static void initPrototype(v8::Local); - - static NAN_METHOD(GetValue); - static NAN_METHOD(SetValue); - static NAN_METHOD(GetKey); - static NAN_METHOD(SetKey); - static NAN_METHOD(GetLength); + static napi_value GetValue(napi_env env, napi_callback_info info); + static napi_value SetValue(napi_env env, napi_callback_info info); + static napi_value GetKey(napi_env env, napi_callback_info info); + static napi_value SetKey(napi_env env, napi_callback_info info); + static napi_value GetLength(napi_env env, napi_callback_info info); }; } diff --git a/src/sass_types/null.cpp b/src/sass_types/null.cpp index 69f4c216d..bc5d544c1 100644 --- a/src/sass_types/null.cpp +++ b/src/sass_types/null.cpp @@ -1,57 +1,73 @@ -#include #include "null.h" namespace SassTypes { - Nan::Persistent Null::constructor; + napi_ref Null::constructor = nullptr; bool Null::constructor_locked = false; - Null::Null() { - value = sass_make_null(); - } + Null::Null() : js_object(nullptr) {} Null& Null::get_singleton() { static Null singleton_instance; return singleton_instance; } - v8::Local Null::get_constructor() { - Nan::EscapableHandleScope scope; - v8::Local conslocal; - if (constructor.IsEmpty()) { - v8::Local tpl = Nan::New(New); + napi_value Null::construct_and_wrap_instance(napi_env env, napi_value ctor, Null* n) { + Napi::EscapableHandleScope scope(env); - tpl->SetClassName(Nan::New("SassNull").ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); + napi_value instance; + CHECK_NAPI_RESULT(napi_new_instance(env, ctor, 0, nullptr, &instance)); + CHECK_NAPI_RESULT(napi_wrap(env, instance, n, nullptr, nullptr, nullptr)); + CHECK_NAPI_RESULT(napi_create_reference(env, instance, 1, &(n->js_object))); - conslocal = Nan::GetFunction(tpl).ToLocalChecked(); - constructor.Reset(conslocal); + return scope.Escape(instance); + } - get_singleton().js_object.Reset(Nan::NewInstance(conslocal).ToLocalChecked()); - Nan::SetInternalFieldPointer(Nan::New(get_singleton().js_object), 0, &get_singleton()); - Nan::Set(conslocal, Nan::New("NULL").ToLocalChecked(), Nan::New(get_singleton().js_object)); + napi_value Null::get_constructor(napi_env env) { + Napi::EscapableHandleScope scope(env); + napi_value ctor; - constructor_locked = true; + if (Null::constructor) { + CHECK_NAPI_RESULT(napi_get_reference_value(env, Null::constructor, &ctor)); } else { - conslocal = Nan::New(constructor); + CHECK_NAPI_RESULT(napi_define_class(env, "SassNull", NAPI_AUTO_LENGTH, Null::New, nullptr, 0, nullptr, &ctor)); + CHECK_NAPI_RESULT(napi_create_reference(env, ctor, 1, &Null::constructor)); + + Null& singleton = get_singleton(); + napi_value instance = construct_and_wrap_instance(env, ctor, &singleton); + + CHECK_NAPI_RESULT(napi_set_named_property(env, ctor, "NULL", instance)); + + constructor_locked = true; } - return scope.Escape(conslocal); + return scope.Escape(ctor); + } + + Sass_Value* Null::get_sass_value() { + return sass_make_null(); } - v8::Local Null::get_js_object() { - return Nan::New(this->js_object); + napi_value Null::get_js_object(napi_env env) { + napi_value v; + CHECK_NAPI_RESULT(napi_get_reference_value(env, this->js_object, &v)); + return v; } - NAN_METHOD(Null::New) { + napi_value Null::New(napi_env env, napi_callback_info info) { + napi_value t; + CHECK_NAPI_RESULT(napi_get_new_target(env, info, &t)); + bool r = (t != nullptr); - if (info.IsConstructCall()) { + if (r) { if (constructor_locked) { - return Nan::ThrowTypeError("Cannot instantiate SassNull"); + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Cannot instantiate SassNull")); } } else { - info.GetReturnValue().Set(get_singleton().get_js_object()); + napi_value obj = Null::get_singleton().get_js_object(env); + return obj; } + return nullptr; } } diff --git a/src/sass_types/null.h b/src/sass_types/null.h index 15b64ba09..b01132fd4 100644 --- a/src/sass_types/null.h +++ b/src/sass_types/null.h @@ -1,27 +1,33 @@ #ifndef SASS_TYPES_NULL_H #define SASS_TYPES_NULL_H -#include #include "value.h" +// node-sass only builds with MSVC 2013 which doesn't appear to have char16_t defined +#define char16_t wchar_t + +#include + namespace SassTypes { class Null : public SassTypes::Value { public: static Null& get_singleton(); - static v8::Local get_constructor(); + static napi_value get_constructor(napi_env env); Sass_Value* get_sass_value(); - v8::Local get_js_object(); + napi_value get_js_object(napi_env env); - static NAN_METHOD(New); + static napi_value New(napi_env env, napi_callback_info info); private: Null(); - Nan::Persistent js_object; + static napi_value construct_and_wrap_instance(napi_env env, napi_value ctor, Null* n); + + napi_ref js_object; - static Nan::Persistent constructor; + static napi_ref constructor; static bool constructor_locked; }; } diff --git a/src/sass_types/number.cpp b/src/sass_types/number.cpp index d8d303ee0..56de8fea9 100644 --- a/src/sass_types/number.cpp +++ b/src/sass_types/number.cpp @@ -1,75 +1,64 @@ -#include #include "number.h" #include "../create_string.h" namespace SassTypes { - Number::Number(Sass_Value* v) : SassValueWrapper(v) {} + Number::Number(napi_env env, Sass_Value* v) : SassValueWrapper(env, v) {} - Sass_Value* Number::construct(const std::vector> raw_val, Sass_Value **out) { + Sass_Value* Number::construct(napi_env env, const std::vector raw_val, Sass_Value **out) { double value = 0; char const* unit = ""; if (raw_val.size() >= 1) { - if (!raw_val[0]->IsNumber()) { + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + + if (t != napi_number) { return fail("First argument should be a number.", out); } - value = Nan::To(raw_val[0]).FromJust(); + CHECK_NAPI_RESULT(napi_get_value_double(env, raw_val[0], &value)); if (raw_val.size() >= 2) { - if (!raw_val[1]->IsString()) { + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[1], &t)); + + if (t != napi_string) { return fail("Second argument should be a string.", out); } - unit = create_string(raw_val[1]); - *out = sass_make_number(value, unit); - delete unit; - return *out; - + unit = create_string(env, raw_val[1]); } } return *out = sass_make_number(value, unit); } - void Number::initPrototype(v8::Local proto) { - Nan::SetPrototypeMethod(proto, "getValue", GetValue); - Nan::SetPrototypeMethod(proto, "getUnit", GetUnit); - Nan::SetPrototypeMethod(proto, "setValue", SetValue); - Nan::SetPrototypeMethod(proto, "setUnit", SetUnit); + napi_value Number::getConstructor(napi_env env, napi_callback cb) { + napi_value ctor; + napi_property_descriptor descriptors [] = { + { "getValue", nullptr, GetValue }, + { "getUnit", nullptr, GetUnit }, + { "setValue", nullptr, SetValue }, + { "setUnit", nullptr, SetUnit }, + }; + + CHECK_NAPI_RESULT(napi_define_class(env, get_constructor_name(), NAPI_AUTO_LENGTH, cb, nullptr, 4, descriptors, &ctor)); + return ctor; } - NAN_METHOD(Number::GetValue) { - info.GetReturnValue().Set(Nan::New(sass_number_get_value(Number::Unwrap(info.This())->value))); + napi_value Number::GetValue(napi_env env, napi_callback_info info) { + return CommonGetNumber(env, info, sass_number_get_value); } - NAN_METHOD(Number::GetUnit) { - info.GetReturnValue().Set(Nan::New(sass_number_get_unit(Number::Unwrap(info.This())->value)).ToLocalChecked()); + napi_value Number::GetUnit(napi_env env, napi_callback_info info) { + return CommonGetString(env, info, sass_number_get_unit); } - NAN_METHOD(Number::SetValue) { - - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsNumber()) { - return Nan::ThrowTypeError("Supplied value should be a number"); - } - - sass_number_set_value(Number::Unwrap(info.This())->value, Nan::To(info[0]).FromJust()); + napi_value Number::SetValue(napi_env env, napi_callback_info info) { + return CommonSetNumber(env, info, sass_number_set_value); } - NAN_METHOD(Number::SetUnit) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsString()) { - return Nan::ThrowTypeError("Supplied value should be a string"); - } - - sass_number_set_unit(Number::Unwrap(info.This())->value, create_string(info[0])); + napi_value Number::SetUnit(napi_env env, napi_callback_info info) { + return CommonSetString(env, info, sass_number_set_unit); } } diff --git a/src/sass_types/number.h b/src/sass_types/number.h index 48a02361f..7c99c9dd1 100644 --- a/src/sass_types/number.h +++ b/src/sass_types/number.h @@ -1,24 +1,21 @@ #ifndef SASS_TYPES_NUMBER_H #define SASS_TYPES_NUMBER_H -#include #include "sass_value_wrapper.h" namespace SassTypes { - class Number : public SassValueWrapper { public: - Number(Sass_Value*); + Number(napi_env, Sass_Value*); static char const* get_constructor_name() { return "SassNumber"; } - static Sass_Value* construct(const std::vector>, Sass_Value **out); - - static void initPrototype(v8::Local); + static Sass_Value* construct(napi_env, const std::vector, Sass_Value **); + static napi_value getConstructor(napi_env, napi_callback); - static NAN_METHOD(GetValue); - static NAN_METHOD(GetUnit); - static NAN_METHOD(SetValue); - static NAN_METHOD(SetUnit); + static napi_value GetValue(napi_env env, napi_callback_info info); + static napi_value GetUnit(napi_env env, napi_callback_info info); + static napi_value SetValue(napi_env env, napi_callback_info info); + static napi_value SetUnit(napi_env env, napi_callback_info info); }; } diff --git a/src/sass_types/sass_value_wrapper.h b/src/sass_types/sass_value_wrapper.h index 52a351187..df5c3515b 100644 --- a/src/sass_types/sass_value_wrapper.h +++ b/src/sass_types/sass_value_wrapper.h @@ -3,99 +3,303 @@ #include #include -#include #include "value.h" #include "factory.h" +#include "../create_string.h" +#include namespace SassTypes { // Include this in any SassTypes::Value subclasses to handle all the heavy lifting of constructing JS // objects and wrapping sass values inside them template - /* class SassValueWrapper : public SassTypes::Value { */ - class SassValueWrapper : public SassTypes::Value { - public: - static char const* get_constructor_name() { return "SassValue"; } + class SassValueWrapper : public SassTypes::Value { + public: + static char const* get_constructor_name() { return "SassValue"; } - SassValueWrapper(Sass_Value* v) : Value(v) { } - v8::Local get_js_object(); + SassValueWrapper(napi_env, Sass_Value*); + virtual ~SassValueWrapper(); - static v8::Local get_constructor(); - static v8::Local get_constructor_template(); - static NAN_METHOD(New); - static Sass_Value *fail(const char *, Sass_Value **); + Sass_Value* get_sass_value(); + napi_value get_js_object(napi_env); - /* private: */ - static Nan::Persistent constructor; - }; + static napi_value get_constructor(napi_env); + static napi_value New(napi_env env, napi_callback_info info); + static Sass_Value *fail(const char *, Sass_Value **); + + protected: + Sass_Value* value; + static T* unwrap(napi_env, napi_value); + + static napi_value CommonGetNumber(napi_env env, napi_callback_info info, double(fnc)(const Sass_Value*)); + static napi_value CommonSetNumber(napi_env env, napi_callback_info info, void(fnc)(Sass_Value*, double)); + + static napi_value CommonGetString(napi_env env, napi_callback_info info, const char*(fnc)(const Sass_Value*)); + static napi_value CommonSetString(napi_env env, napi_callback_info info, void(fnc)(Sass_Value*, char*)); + + static napi_value CommonGetIndexedValue(napi_env env, napi_callback_info info, size_t(lenfnc)(const Sass_Value*), Sass_Value*(getfnc)(const Sass_Value*, size_t)); + static napi_value CommonSetIndexedValue(napi_env env, napi_callback_info info, void(setfnc)(Sass_Value*, size_t, Sass_Value*)); + + private: + static napi_ref constructor; + napi_ref js_object; + napi_env e; + }; template - Nan::Persistent SassValueWrapper::constructor; + napi_ref SassValueWrapper::constructor = nullptr; template - v8::Local SassValueWrapper::get_js_object() { - if (this->persistent().IsEmpty()) { - v8::Local wrapper = Nan::NewInstance(T::get_constructor()).ToLocalChecked(); - this->Wrap(wrapper); - } + SassValueWrapper::SassValueWrapper(napi_env env, Sass_Value* v) { + this->value = sass_clone_value(v); + this->e = env; + this->js_object = nullptr; + } + + template + SassValueWrapper::~SassValueWrapper() { + CHECK_NAPI_RESULT(napi_delete_reference(this->e, this->js_object)); + sass_delete_value(this->value); + } + + template + napi_value SassValueWrapper::CommonGetNumber(napi_env env, napi_callback_info info, double(fnc)(const Sass_Value*)) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + double d = fnc(unwrap(env, _this)->value); + + napi_value ret; + CHECK_NAPI_RESULT(napi_create_double(env, d, &ret)); + return ret; + } + + template + napi_value SassValueWrapper::CommonSetNumber(napi_env env, napi_callback_info info, void(fnc)(Sass_Value*, double)) { + size_t argc = 1; + napi_value arg; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr)); - return this->handle(); + if (argc != 1) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected just one argument")); + return nullptr; } + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, arg, &t)); + + if (t != napi_number) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Supplied value should be a number")); + return nullptr; + } + + double d; + CHECK_NAPI_RESULT(napi_get_value_double(env, arg, &d)); + + fnc(unwrap(env, _this)->value, d); + return nullptr; + } + + template + napi_value SassValueWrapper::CommonGetString(napi_env env, napi_callback_info info, const char*(fnc)(const Sass_Value*)) { + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, nullptr, nullptr, &_this, nullptr)); + + const char* v = fnc(unwrap(env, _this)->value); + int len = (int)strlen(v); + + napi_value str; + CHECK_NAPI_RESULT(napi_create_string_utf8(env, v, len, &str)); + return str; + } + template - v8::Local SassValueWrapper::get_constructor_template() { - Nan::EscapableHandleScope scope; - v8::Local tpl = Nan::New(New); - tpl->SetClassName(Nan::New(T::get_constructor_name()).ToLocalChecked()); - tpl->InstanceTemplate()->SetInternalFieldCount(1); - T::initPrototype(tpl); + napi_value SassValueWrapper::CommonSetString(napi_env env, napi_callback_info info, void(fnc)(Sass_Value*, char*)) { + size_t argc = 1; + napi_value arg; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr)); - return scope.Escape(tpl); + if (argc != 1) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected just one argument")); + return nullptr; } + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, arg, &t)); + + if (t != napi_string) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Supplied value should be a string")); + return nullptr; + } + + char* s = create_string(env, arg); + + fnc(unwrap(env, _this)->value, s); + return nullptr; + } + template - v8::Local SassValueWrapper::get_constructor() { - if (constructor.IsEmpty()) { - constructor.Reset(Nan::GetFunction(T::get_constructor_template()).ToLocalChecked()); - } + napi_value SassValueWrapper::CommonGetIndexedValue(napi_env env, napi_callback_info info, size_t(lenfnc)(const Sass_Value*), Sass_Value*(getfnc)(const Sass_Value*,size_t)) { + size_t argc = 1; + napi_value arg; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, &arg, &_this, nullptr)); + + if (argc != 1) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected just one argument")); + return nullptr; + } + + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, arg, &t)); + + if (t != napi_number) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Supplied index should be an integer")); + return nullptr; + } + + uint32_t index; + CHECK_NAPI_RESULT(napi_get_value_uint32(env, arg, &index)); + + Sass_Value* collection = unwrap(env, _this)->value; - return Nan::New(constructor); + if (index >= lenfnc(collection)) { + CHECK_NAPI_RESULT(napi_throw_range_error(env, nullptr, "Out of bound index")); + return nullptr; } + napi_value ret = Factory::create(env, getfnc(collection, index))->get_js_object(env); + return ret; + } + template - NAN_METHOD(SassValueWrapper::New) { - std::vector> localArgs(info.Length()); + napi_value SassValueWrapper::CommonSetIndexedValue(napi_env env, napi_callback_info info, void(setfnc)(Sass_Value*, size_t, Sass_Value*)) { + size_t argc = 2; + napi_value argv[2]; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, argv, &_this, nullptr)); - for (auto i = 0; i < info.Length(); ++i) { - localArgs[i] = info[i]; - } - if (info.IsConstructCall()) { - Sass_Value* value; - if (T::construct(localArgs, &value) != NULL) { - T* obj = new T(value); - sass_delete_value(value); - - obj->Wrap(info.This()); - info.GetReturnValue().Set(info.This()); - } else { - return Nan::ThrowError(Nan::New(sass_error_get_message(value)).ToLocalChecked()); - } + if (argc != 2) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Expected two arguments")); + return nullptr; + } + + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, argv[0], &t)); + + if (t != napi_number) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Supplied index should be an integer")); + return nullptr; + } + + CHECK_NAPI_RESULT(napi_typeof(env, argv[1], &t)); + + if (t != napi_object) { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "Supplied value should be a SassValue object")); + return nullptr; + } + + uint32_t v; + CHECK_NAPI_RESULT(napi_get_value_uint32(env, argv[0], &v)); + + Value* sass_value = Factory::unwrap(env, argv[1]); + if (sass_value) { + setfnc(unwrap(env, _this)->value, v, sass_value->get_sass_value()); + } + else { + CHECK_NAPI_RESULT(napi_throw_type_error(env, nullptr, "A SassValue is expected")); + } + return nullptr; + } + + template + Sass_Value* SassValueWrapper::get_sass_value() { + return sass_clone_value(this->value); + } + + template + napi_value SassValueWrapper::get_js_object(napi_env env) { + if (this->js_object == nullptr) { + napi_value wrapper; + napi_value ctor = T::get_constructor(env); + CHECK_NAPI_RESULT(napi_new_instance(env, ctor, 0, nullptr, &wrapper)); + void* wrapped; + CHECK_NAPI_RESULT(napi_remove_wrap(env, wrapper, &wrapped)); + delete static_cast(wrapped); + CHECK_NAPI_RESULT(napi_wrap(env, wrapper, this, nullptr, nullptr, nullptr)); + CHECK_NAPI_RESULT(napi_create_reference(env, wrapper, 1, &this->js_object)); + } + + napi_value v; + CHECK_NAPI_RESULT(napi_get_reference_value(env, this->js_object, &v)); + return v; + } + + template + napi_value SassValueWrapper::get_constructor(napi_env env) { + Napi::EscapableHandleScope scope(env); + + napi_value ctor; + if (!constructor) { + ctor = T::getConstructor(env, New); + CHECK_NAPI_RESULT(napi_create_reference(env, ctor, 1, &constructor)); + } + else { + CHECK_NAPI_RESULT(napi_get_reference_value(env, constructor, &ctor)); + } + + return scope.Escape(ctor); + } + + template + napi_value SassValueWrapper::New(napi_env env, napi_callback_info info) { + size_t argc = 0; + napi_value _this; + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, nullptr, &_this, nullptr)); + std::vector argv(argc); + CHECK_NAPI_RESULT(napi_get_cb_info(env, info, &argc, argv.data(), nullptr, nullptr)); + + napi_value t; + CHECK_NAPI_RESULT(napi_get_new_target(env, info, &t)); + bool r = (t != nullptr); + + if (r) { + Sass_Value* value; + if (T::construct(env, argv, &value) != NULL) { + T* obj = new T(env, value); + sass_delete_value(value); + + CHECK_NAPI_RESULT(napi_wrap(env, _this, obj, nullptr, nullptr, nullptr)); + CHECK_NAPI_RESULT(napi_create_reference(env, _this, 1, &obj->js_object)); + return _this; } else { - v8::Local cons = T::get_constructor(); - v8::Local inst; - if (Nan::NewInstance(cons, info.Length(), &localArgs[0]).ToLocal(&inst)) { - info.GetReturnValue().Set(inst); - } else { - info.GetReturnValue().Set(Nan::Undefined()); - } + CHECK_NAPI_RESULT(napi_throw_error(env, nullptr, sass_error_get_message(value))); + } + } else { + napi_value ctor = T::get_constructor(env); + napi_value instance; + napi_status status = napi_new_instance(env, ctor, argc, argv.data(), &instance); + if (status == napi_ok) { + return instance; } } + return nullptr; + } template - Sass_Value *SassValueWrapper::fail(const char *reason, Sass_Value **out) { - *out = sass_make_error(reason); - return NULL; - } + T* SassValueWrapper::unwrap(napi_env env, napi_value obj) { + /* This maybe NULL */ + return static_cast(Factory::unwrap(env, obj)); + } + + template + Sass_Value *SassValueWrapper::fail(const char *reason, Sass_Value **out) { + *out = sass_make_error(reason); + return NULL; + } } + #endif diff --git a/src/sass_types/string.cpp b/src/sass_types/string.cpp index c6f2c48fc..fd59fb802 100644 --- a/src/sass_types/string.cpp +++ b/src/sass_types/string.cpp @@ -1,48 +1,43 @@ -#include #include "string.h" #include "../create_string.h" namespace SassTypes { - String::String(Sass_Value* v) : SassValueWrapper(v) {} + String::String(napi_env env, Sass_Value* v) : SassValueWrapper(env, v) {} - Sass_Value* String::construct(const std::vector> raw_val, Sass_Value **out) { + Sass_Value* String::construct(napi_env env, const std::vector raw_val, Sass_Value **out) { char const* value = ""; if (raw_val.size() >= 1) { - if (!raw_val[0]->IsString()) { + napi_valuetype t; + CHECK_NAPI_RESULT(napi_typeof(env, raw_val[0], &t)); + + if (t != napi_string) { return fail("Argument should be a string.", out); } - value = create_string(raw_val[0]); - *out = sass_make_string(value); - delete value; - return *out; - - } else { - return *out = sass_make_string(value); + value = create_string(env, raw_val[0]); } + return *out = sass_make_string(value); } - void String::initPrototype(v8::Local proto) { - Nan::SetPrototypeMethod(proto, "getValue", GetValue); - Nan::SetPrototypeMethod(proto, "setValue", SetValue); - } + napi_value String::getConstructor(napi_env env, napi_callback cb) { + napi_value ctor; + napi_property_descriptor descriptors[] = { + { "getValue", nullptr, GetValue }, + { "setValue", nullptr, SetValue }, + }; - NAN_METHOD(String::GetValue) { - info.GetReturnValue().Set(Nan::New(sass_string_get_value(String::Unwrap(info.This())->value)).ToLocalChecked()); + CHECK_NAPI_RESULT(napi_define_class(env, get_constructor_name(), NAPI_AUTO_LENGTH, cb, nullptr, 2, descriptors, &ctor)); + return ctor; } - NAN_METHOD(String::SetValue) { - if (info.Length() != 1) { - return Nan::ThrowTypeError("Expected just one argument"); - } - - if (!info[0]->IsString()) { - return Nan::ThrowTypeError("Supplied value should be a string"); - } + napi_value String::GetValue(napi_env env, napi_callback_info info) { + return CommonGetString(env, info, sass_string_get_value); + } - sass_string_set_value(String::Unwrap(info.This())->value, create_string(info[0])); + napi_value String::SetValue(napi_env env, napi_callback_info info) { + return CommonSetString(env, info, sass_string_set_value); } } diff --git a/src/sass_types/string.h b/src/sass_types/string.h index 2e72c8251..8152cbffb 100644 --- a/src/sass_types/string.h +++ b/src/sass_types/string.h @@ -1,21 +1,20 @@ #ifndef SASS_TYPES_STRING_H #define SASS_TYPES_STRING_H -#include #include "sass_value_wrapper.h" namespace SassTypes { class String : public SassValueWrapper { public: - String(Sass_Value*); + String(napi_env, Sass_Value*); static char const* get_constructor_name() { return "SassString"; } - static Sass_Value* construct(const std::vector>, Sass_Value **); - static void initPrototype(v8::Local); + static Sass_Value* construct(napi_env, const std::vector, Sass_Value **); + static napi_value getConstructor(napi_env, napi_callback); - static NAN_METHOD(GetValue); - static NAN_METHOD(SetValue); + static napi_value GetValue(napi_env env, napi_callback_info info); + static napi_value SetValue(napi_env env, napi_callback_info info); }; } diff --git a/src/sass_types/value.h b/src/sass_types/value.h index fa4703cb3..ec1a977ac 100644 --- a/src/sass_types/value.h +++ b/src/sass_types/value.h @@ -1,41 +1,17 @@ #ifndef SASS_TYPES_VALUE_H #define SASS_TYPES_VALUE_H -#include #include +#include "../common.h" +#include namespace SassTypes { // This is the interface that all sass values must comply with - class Value : public Nan::ObjectWrap { - + class Value { public: - virtual v8::Local get_js_object() =0; - - Value() { - - } - - Sass_Value* get_sass_value() { - return sass_clone_value(this->value); - } - - protected: - - Sass_Value* value; - - Value(Sass_Value* v) { - this->value = sass_clone_value(v); - } - - ~Value() { - sass_delete_value(this->value); - } - - static Sass_Value* fail(const char *reason, Sass_Value **out) { - *out = sass_make_error(reason); - return NULL; - } + virtual Sass_Value* get_sass_value() =0; + virtual napi_value get_js_object(napi_env env) = 0; }; } diff --git a/test/api.js b/test/api.js index 129c60d5e..be0b5d83f 100644 --- a/test/api.js +++ b/test/api.js @@ -1112,7 +1112,7 @@ describe('api', function() { } } }, function(error) { - assert.ok(/A SassValue is expected as the list item/.test(error.message)); + assert.ok(/A SassValue is expected/.test(error.message)); done(); }); }); @@ -1129,7 +1129,7 @@ describe('api', function() { } } }, function(error) { - assert.ok(/A SassValue is expected as a map key/.test(error.message)); + assert.ok(/A SassValue is expected/.test(error.message)); done(); }); }); @@ -1146,7 +1146,7 @@ describe('api', function() { } } }, function(error) { - assert.ok(/A SassValue is expected as a map value/.test(error.message)); + assert.ok(/A SassValue is expected/.test(error.message)); done(); }); }); diff --git a/test/binding.js b/test/binding.js index 4997d3ecf..c5d12024e 100644 --- a/test/binding.js +++ b/test/binding.js @@ -100,14 +100,16 @@ describe('binding', function() { describe('with an unsupported runtime', function() { beforeEach(function() { - Object.defineProperty(process.versions, 'modules', { + Object.defineProperty(process.versions, 'napi', { value: 'baz', + configurable: true, }); }); afterEach(function() { - Object.defineProperty(process.versions, 'modules', { - value: 51, + Object.defineProperty(process.versions, 'napi', { + value: 3, + configurable: true, }); }); diff --git a/test/cli.js b/test/cli.js index e15fbb093..8ea28d325 100644 --- a/test/cli.js +++ b/test/cli.js @@ -9,6 +9,10 @@ var assert = require('assert'), cli = path.join(__dirname, '..', 'bin', 'node-sass'), fixture = path.join.bind(null, __dirname, 'fixtures'); +function isNapiError(error) { + return /N-API is an experimental/.exec(error.toString()); +} + describe('cli', function() { // For some reason we experience random timeout failures in CI // due to spawn hanging/failing silently. See #1692. @@ -168,8 +172,10 @@ describe('cli', function() { var bin = spawn(cli, [src, dest, '--quiet']); var didEmit = false; - bin.stderr.once('data', function() { - didEmit = true; + bin.stderr.on('data', function(code) { + if (!isNapiError(code)) { + didEmit = true; + } }); bin.once('close', function() { @@ -750,9 +756,11 @@ describe('cli', function() { '--importer', fixture('extras/my_custom_importer_error.js') ]); - bin.stderr.once('data', function(code) { - assert.equal(JSON.parse(code).message, 'doesn\'t exist!'); - done(); + bin.stderr.on('data', function(code) { + if (!isNapiError(code)) { + assert.equal(JSON.parse(code).message, 'doesn\'t exist!'); + done(); + } }); }); });