From 5211c5df8d5d7ca3901a50b7afcc5b9039cf1218 Mon Sep 17 00:00:00 2001 From: Adeel Date: Fri, 20 Mar 2015 11:34:14 +0200 Subject: [PATCH] ~interim commit~custom importers~ --- binding.gyp | 15 ++- lib/index.js | 34 ++++- src/binding.cpp | 238 ++++++++++++--------------------- src/callback_bridge.h | 171 +++++++++++++++++++++++ src/create_string.cpp | 12 ++ src/create_string.h | 10 ++ src/custom_function_bridge.cpp | 21 +++ src/custom_function_bridge.h | 20 +++ src/custom_importer_bridge.cpp | 51 +++++++ src/custom_importer_bridge.h | 22 +++ src/sass_context_wrapper.cpp | 23 ++-- src/sass_context_wrapper.h | 17 ++- src/sass_types/boolean.cpp | 44 ++++++ src/sass_types/boolean.h | 25 ++++ src/sass_types/color.cpp | 141 +++++++++++++++++++ src/sass_types/color.h | 33 +++++ src/sass_types/core_value.h | 110 +++++++++++++++ src/sass_types/error.cpp | 24 ++++ src/sass_types/error.h | 22 +++ src/sass_types/factory.cpp | 71 ++++++++++ src/sass_types/factory.h | 24 ++++ src/sass_types/list.cpp | 98 ++++++++++++++ src/sass_types/list.h | 30 +++++ src/sass_types/map.cpp | 110 +++++++++++++++ src/sass_types/map.h | 28 ++++ src/sass_types/null.cpp | 14 ++ src/sass_types/null.h | 24 ++++ src/sass_types/number.cpp | 72 ++++++++++ src/sass_types/number.h | 30 +++++ src/sass_types/string.cpp | 47 +++++++ src/sass_types/string.h | 28 ++++ src/sass_types/value.cpp | 14 ++ src/sass_types/value.h | 24 ++++ 33 files changed, 1465 insertions(+), 182 deletions(-) create mode 100644 src/callback_bridge.h create mode 100644 src/create_string.cpp create mode 100644 src/create_string.h create mode 100644 src/custom_function_bridge.cpp create mode 100644 src/custom_function_bridge.h create mode 100644 src/custom_importer_bridge.cpp create mode 100644 src/custom_importer_bridge.h create mode 100644 src/sass_types/boolean.cpp create mode 100644 src/sass_types/boolean.h create mode 100644 src/sass_types/color.cpp create mode 100644 src/sass_types/color.h create mode 100644 src/sass_types/core_value.h create mode 100644 src/sass_types/error.cpp create mode 100644 src/sass_types/error.h create mode 100644 src/sass_types/factory.cpp create mode 100644 src/sass_types/factory.h create mode 100644 src/sass_types/list.cpp create mode 100644 src/sass_types/list.h create mode 100644 src/sass_types/map.cpp create mode 100644 src/sass_types/map.h create mode 100644 src/sass_types/null.cpp create mode 100644 src/sass_types/null.h create mode 100644 src/sass_types/number.cpp create mode 100644 src/sass_types/number.h create mode 100644 src/sass_types/string.cpp create mode 100644 src/sass_types/string.h create mode 100644 src/sass_types/value.cpp create mode 100644 src/sass_types/value.h diff --git a/binding.gyp b/binding.gyp index 5cd6af875..76d0edb90 100644 --- a/binding.gyp +++ b/binding.gyp @@ -4,7 +4,20 @@ 'target_name': 'binding', 'sources': [ 'src/binding.cpp', - 'src/sass_context_wrapper.cpp' + 'src/create_string.cpp', + 'src/custom_function_bridge.cpp', + 'src/custom_importer_bridge.cpp', + 'src/sass_context_wrapper.cpp', + 'src/sass_types/boolean.cpp', + 'src/sass_types/color.cpp', + 'src/sass_types/error.cpp', + 'src/sass_types/factory.cpp', + 'src/sass_types/list.cpp', + 'src/sass_types/map.cpp', + 'src/sass_types/null.cpp', + 'src/sass_types/number.cpp', + 'src/sass_types/string.cpp', + 'src/sass_types/value.cpp' ], 'include_dirs': [ ' #include #include "sass_context_wrapper.h" +#include "custom_function_bridge.h" +#include "create_string.h" +#include "sass_types/factory.h" -char* create_string(Local value) { - if (value->IsNull() || !value->IsString()) { - return 0; - } - - String::Utf8Value string(value); - char *str = (char *)malloc(string.length() + 1); - strcpy(str, *string); - return str; -} - -std::vector imports_collection; - -void prepare_import_results(Local returned_value, sass_context_wrapper* ctx_w) { - NanScope(); - - if (returned_value->IsArray()) { - Handle array = Handle::Cast(returned_value); - - ctx_w->imports = sass_make_import_list(array->Length()); - - for (size_t i = 0; i < array->Length(); ++i) { - Local value = array->Get(static_cast(i)); - - if (!value->IsObject()) - continue; - - Local object = Local::Cast(value); - char* path = create_string(object->Get(NanNew("file"))); - char* contents = create_string(object->Get(NanNew("contents"))); - - ctx_w->imports[i] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0); - } - } - else if (returned_value->IsObject()) { - ctx_w->imports = sass_make_import_list(1); - Local object = Local::Cast(returned_value); - char* path = create_string(object->Get(NanNew("file"))); - char* contents = create_string(object->Get(NanNew("contents"))); - - ctx_w->imports[0] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0); - } - else { - ctx_w->imports = sass_make_import_list(1); - ctx_w->imports[0] = sass_make_import_entry(ctx_w->file, 0, 0); - } -} - -void dispatched_async_uv_callback(uv_async_t *req) { - NanScope(); - sass_context_wrapper* ctx_w = static_cast(req->data); - - TryCatch try_catch; - - imports_collection.push_back(ctx_w); - - Handle argv[] = { - NanNew(strdup(ctx_w->file ? ctx_w->file : 0)), - NanNew(strdup(ctx_w->prev ? ctx_w->prev : 0)), - NanNew(imports_collection.size() - 1) - }; - - NanNew(ctx_w->importer_callback->Call(3, argv)); - - if (try_catch.HasCaught()) { - node::FatalException(try_catch); - } -} - -struct Sass_Import** sass_importer(const char* file, const char* prev, void* cookie) +struct Sass_Import** SassImporter(const char* file, const char* prev, void* cookie) { sass_context_wrapper* ctx_w = static_cast(cookie); + CustomImporterBridge& bridge = *(ctx_w->importer_bridge); - if (!ctx_w->is_sync) { - /* that is async: Render() or RenderFile(), - * the default even loop is unblocked so it - * can run uv_async_send without a push. - */ + std::vector argv; + argv.push_back((void*)file); + argv.push_back((void*)prev); - std::unique_lock lock(*ctx_w->importer_mutex); + return bridge(argv); +} - ctx_w->file = file ? strdup(file) : 0; - ctx_w->prev = prev ? strdup(prev) : 0; - ctx_w->async.data = (void*)ctx_w; +union Sass_Value* SassCustomFunction(const union Sass_Value* s_args, void* cookie) +{ + CustomFunctionBridge& bridge = *(static_cast(cookie)); - uv_async_send(&ctx_w->async); - ctx_w->importer_condition_variable->wait(lock); + std::vector argv; + for (unsigned l = sass_list_get_length(s_args), i = 0; i < l; i++) { + argv.push_back((void*)sass_list_get_value(s_args, i)); } - else { - NanScope(); - - Handle argv[] = { - NanNew(file), - NanNew(prev) - }; - - Local returned_value = Local::Cast(NanNew(ctx_w->importer_callback->Call(2, argv))); - prepare_import_results(returned_value->Get(NanNew("objectLiteral")), ctx_w); + try { + return bridge(argv); + } + catch (const std::exception& e) { + return sass_make_error(e.what()); } - - return ctx_w->imports; } -void extract_options(Local options, void* cptr, sass_context_wrapper* ctx_w, bool is_file, bool is_sync) { +void ExtractOptions(Local options, void* cptr, sass_context_wrapper* ctx_w, bool is_file, bool is_sync) { NanScope(); struct Sass_Context* ctx; @@ -124,7 +52,6 @@ void extract_options(Local options, void* cptr, sass_context_wrapper* ct struct Sass_Options* sass_options = sass_context_get_options(ctx); - ctx_w->importer_callback = NULL; ctx_w->is_sync = is_sync; if (!is_sync) { @@ -141,28 +68,52 @@ void extract_options(Local options, void* cptr, sass_context_wrapper* ct Local importer_callback = Local::Cast(options->Get(NanNew("importer"))); if (importer_callback->IsFunction()) { - ctx_w->importer_callback = new NanCallback(importer_callback); - uv_async_init(uv_default_loop(), &ctx_w->async, (uv_async_cb)dispatched_async_uv_callback); - sass_option_set_importer(sass_options, sass_make_importer(sass_importer, ctx_w)); + ctx_w->importer_bridge = new CustomImporterBridge(new NanCallback(importer_callback), ctx_w->is_sync); + sass_option_set_importer(sass_options, sass_make_importer(SassImporter, ctx_w)); } if (!is_file) { - sass_option_set_input_path(sass_options, create_string(options->Get(NanNew("file")))); + sass_option_set_input_path(sass_options, CreateString(options->Get(NanNew("file")))); } - sass_option_set_output_path(sass_options, create_string(options->Get(NanNew("outFile")))); + sass_option_set_output_path(sass_options, CreateString(options->Get(NanNew("outFile")))); sass_option_set_output_style(sass_options, (Sass_Output_Style)options->Get(NanNew("style"))->Int32Value()); sass_option_set_is_indented_syntax_src(sass_options, options->Get(NanNew("indentedSyntax"))->BooleanValue()); sass_option_set_source_comments(sass_options, options->Get(NanNew("sourceComments"))->BooleanValue()); sass_option_set_omit_source_map_url(sass_options, options->Get(NanNew("omitSourceMapUrl"))->BooleanValue()); sass_option_set_source_map_embed(sass_options, options->Get(NanNew("sourceMapEmbed"))->BooleanValue()); sass_option_set_source_map_contents(sass_options, options->Get(NanNew("sourceMapContents"))->BooleanValue()); - sass_option_set_source_map_file(sass_options, create_string(options->Get(NanNew("sourceMap")))); - sass_option_set_include_path(sass_options, create_string(options->Get(NanNew("includePaths")))); + sass_option_set_source_map_file(sass_options, CreateString(options->Get(NanNew("sourceMap")))); + sass_option_set_include_path(sass_options, CreateString(options->Get(NanNew("includePaths")))); sass_option_set_precision(sass_options, options->Get(NanNew("precision"))->Int32Value()); + + Local custom_functions = Local::Cast(options->Get(NanNew("functions"))); + + if (custom_functions->IsObject()) { + Local signatures = custom_functions->GetOwnPropertyNames(); + unsigned num_signatures = signatures->Length(); + Sass_C_Function_List fn_list = sass_make_function_list(num_signatures); + + for (unsigned i = 0; i < num_signatures; i++) { + Local signature = Local::Cast(signatures->Get(NanNew(i))); + Local callback = Local::Cast(custom_functions->Get(signature)); + + if (!signature->IsString() || !callback->IsFunction()) { + NanThrowError(NanNew("options.functions must be a (signature -> function) hash")); + } + + CustomFunctionBridge* bridge = new CustomFunctionBridge(new NanCallback(callback), ctx_w->is_sync); + ctx_w->function_bridges.push_back(bridge); + + Sass_C_Function_Callback fn = sass_make_function(CreateString(signature), SassCustomFunction, bridge); + sass_function_set_list_entry(fn_list, i, fn); + } + + sass_option_set_c_functions(sass_options, fn_list); + } } -void get_stats(sass_context_wrapper* ctx_w, Sass_Context* ctx) { +void GetStats(sass_context_wrapper* ctx_w, Sass_Context* ctx) { NanScope(); char** included_files = sass_context_get_included_files(ctx); @@ -177,7 +128,7 @@ void get_stats(sass_context_wrapper* ctx_w, Sass_Context* ctx) { NanNew(ctx_w->result)->Get(NanNew("stats"))->ToObject()->Set(NanNew("includedFiles"), arr); } -int get_result(sass_context_wrapper* ctx_w, Sass_Context* ctx, bool is_sync = false) { +int GetResult(sass_context_wrapper* ctx_w, Sass_Context* ctx, bool is_sync = false) { NanScope(); int status = sass_context_get_error_status(ctx); @@ -188,7 +139,7 @@ int get_result(sass_context_wrapper* ctx_w, Sass_Context* ctx, bool is_sync = fa NanNew(ctx_w->result)->Set(NanNew("css"), NanNewBufferHandle(css, static_cast(strlen(css)))); - get_stats(ctx_w, ctx); + GetStats(ctx_w, ctx); if (map) { NanNew(ctx_w->result)->Set(NanNew("map"), NanNewBufferHandle(map, static_cast(strlen(map)))); @@ -201,7 +152,7 @@ int get_result(sass_context_wrapper* ctx_w, Sass_Context* ctx, bool is_sync = fa return status; } -void make_callback(uv_work_t* req) { +void MakeCallback(uv_work_t* req) { NanScope(); TryCatch try_catch; @@ -215,7 +166,7 @@ void make_callback(uv_work_t* req) { ctx = sass_file_context_get_context(ctx_w->fctx); } - int status = get_result(ctx_w, ctx); + int status = GetResult(ctx_w, ctx); if (status == 0 && ctx_w->success_callback) { // if no error, do callback(null, result) @@ -233,117 +184,92 @@ void make_callback(uv_work_t* req) { node::FatalException(try_catch); } - if (ctx_w->importer_callback) { - uv_close((uv_handle_t*)&ctx_w->async, NULL); - } + //if (ctx_w->importer_bridge) { + // uv_close((uv_handle_t*)&ctx_w->async, NULL); + //} sass_free_context_wrapper(ctx_w); } -NAN_METHOD(render) { +NAN_METHOD(Render) { NanScope(); Local options = args[0]->ToObject(); - char* source_string = create_string(options->Get(NanNew("data"))); + char* source_string = CreateString(options->Get(NanNew("data"))); struct Sass_Data_Context* dctx = sass_make_data_context(source_string); sass_context_wrapper* ctx_w = sass_make_context_wrapper(); - extract_options(options, dctx, ctx_w, false, false); + ExtractOptions(options, dctx, ctx_w, false, false); - int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)make_callback); + int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); assert(status == 0); NanReturnUndefined(); } -NAN_METHOD(render_sync) { +NAN_METHOD(RenderSync) { NanScope(); Local options = args[0]->ToObject(); - char* source_string = create_string(options->Get(NanNew("data"))); + char* source_string = CreateString(options->Get(NanNew("data"))); 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(); - extract_options(options, dctx, ctx_w, false, true); + ExtractOptions(options, dctx, ctx_w, false, true); compile_data(dctx); - int result = get_result(ctx_w, ctx, true); + int result = GetResult(ctx_w, ctx, true); sass_free_context_wrapper(ctx_w); NanReturnValue(NanNew(result == 0)); } -NAN_METHOD(render_file) { +NAN_METHOD(RenderFile) { NanScope(); Local options = args[0]->ToObject(); - char* input_path = create_string(options->Get(NanNew("file"))); + char* input_path = CreateString(options->Get(NanNew("file"))); struct Sass_File_Context* fctx = sass_make_file_context(input_path); sass_context_wrapper* ctx_w = sass_make_context_wrapper(); - extract_options(options, fctx, ctx_w, true, false); + ExtractOptions(options, fctx, ctx_w, true, false); - int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)make_callback); + int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback); assert(status == 0); NanReturnUndefined(); } -NAN_METHOD(render_file_sync) { +NAN_METHOD(RenderFileSync) { NanScope(); Local options = args[0]->ToObject(); - char* input_path = create_string(options->Get(NanNew("file"))); + char* input_path = CreateString(options->Get(NanNew("file"))); 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(); - extract_options(options, fctx, ctx_w, true, true); + ExtractOptions(options, fctx, ctx_w, true, true); compile_file(fctx); - int result = get_result(ctx_w, ctx, true); + int result = GetResult(ctx_w, ctx, true); sass_wrapper_dispose(ctx_w, input_path); NanReturnValue(NanNew(result == 0)); } -NAN_METHOD(imported_callback) { - NanScope(); - - TryCatch try_catch; - - Local options = args[0]->ToObject(); - Local returned_value = options->Get(NanNew("objectLiteral")); - size_t index = options->Get(NanNew("index"))->Int32Value(); - - if (index >= imports_collection.size()) { - NanReturnUndefined(); - } - - sass_context_wrapper* ctx_w = imports_collection[index]; - - prepare_import_results(returned_value, ctx_w); - ctx_w->importer_condition_variable->notify_all(); - - if (try_catch.HasCaught()) { - node::FatalException(try_catch); - } - - NanReturnValue(NanNew(0)); -} - void RegisterModule(v8::Handle target) { - NODE_SET_METHOD(target, "render", render); - NODE_SET_METHOD(target, "renderSync", render_sync); - NODE_SET_METHOD(target, "renderFile", render_file); - NODE_SET_METHOD(target, "renderFileSync", render_file_sync); - NODE_SET_METHOD(target, "importedCallback", imported_callback); + NODE_SET_METHOD(target, "render", Render); + NODE_SET_METHOD(target, "renderSync", RenderSync); + NODE_SET_METHOD(target, "renderFile", RenderFile); + NODE_SET_METHOD(target, "renderFileSync", RenderFileSync); + SassTypes::Factory::initExports(target); } NODE_MODULE(binding, RegisterModule); diff --git a/src/callback_bridge.h b/src/callback_bridge.h new file mode 100644 index 000000000..a231652d3 --- /dev/null +++ b/src/callback_bridge.h @@ -0,0 +1,171 @@ +#ifndef CALLBACK_BRIDGE_H +#define CALLBACK_BRIDGE_H + +#include +#include +#include +#include + +#define COMMA , + +using namespace v8; + +template +class CallbackBridge { + public: + CallbackBridge(NanCallback*, bool); + virtual ~CallbackBridge(); + + // Executes the callback + T operator()(std::vector); + + 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 Handle get_wrapper_constructor(); + static NAN_METHOD(New); + static NAN_METHOD(ReturnCallback); + static Persistent wrapper_constructor; + Persistent 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() + static void dispatched_async_uv_callback(uv_async_t*); + + // 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(Handle) const =0; + + + virtual std::vector> pre_process_args(std::vector) const =0; + + NanCallback* callback; + bool is_sync; + + std::mutex cv_mutex; + std::condition_variable condition_variable; + uv_async_t async; + std::vector argv; + bool has_returned; + T return_value; +}; + + +template +Persistent CallbackBridge::wrapper_constructor; + + +template +CallbackBridge::CallbackBridge(NanCallback* callback, bool is_sync) : callback(callback), is_sync(is_sync) { + // This assumes the main thread will be the one instantiating the bridge + if (!is_sync) { + uv_async_init(uv_default_loop(), &this->async, (uv_async_cb) dispatched_async_uv_callback); + this->async.data = (void*) this; + } + + NanAssignPersistent(wrapper, NanNew(CallbackBridge::get_wrapper_constructor())->NewInstance()); + NanSetInternalFieldPointer(NanNew(wrapper), 0, this); +} + + +template +CallbackBridge::~CallbackBridge() { + delete this->callback; + NanDisposePersistent(this->wrapper); + + if (!is_sync) { + uv_close((uv_handle_t*)&this->async, NULL); + } +} + + +template +T CallbackBridge::operator()(std::vector argv) { + // argv.push_back(wrapper); + + if (this->is_sync) { + std::vector> argv_v8 = pre_process_args(argv); + argv_v8.push_back(NanNew(wrapper)); + + return this->post_process_return_value( + NanNew(this->callback->Call(argv_v8.size(), &argv_v8[0])) + ); + } + + this->argv = argv; + + std::unique_lock lock(this->cv_mutex); + this->has_returned = false; + uv_async_send(&this->async); + this->condition_variable.wait(lock, [this] { return this->has_returned; }); + + return this->return_value; +} + + +template +void CallbackBridge::dispatched_async_uv_callback(uv_async_t *req) { + CallbackBridge* bridge = static_cast(req->data); + + NanScope(); + TryCatch try_catch; + + std::vector> argv_v8 = bridge->pre_process_args(bridge->argv); + argv_v8.push_back(NanNew(bridge->wrapper)); + + NanNew(bridge->callback->Call(argv_v8.size(), &argv_v8[0])); + + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } +} + + +template +NAN_METHOD(CallbackBridge::ReturnCallback) { + NanScope(); + + CallbackBridge* bridge = static_cast*>(NanGetInternalFieldPointer(args.This(), 0)); + TryCatch try_catch; + + bridge->return_value = bridge->post_process_return_value(args[0]); + + { + std::lock_guard lock(bridge->cv_mutex); + bridge->has_returned = true; + } + + bridge->condition_variable.notify_all(); + + if (try_catch.HasCaught()) { + node::FatalException(try_catch); + } + + NanReturnUndefined(); +} + + +template +Handle CallbackBridge::get_wrapper_constructor() { + if (wrapper_constructor.IsEmpty()) { + Local tpl = NanNew(New); + tpl->SetClassName(NanNew("CallbackBridge")); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + tpl->PrototypeTemplate()->Set( + NanNew("success"), + NanNew(ReturnCallback)->GetFunction() + ); + + NanAssignPersistent(wrapper_constructor, tpl->GetFunction()); + } + + return NanNew(wrapper_constructor); +} + +template +NAN_METHOD(CallbackBridge::New) { + NanReturnValue(args.This()); +} + +#endif diff --git a/src/create_string.cpp b/src/create_string.cpp new file mode 100644 index 000000000..d8734c604 --- /dev/null +++ b/src/create_string.cpp @@ -0,0 +1,12 @@ +#include "create_string.h" + +char* CreateString(Local value) { + if (value->IsNull() || !value->IsString()) { + return 0; + } + + 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 new file mode 100644 index 000000000..8661b627b --- /dev/null +++ b/src/create_string.h @@ -0,0 +1,10 @@ +#ifndef CREATE_STRING_H +#define CREATE_STRING_H + +#include + +using namespace v8; + +char* CreateString(Local); + +#endif \ No newline at end of file diff --git a/src/custom_function_bridge.cpp b/src/custom_function_bridge.cpp new file mode 100644 index 000000000..668527adb --- /dev/null +++ b/src/custom_function_bridge.cpp @@ -0,0 +1,21 @@ +#include "custom_function_bridge.h" + +Sass_Value* CustomFunctionBridge::post_process_return_value(Handle val) const { + try { + return SassTypes::Factory::unwrap(val)->get_sass_value(); + } catch (const std::invalid_argument& e) { + return sass_make_error(e.what()); + } +} + +std::vector> CustomFunctionBridge::pre_process_args(std::vector in) const { + std::vector> argv = std::vector>(); + + for (void* value : in) { + argv.push_back( + SassTypes::Factory::create(static_cast(value))->get_js_object() + ); + } + + return argv; +} diff --git a/src/custom_function_bridge.h b/src/custom_function_bridge.h new file mode 100644 index 000000000..41fd3cbd9 --- /dev/null +++ b/src/custom_function_bridge.h @@ -0,0 +1,20 @@ +#ifndef CUSTOM_FUNCTION_BRIDGE_H +#define CUSTOM_FUNCTION_BRIDGE_H + +#include +#include +#include "callback_bridge.h" +#include "sass_types/factory.h" + +using namespace v8; + +class CustomFunctionBridge : public CallbackBridge { + public: + CustomFunctionBridge(NanCallback* cb, bool is_sync) : CallbackBridge(cb, is_sync) {} + + private: + Sass_Value* post_process_return_value(Handle) const; + std::vector> pre_process_args(std::vector) const; +}; + +#endif diff --git a/src/custom_importer_bridge.cpp b/src/custom_importer_bridge.cpp new file mode 100644 index 000000000..a75c36a77 --- /dev/null +++ b/src/custom_importer_bridge.cpp @@ -0,0 +1,51 @@ +#include "custom_importer_bridge.h" + +SassImportList CustomImporterBridge::post_process_return_value(Handle val) const { + SassImportList imports; + NanScope(); + + Local returned_value = NanNew(val); + + if (returned_value->IsArray()) { + Handle array = Handle::Cast(returned_value); + + imports = sass_make_import_list(array->Length()); + + for (size_t i = 0; i < array->Length(); ++i) { + Local value = array->Get(i); + + if (!value->IsObject()) + continue; + + Local object = Local::Cast(value); + char* path = CreateString(object->Get(NanNew("file"))); + char* contents = CreateString(object->Get(NanNew("contents"))); + + imports[i] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0); + } + } + else if (returned_value->IsObject()) { + imports = sass_make_import_list(1); + Local object = Local::Cast(returned_value); + char* path = CreateString(object->Get(NanNew("file"))); + char* contents = CreateString(object->Get(NanNew("contents"))); + + imports[0] = sass_make_import_entry(path, (!contents || contents[0] == '\0') ? 0 : strdup(contents), 0); + } + else { + imports = sass_make_import_list(1); + imports[0] = sass_make_import_entry((char const*) this->argv[0], 0, 0); + } + + return imports; +} + +std::vector> CustomImporterBridge::pre_process_args(std::vector in) const { + std::vector> out; + + for (void* ptr : in) { + out.push_back(NanNew((char const*) ptr)); + } + + return out; +} diff --git a/src/custom_importer_bridge.h b/src/custom_importer_bridge.h new file mode 100644 index 000000000..56b0c1a72 --- /dev/null +++ b/src/custom_importer_bridge.h @@ -0,0 +1,22 @@ +#ifndef CUSTOM_IMPORTER_BRIDGE_H +#define CUSTOM_IMPORTER_BRIDGE_H + +#include +#include +#include "callback_bridge.h" +#include "create_string.h" + +using namespace v8; + +typedef Sass_Import** SassImportList; + +class CustomImporterBridge : public CallbackBridge { + public: + CustomImporterBridge(NanCallback* cb, bool is_sync) : CallbackBridge(cb, is_sync) {} + + private: + SassImportList post_process_return_value(Handle) const; + std::vector> pre_process_args(std::vector) const; +}; + +#endif diff --git a/src/sass_context_wrapper.cpp b/src/sass_context_wrapper.cpp index 411f4ae26..0bb846f69 100644 --- a/src/sass_context_wrapper.cpp +++ b/src/sass_context_wrapper.cpp @@ -23,12 +23,7 @@ extern "C" { } sass_context_wrapper* sass_make_context_wrapper() { - sass_context_wrapper* ctx_w = (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper)); - - ctx_w->importer_mutex = new std::mutex(); - ctx_w->importer_condition_variable = new std::condition_variable(); - - return ctx_w; + return (sass_context_wrapper*)calloc(1, sizeof(sass_context_wrapper)); } void sass_wrapper_dispose(struct sass_context_wrapper* ctx_w, char* string = 0) { @@ -39,20 +34,24 @@ extern "C" { sass_delete_file_context(ctx_w->fctx); } - delete ctx_w->file; - delete ctx_w->prev; delete ctx_w->error_callback; delete ctx_w->success_callback; - delete ctx_w->importer_callback; - - delete ctx_w->importer_mutex; - delete ctx_w->importer_condition_variable; NanDisposePersistent(ctx_w->result); if(string) { free(string); } + + if (!ctx_w->function_bridges.empty()) { + for (CustomFunctionBridge* bridge : ctx_w->function_bridges) { + delete bridge; + } + } + + if (ctx_w->importer_bridge) { + delete ctx_w->importer_bridge; + } } void sass_free_context_wrapper(sass_context_wrapper* ctx_w) { diff --git a/src/sass_context_wrapper.h b/src/sass_context_wrapper.h index ba412fcb4..6fa03dee2 100644 --- a/src/sass_context_wrapper.h +++ b/src/sass_context_wrapper.h @@ -1,7 +1,13 @@ +#ifndef SASS_CONTEXT_WRAPPER +#define SASS_CONTEXT_WRAPPER + +#include #include #include #include #include +#include "custom_function_bridge.h" +#include "custom_importer_bridge.h" #ifdef __cplusplus extern "C" { @@ -17,13 +23,8 @@ extern "C" { // binding related bool is_sync; void* cookie; - const char* prev; - const char* file; - std::mutex* importer_mutex; - std::condition_variable* importer_condition_variable; // libsass related - Sass_Import** imports; Sass_Data_Context* dctx; Sass_File_Context* fctx; @@ -35,7 +36,9 @@ extern "C" { Persistent result; NanCallback* error_callback; NanCallback* success_callback; - NanCallback* importer_callback; + + std::vector function_bridges; + CustomImporterBridge* importer_bridge; }; struct sass_context_wrapper* sass_make_context_wrapper(void); @@ -45,3 +48,5 @@ extern "C" { #ifdef __cplusplus } #endif + +#endif diff --git a/src/sass_types/boolean.cpp b/src/sass_types/boolean.cpp new file mode 100644 index 000000000..e7714f587 --- /dev/null +++ b/src/sass_types/boolean.cpp @@ -0,0 +1,44 @@ +#include "boolean.h" + +using namespace v8; + +namespace SassTypes +{ + Boolean::Boolean(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* Boolean::construct(const std::vector> raw_val) { + bool value = false; + + if (raw_val.size() >= 1) { + if (!raw_val[0]->IsBoolean()) { + throw std::invalid_argument("Argument should be a bool"); + } + + value = raw_val[0]->ToBoolean()->Value(); + } + + return sass_make_boolean(value); + } + + void Boolean::initPrototype(Handle proto) { + proto->Set(NanNew("getValue"), NanNew(GetValue)->GetFunction()); + proto->Set(NanNew("setValue"), NanNew(SetValue)->GetFunction()); + } + + NAN_METHOD(Boolean::GetValue) { + NanReturnValue(NanNew(sass_boolean_get_value(unwrap(args.This())->value))); + } + + NAN_METHOD(Boolean::SetValue) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsBoolean()) { + return NanThrowError(NanNew("Supplied value should be a boolean")); + } + + sass_boolean_set_value(unwrap(args.This())->value, args[0]->ToBoolean()->Value()); + NanReturnUndefined(); + } +} diff --git a/src/sass_types/boolean.h b/src/sass_types/boolean.h new file mode 100644 index 000000000..4f7a6222d --- /dev/null +++ b/src/sass_types/boolean.h @@ -0,0 +1,25 @@ +#ifndef SASS_TYPES_BOOLEAN_H +#define SASS_TYPES_BOOLEAN_H + +#include +#include +#include "core_value.h" + +namespace SassTypes +{ + using namespace v8; + + class Boolean : public CoreValue { + public: + Boolean(Sass_Value*); + static char const* get_constructor_name() { return "SassBoolean"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(v8::Handle); + + static NAN_METHOD(GetValue); + static NAN_METHOD(SetValue); + }; +} + +#endif diff --git a/src/sass_types/color.cpp b/src/sass_types/color.cpp new file mode 100644 index 000000000..76bfbd2fa --- /dev/null +++ b/src/sass_types/color.cpp @@ -0,0 +1,141 @@ +#include "color.h" + +using namespace v8; + +namespace SassTypes +{ + Color::Color(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* Color::construct(const std::vector> raw_val) { + double a = 1.0, r = 0, g = 0, b = 0; + unsigned argb; + + switch (raw_val.size()) { + case 1: + if (!raw_val[0]->IsNumber()) { + throw std::invalid_argument("Only argument should be an integer"); + } + + argb = raw_val[0]->ToInt32()->Value(); + a = (double) ((argb >> 030) & 0xff) / 0xff; + r = (double) ((argb >> 020) & 0xff); + g = (double) ((argb >> 010) & 0xff); + b = (double) (argb & 0xff); + break; + + case 4: + if (!raw_val[3]->IsNumber()) { + throw std::invalid_argument("Constructor arguments should be numbers exclusively"); + } + + a = raw_val[3]->ToNumber()->Value(); + // fall through vvv + + case 3: + if (!raw_val[0]->IsNumber() || !raw_val[1]->IsNumber() || !raw_val[2]->IsNumber()) { + throw std::invalid_argument("Constructor arguments should be numbers exclusively"); + } + + r = raw_val[0]->ToNumber()->Value(); + g = raw_val[1]->ToNumber()->Value(); + b = raw_val[2]->ToNumber()->Value(); + break; + + case 0: + break; + + default: + throw std::invalid_argument("Constructor should be invoked with either 0, 1, 3 or 4 arguments"); + } + + return sass_make_color(r, g, b, a); + } + + + void Color::initPrototype(Handle proto) { + proto->Set(NanNew("getR"), NanNew(GetR)->GetFunction()); + proto->Set(NanNew("getG"), NanNew(GetG)->GetFunction()); + proto->Set(NanNew("getB"), NanNew(GetB)->GetFunction()); + proto->Set(NanNew("getA"), NanNew(GetA)->GetFunction()); + proto->Set(NanNew("setR"), NanNew(SetR)->GetFunction()); + proto->Set(NanNew("setG"), NanNew(SetG)->GetFunction()); + proto->Set(NanNew("setB"), NanNew(SetB)->GetFunction()); + proto->Set(NanNew("setA"), NanNew(SetA)->GetFunction()); + } + + + NAN_METHOD(Color::GetR) { + NanReturnValue(NanNew(sass_color_get_r(unwrap(args.This())->value))); + } + + + NAN_METHOD(Color::GetG) { + NanReturnValue(NanNew(sass_color_get_g(unwrap(args.This())->value))); + } + + + NAN_METHOD(Color::GetB) { + NanReturnValue(NanNew(sass_color_get_b(unwrap(args.This())->value))); + } + + + NAN_METHOD(Color::GetA) { + NanReturnValue(NanNew(sass_color_get_a(unwrap(args.This())->value))); + } + + + NAN_METHOD(Color::SetR) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied value should be a number")); + } + + sass_color_set_r(unwrap(args.This())->value, args[0]->ToNumber()->Value()); + NanReturnUndefined(); + } + + + NAN_METHOD(Color::SetG) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied value should be a number")); + } + + sass_color_set_g(unwrap(args.This())->value, args[0]->ToNumber()->Value()); + NanReturnUndefined(); + } + + + NAN_METHOD(Color::SetB) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied value should be a number")); + } + + sass_color_set_b(unwrap(args.This())->value, args[0]->ToNumber()->Value()); + NanReturnUndefined(); + } + + + NAN_METHOD(Color::SetA) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied value should be a number")); + } + + sass_color_set_a(unwrap(args.This())->value, args[0]->ToNumber()->Value()); + NanReturnUndefined(); + } +} diff --git a/src/sass_types/color.h b/src/sass_types/color.h new file mode 100644 index 000000000..cdf9fb540 --- /dev/null +++ b/src/sass_types/color.h @@ -0,0 +1,33 @@ +#ifndef SASS_TYPES_COLOR_H +#define SASS_TYPES_COLOR_H + +#include +#include +#include "core_value.h" + + +namespace SassTypes +{ + using namespace v8; + + class Color : public CoreValue { + public: + Color(Sass_Value*); + static char const* get_constructor_name() { return "SassColor"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + + 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); + }; +} + + +#endif diff --git a/src/sass_types/core_value.h b/src/sass_types/core_value.h new file mode 100644 index 000000000..4837286e3 --- /dev/null +++ b/src/sass_types/core_value.h @@ -0,0 +1,110 @@ +#ifndef SASS_TYPES_CORE_VALUE_H +#define SASS_TYPES_CORE_VALUE_H + +#include +#include +#include +#include "value.h" +#include "factory.h" + +namespace SassTypes +{ + using namespace v8; + + // 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 CoreValue : public Value { + public: + static char const* get_constructor_name() { return "SassValue"; } + + CoreValue(Sass_Value*); + + Sass_Value* get_sass_value(); + Local get_js_object(); + + static Handle get_constructor(); + static NAN_METHOD(New); + + protected: + static T* unwrap(Local); + + private: + static Persistent constructor; + }; + + template + Persistent CoreValue::constructor; + + template + CoreValue::CoreValue(Sass_Value* v) : Value(v) {} + + template + Sass_Value* CoreValue::get_sass_value() { + return sass_clone_value(this->value); + } + + template + Local CoreValue::get_js_object() { + Local wrapper = NanNew(T::get_constructor())->NewInstance(); + + delete static_cast(NanGetInternalFieldPointer(wrapper, 0)); + NanSetInternalFieldPointer(wrapper, 0, this); + + return wrapper; + } + + template + Handle CoreValue::get_constructor() { + if (constructor.IsEmpty()) { + Local tpl = NanNew(New); + tpl->SetClassName(NanNew(T::get_constructor_name())); + tpl->InstanceTemplate()->SetInternalFieldCount(1); + T::initPrototype(tpl->PrototypeTemplate()); + NanAssignPersistent(constructor, tpl->GetFunction()); + } + + return NanNew(constructor); + } + + template + NAN_METHOD(CoreValue::New) { + if (!args.IsConstructCall()) { + unsigned argc = args.Length(); + std::vector> argv; + + argv.reserve(argc); + for (unsigned i = 0; i < argc; i++) { + argv.push_back(args[i]); + } + + NanReturnValue(NanNew(T::get_constructor())->NewInstance(argc, &argv[0])); + } + + std::vector> localArgs(args.Length()); + + for (auto i = 0; i < args.Length(); ++i) { + localArgs[i] = args[i]; + } + + try { + Sass_Value* value = T::construct(localArgs); + T* obj = new T(value); + sass_delete_value(value); + + NanSetInternalFieldPointer(args.This(), 0, obj); + + NanNew(args.This()); + } + catch (const std::exception& e) { + NanThrowError(NanNew(e.what())); + } + } + + template + T* CoreValue::unwrap(Local obj) { + return static_cast(Factory::unwrap(obj)); + } +} + +#endif diff --git a/src/sass_types/error.cpp b/src/sass_types/error.cpp new file mode 100644 index 000000000..c5b695d82 --- /dev/null +++ b/src/sass_types/error.cpp @@ -0,0 +1,24 @@ +#include "error.h" + +using namespace v8; + +namespace SassTypes +{ + Error::Error(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* Error::construct(const std::vector> raw_val) { + char const* value = ""; + + if (raw_val.size() >= 1) { + if (!raw_val[0]->IsString()) { + throw std::invalid_argument("Argument should be a string"); + } + + value = CreateString(raw_val[0]); + } + + return sass_make_error(value); + } + + void Error::initPrototype(Handle) {} +} diff --git a/src/sass_types/error.h b/src/sass_types/error.h new file mode 100644 index 000000000..214cb536a --- /dev/null +++ b/src/sass_types/error.h @@ -0,0 +1,22 @@ +#ifndef SASS_TYPES_ERROR_H +#define SASS_TYPES_ERROR_H + +#include +#include "../create_string.h" +#include "core_value.h" + +namespace SassTypes +{ + using namespace v8; + + class Error : public CoreValue { + public: + Error(Sass_Value*); + static char const* get_constructor_name() { return "SassError"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + }; +} + +#endif diff --git a/src/sass_types/factory.cpp b/src/sass_types/factory.cpp new file mode 100644 index 000000000..58bc5924e --- /dev/null +++ b/src/sass_types/factory.cpp @@ -0,0 +1,71 @@ +#include +#include "factory.h" +#include +#include "value.h" +#include "number.h" +#include "string.h" +#include "color.h" +#include "boolean.h" +#include "list.h" +#include "map.h" +#include "null.h" +#include "error.h" + +using namespace v8; + +namespace SassTypes +{ + Value* Factory::create(Sass_Value* v) { + switch (sass_value_get_tag(v)) { + case SASS_NUMBER: + return new Number(v); + + case SASS_STRING: + return new String(v); + + case SASS_COLOR: + return new Color(v); + + case SASS_BOOLEAN: + return new Boolean(v); + + case SASS_LIST: + return new List(v); + + case SASS_MAP: + return new Map(v); + + case SASS_NULL: + return new Null(v); + + case SASS_ERROR: + return new Error(v); + + default: + throw std::invalid_argument("Unknown type"); + } + } + + void Factory::initExports(Handle exports) { + Local types = NanNew(); + exports->Set(NanNew("types"), types); + + types->Set(NanNew("Number"), Number::get_constructor()); + types->Set(NanNew("String"), String::get_constructor()); + types->Set(NanNew("Color"), Color::get_constructor()); + types->Set(NanNew("Boolean"), Boolean::get_constructor()); + types->Set(NanNew("List"), List::get_constructor()); + types->Set(NanNew("Map"), Map::get_constructor()); + types->Set(NanNew("Null"), Null::get_constructor()); + types->Set(NanNew("Error"), Error::get_constructor()); + } + + Value* Factory::unwrap(Handle obj) { + // Todo: non-SassValue objects could easily fall under that condition, need to be more specific. + if (!obj->IsObject() || obj->ToObject()->InternalFieldCount() != 1) { + throw std::invalid_argument("A SassValue object was expected"); + } + + return static_cast(NanGetInternalFieldPointer(obj->ToObject(), 0)); + } +} diff --git a/src/sass_types/factory.h b/src/sass_types/factory.h new file mode 100644 index 000000000..161470f16 --- /dev/null +++ b/src/sass_types/factory.h @@ -0,0 +1,24 @@ +#ifndef SASS_TYPES_FACTORY_H +#define SASS_TYPES_FACTORY_H + +#include +#include +#include "value.h" + + +namespace SassTypes +{ + using namespace v8; + + // This is the guru that knows everything about instantiating the right subclass of SassTypes::Value + // to wrap a given Sass_Value object. + class Factory { + public: + static void initExports(Handle); + static Value* create(Sass_Value*); + static Value* unwrap(Handle); + }; +} + + +#endif diff --git a/src/sass_types/list.cpp b/src/sass_types/list.cpp new file mode 100644 index 000000000..98c724ac2 --- /dev/null +++ b/src/sass_types/list.cpp @@ -0,0 +1,98 @@ +#include "list.h" + +using namespace v8; + +namespace SassTypes +{ + List::List(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* List::construct(const std::vector> raw_val) { + size_t length = 0; + bool comma = true; + + if (raw_val.size() >= 1) { + if (!raw_val[0]->IsNumber()) { + throw std::invalid_argument("First argument should be an integer"); + } + + length = raw_val[0]->ToInt32()->Value(); + + if (raw_val.size() >= 2) { + if (!raw_val[1]->IsBoolean()) { + throw std::invalid_argument("Second argument should be a boolean"); + } + + comma = raw_val[1]->ToBoolean()->Value(); + } + } + + return sass_make_list(length, comma ? SASS_COMMA : SASS_SPACE); + } + + void List::initPrototype(Handle proto) { + proto->Set(NanNew("getLength"), NanNew(GetLength)->GetFunction()); + proto->Set(NanNew("getSeparator"), NanNew(GetSeparator)->GetFunction()); + proto->Set(NanNew("setSeparator"), NanNew(SetSeparator)->GetFunction()); + proto->Set(NanNew("getValue"), NanNew(GetValue)->GetFunction()); + proto->Set(NanNew("setValue"), NanNew(SetValue)->GetFunction()); + } + + NAN_METHOD(List::GetValue) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied index should be an integer")); + } + + Sass_Value* list = unwrap(args.This())->value; + size_t index = args[0]->ToInt32()->Value(); + + + if (index >= sass_list_get_length(list)) { + return NanThrowError(NanNew("Out of bound index")); + } + + NanReturnValue(Factory::create(sass_list_get_value(list, args[0]->ToInt32()->Value()))->get_js_object()); + } + + NAN_METHOD(List::SetValue) { + if (args.Length() != 2) { + return NanThrowError(NanNew("Expected two arguments")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied index should be an integer")); + } + + if (!args[1]->IsObject()) { + return NanThrowError(NanNew("Supplied value should be a SassValue object")); + } + + Value* sass_value = Factory::unwrap(args[1]); + sass_list_set_value(unwrap(args.This())->value, args[0]->ToInt32()->Value(), sass_value->get_sass_value()); + NanReturnUndefined(); + } + + NAN_METHOD(List::GetSeparator) { + NanReturnValue(NanNew(sass_list_get_separator(unwrap(args.This())->value) == SASS_COMMA)); + } + + NAN_METHOD(List::SetSeparator) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsBoolean()) { + return NanThrowError(NanNew("Supplied value should be a boolean")); + } + + sass_list_set_separator(unwrap(args.This())->value, args[0]->ToBoolean()->Value() ? SASS_COMMA : SASS_SPACE); + NanReturnUndefined(); + } + + NAN_METHOD(List::GetLength) { + NanReturnValue(NanNew(sass_list_get_length(unwrap(args.This())->value))); + } +} diff --git a/src/sass_types/list.h b/src/sass_types/list.h new file mode 100644 index 000000000..c2e6ed8f3 --- /dev/null +++ b/src/sass_types/list.h @@ -0,0 +1,30 @@ +#ifndef SASS_TYPES_LIST_H +#define SASS_TYPES_LIST_H + +#include +#include +#include "core_value.h" + + +namespace SassTypes +{ + using namespace v8; + + class List : public CoreValue { + public: + List(Sass_Value*); + static char const* get_constructor_name() { return "SassList"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + + static NAN_METHOD(GetValue); + static NAN_METHOD(SetValue); + static NAN_METHOD(GetSeparator); + static NAN_METHOD(SetSeparator); + static NAN_METHOD(GetLength); + }; +} + + +#endif diff --git a/src/sass_types/map.cpp b/src/sass_types/map.cpp new file mode 100644 index 000000000..40782457e --- /dev/null +++ b/src/sass_types/map.cpp @@ -0,0 +1,110 @@ +#include "map.h" + +using namespace v8; + +namespace SassTypes +{ + Map::Map(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* Map::construct(const std::vector> raw_val) { + size_t length = 0; + + if (raw_val.size() >= 1) { + if (!raw_val[0]->IsNumber()) { + throw std::invalid_argument("First argument should be an integer"); + } + + length = raw_val[0]->ToInt32()->Value(); + } + + return sass_make_map(length); + } + + void Map::initPrototype(Handle proto) { + proto->Set(NanNew("getLength"), NanNew(GetLength)->GetFunction()); + proto->Set(NanNew("getKey"), NanNew(GetKey)->GetFunction()); + proto->Set(NanNew("setKey"), NanNew(SetKey)->GetFunction()); + proto->Set(NanNew("getValue"), NanNew(GetValue)->GetFunction()); + proto->Set(NanNew("setValue"), NanNew(SetValue)->GetFunction()); + } + + NAN_METHOD(Map::GetValue) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied index should be an integer")); + } + + Sass_Value* map = unwrap(args.This())->value; + size_t index = args[0]->ToInt32()->Value(); + + + if (index >= sass_map_get_length(map)) { + return NanThrowError(NanNew("Out of bound index")); + } + + NanNew(Factory::create(sass_map_get_value(map, args[0]->ToInt32()->Value()))->get_js_object()); + } + + NAN_METHOD(Map::SetValue) { + if (args.Length() != 2) { + return NanThrowError(NanNew("Expected two arguments")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied index should be an integer")); + } + + if (!args[1]->IsObject()) { + return NanThrowError(NanNew("Supplied value should be a SassValue object")); + } + + Value* sass_value = Factory::unwrap(args[1]); + sass_map_set_value(unwrap(args.This())->value, args[0]->ToInt32()->Value(), sass_value->get_sass_value()); + NanReturnUndefined(); + } + + NAN_METHOD(Map::GetKey) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied index should be an integer")); + } + + Sass_Value* map = unwrap(args.This())->value; + size_t index = args[0]->ToInt32()->Value(); + + + if (index >= sass_map_get_length(map)) { + return NanThrowError(NanNew("Out of bound index")); + } + + NanReturnValue(Factory::create(sass_map_get_key(map, args[0]->ToInt32()->Value()))->get_js_object()); + } + + NAN_METHOD(Map::SetKey) { + if (args.Length() != 2) { + return NanThrowError(NanNew("Expected two arguments")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied index should be an integer")); + } + + if (!args[1]->IsObject()) { + return NanThrowError(NanNew("Supplied value should be a SassValue object")); + } + + Value* sass_value = Factory::unwrap(args[1]); + sass_map_set_key(unwrap(args.This())->value, args[0]->ToInt32()->Value(), sass_value->get_sass_value()); + NanReturnUndefined(); + } + + NAN_METHOD(Map::GetLength) { + NanReturnValue(NanNew(sass_map_get_length(unwrap(args.This())->value))); + } +} diff --git a/src/sass_types/map.h b/src/sass_types/map.h new file mode 100644 index 000000000..a3164520f --- /dev/null +++ b/src/sass_types/map.h @@ -0,0 +1,28 @@ +#ifndef SASS_TYPES_MAP_H +#define SASS_TYPES_MAP_H + +#include +#include +#include "core_value.h" + +namespace SassTypes +{ + using namespace v8; + + class Map : public CoreValue { + public: + Map(Sass_Value*); + static char const* get_constructor_name() { return "SassMap"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + + static NAN_METHOD(GetValue); + static NAN_METHOD(SetValue); + static NAN_METHOD(GetKey); + static NAN_METHOD(SetKey); + static NAN_METHOD(GetLength); + }; +} + +#endif diff --git a/src/sass_types/null.cpp b/src/sass_types/null.cpp new file mode 100644 index 000000000..0d17b8c62 --- /dev/null +++ b/src/sass_types/null.cpp @@ -0,0 +1,14 @@ +#include "null.h" + +using namespace v8; + +namespace SassTypes +{ + Null::Null(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* Null::construct(const std::vector> raw_val) { + return sass_make_null(); + } + + void Null::initPrototype(Handle) {} +} diff --git a/src/sass_types/null.h b/src/sass_types/null.h new file mode 100644 index 000000000..e6ff7e5ce --- /dev/null +++ b/src/sass_types/null.h @@ -0,0 +1,24 @@ +#ifndef SASS_TYPES_NULL_H +#define SASS_TYPES_NULL_H + +#include +#include +#include "core_value.h" + + +namespace SassTypes +{ + using namespace v8; + + class Null : public CoreValue { + public: + Null(Sass_Value* v); + static char const* get_constructor_name() { return "SassNull"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + }; +} + + +#endif diff --git a/src/sass_types/number.cpp b/src/sass_types/number.cpp new file mode 100644 index 000000000..54cb206c6 --- /dev/null +++ b/src/sass_types/number.cpp @@ -0,0 +1,72 @@ +#include "number.h" + +using namespace v8; + +namespace SassTypes +{ + Number::Number(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* Number::construct(const std::vector> raw_val) { + double value = 0; + char const* unit = ""; + + if (raw_val.size() >= 1) { + if (!raw_val[0]->IsNumber()) { + throw std::invalid_argument("First argument should be a number"); + } + + value = raw_val[0]->ToNumber()->Value(); + + if (raw_val.size() >= 2) { + if (!raw_val[1]->IsString()) { + throw std::invalid_argument("Second argument should be a string"); + } + + unit = CreateString(raw_val[1]); + } + } + + return sass_make_number(value, unit); + } + + void Number::initPrototype(Handle proto) { + proto->Set(NanNew("getValue"), NanNew(GetValue)->GetFunction()); + proto->Set(NanNew("getUnit"), NanNew(GetUnit)->GetFunction()); + proto->Set(NanNew("setValue"), NanNew(SetValue)->GetFunction()); + proto->Set(NanNew("setUnit"), NanNew(SetUnit)->GetFunction()); + } + + NAN_METHOD(Number::GetValue) { + NanReturnValue(NanNew(sass_number_get_value(unwrap(args.This())->value))); + } + + NAN_METHOD(Number::GetUnit) { + NanReturnValue(NanNew(sass_number_get_unit(unwrap(args.This())->value))); + } + + NAN_METHOD(Number::SetValue) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsNumber()) { + return NanThrowError(NanNew("Supplied value should be a number")); + } + + sass_number_set_value(unwrap(args.This())->value, args[0]->ToNumber()->Value()); + NanReturnUndefined(); + } + + NAN_METHOD(Number::SetUnit) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsString()) { + return NanThrowError(NanNew("Supplied value should be a string")); + } + + sass_number_set_unit(unwrap(args.This())->value, CreateString(args[0])); + NanReturnUndefined(); + } +} diff --git a/src/sass_types/number.h b/src/sass_types/number.h new file mode 100644 index 000000000..a8932cece --- /dev/null +++ b/src/sass_types/number.h @@ -0,0 +1,30 @@ +#ifndef SASS_TYPES_NUMBER_H +#define SASS_TYPES_NUMBER_H + +#include +#include +#include "../create_string.h" +#include "core_value.h" + + +namespace SassTypes +{ + using namespace v8; + + class Number : public CoreValue { + public: + Number(Sass_Value*); + static char const* get_constructor_name() { return "SassNumber"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + + static NAN_METHOD(GetValue); + static NAN_METHOD(GetUnit); + static NAN_METHOD(SetValue); + static NAN_METHOD(SetUnit); + }; +} + + +#endif diff --git a/src/sass_types/string.cpp b/src/sass_types/string.cpp new file mode 100644 index 000000000..cf97eb81a --- /dev/null +++ b/src/sass_types/string.cpp @@ -0,0 +1,47 @@ +#include "string.h" + +using namespace v8; + +namespace SassTypes +{ + String::String(Sass_Value* v) : CoreValue(v) {} + + Sass_Value* String::construct(const std::vector> raw_val) { + char const* value = ""; + + if (raw_val.size() >= 1) { + if (!raw_val[0]->IsString()) { + throw std::invalid_argument("Argument should be a string"); + } + + value = CreateString(raw_val[0]); + } + + return sass_make_string(value); + } + + + void String::initPrototype(Handle proto) { + proto->Set(NanNew("getValue"), NanNew(GetValue)->GetFunction()); + proto->Set(NanNew("setValue"), NanNew(SetValue)->GetFunction()); + } + + + NAN_METHOD(String::GetValue) { + NanReturnValue(NanNew(sass_string_get_value(unwrap(args.This())->value))); + } + + + NAN_METHOD(String::SetValue) { + if (args.Length() != 1) { + return NanThrowError(NanNew("Expected just one argument")); + } + + if (!args[0]->IsString()) { + return NanThrowError(NanNew("Supplied value should be a string")); + } + + sass_string_set_value(unwrap(args.This())->value, CreateString(args[0])); + NanReturnUndefined(); + } +} diff --git a/src/sass_types/string.h b/src/sass_types/string.h new file mode 100644 index 000000000..f4a39559e --- /dev/null +++ b/src/sass_types/string.h @@ -0,0 +1,28 @@ +#ifndef SASS_TYPES_STRING_H +#define SASS_TYPES_STRING_H + +#include +#include +#include "../create_string.h" +#include "core_value.h" + + +namespace SassTypes +{ + using namespace v8; + + class String : public CoreValue { + public: + String(Sass_Value*); + static char const* get_constructor_name() { return "SassString"; } + static Sass_Value* construct(const std::vector>); + + static void initPrototype(Handle); + + static NAN_METHOD(GetValue); + static NAN_METHOD(SetValue); + }; +} + + +#endif diff --git a/src/sass_types/value.cpp b/src/sass_types/value.cpp new file mode 100644 index 000000000..4447e381b --- /dev/null +++ b/src/sass_types/value.cpp @@ -0,0 +1,14 @@ +#include "value.h" + +using namespace v8; + +namespace SassTypes +{ + Value::Value(Sass_Value* v) { + this->value = sass_clone_value(v); + } + + Value::~Value() { + sass_delete_value(this->value); + } +} diff --git a/src/sass_types/value.h b/src/sass_types/value.h new file mode 100644 index 000000000..c3e6a3d44 --- /dev/null +++ b/src/sass_types/value.h @@ -0,0 +1,24 @@ +#ifndef SASS_TYPES_VALUE_H +#define SASS_TYPES_VALUE_H + +#include +#include + +namespace SassTypes +{ + using namespace v8; + + // This is the interface that all sass values must comply with + class Value { + public: + Value(Sass_Value*); + virtual ~Value(); + virtual Sass_Value* get_sass_value() =0; + virtual Local get_js_object() =0; + + protected: + Sass_Value* value; + }; +} + +#endif