Unverified Commit e3ca0757 by Michael Mifsud Committed by GitHub

Merge pull request #2128 from stefanpenner/leaks

Fix Some Leaks
parents 0a0f5c64 f6b0c722
"use strict";
module.exports = function iterateAndMeasure(fn, mod = 1000000) {
let count = 0;
while (true) {
count++;
fn();
if (count % mod === 0) {
console.log(process.memoryUsage().rss / 1000000);
}
}
}
'use strict';
var types = require('../').types;
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() { return types.Boolean(true).getValue(); });
"use strict";
var sass = require("../");
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() {
sass.renderSync({
data: '#{headings()} { color: #08c; }',
functions: {
'headings()': function() {
return new sass.types.String('hi');
}
}
});
}, 10000);
'use strict';
var types = require('../').types;
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() {
var key = new types.String('the-key');
var value = new types.String('the-value');
var map = new types.Map(1);
map.setKey(0, key);
map.setValue(0, value);
map.getKey(0);
}, 100000);
'use strict';
var types = require('../').types;
var iterateAndMeasure = require('./_measure');
iterateAndMeasure(function() { return new types.String('hi'); });
...@@ -158,7 +158,9 @@ int ExtractOptions(v8::Local<v8::Object> options, void* cptr, sass_context_wrapp ...@@ -158,7 +158,9 @@ int ExtractOptions(v8::Local<v8::Object> options, void* cptr, sass_context_wrapp
CustomFunctionBridge *bridge = new CustomFunctionBridge(callback, ctx_w->is_sync); CustomFunctionBridge *bridge = new CustomFunctionBridge(callback, ctx_w->is_sync);
ctx_w->function_bridges.push_back(bridge); ctx_w->function_bridges.push_back(bridge);
Sass_Function_Entry fn = sass_make_function(create_string(signature), sass_custom_function, bridge); char* sig = create_string(signature);
Sass_Function_Entry fn = sass_make_function(sig, sass_custom_function, bridge);
free(sig);
sass_function_set_list_entry(fn_list, i, fn); sass_function_set_list_entry(fn_list, i, fn);
} }
...@@ -267,7 +269,7 @@ NAN_METHOD(render) { ...@@ -267,7 +269,7 @@ NAN_METHOD(render) {
struct Sass_Data_Context* dctx = sass_make_data_context(source_string); 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();
if (ExtractOptions(options, dctx, ctx_w, false, false) >= 0) { if (ExtractOptions(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); int status = uv_queue_work(uv_default_loop(), &ctx_w->request, compile_it, (uv_after_work_cb)MakeCallback);
...@@ -290,6 +292,7 @@ NAN_METHOD(render_sync) { ...@@ -290,6 +292,7 @@ NAN_METHOD(render_sync) {
} }
sass_free_context_wrapper(ctx_w); sass_free_context_wrapper(ctx_w);
info.GetReturnValue().Set(result == 0); info.GetReturnValue().Set(result == 0);
} }
......
...@@ -55,7 +55,7 @@ Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor; ...@@ -55,7 +55,7 @@ Nan::Persistent<v8::Function> CallbackBridge<T, L>::wrapper_constructor;
template <typename T, typename L> template <typename T, typename L>
CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) { CallbackBridge<T, L>::CallbackBridge(v8::Local<v8::Function> callback, bool is_sync) : callback(new Nan::Callback(callback)), is_sync(is_sync) {
/* /*
* This is invoked from the main JavaScript thread. * This is invoked from the main JavaScript thread.
* V8 context is available. * V8 context is available.
*/ */
...@@ -89,7 +89,7 @@ template <typename T, typename L> ...@@ -89,7 +89,7 @@ template <typename T, typename L>
T CallbackBridge<T, L>::operator()(std::vector<void*> argv) { T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
// argv.push_back(wrapper); // argv.push_back(wrapper);
if (this->is_sync) { if (this->is_sync) {
/* /*
* This is invoked from the main JavaScript thread. * This is invoked from the main JavaScript thread.
* V8 context is available. * V8 context is available.
* *
...@@ -110,7 +110,7 @@ T CallbackBridge<T, L>::operator()(std::vector<void*> argv) { ...@@ -110,7 +110,7 @@ T CallbackBridge<T, L>::operator()(std::vector<void*> argv) {
this->callback->Call(argv_v8.size(), &argv_v8[0]) this->callback->Call(argv_v8.size(), &argv_v8[0])
); );
} else { } else {
/* /*
* This is invoked from the worker thread. * This is invoked from the worker thread.
* No V8 context and functions available. * No V8 context and functions available.
* Just wait for response from asynchronously * Just wait for response from asynchronously
...@@ -141,7 +141,7 @@ template <typename T, typename L> ...@@ -141,7 +141,7 @@ template <typename T, typename L>
void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) { void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
CallbackBridge* bridge = static_cast<CallbackBridge*>(req->data); CallbackBridge* bridge = static_cast<CallbackBridge*>(req->data);
/* /*
* Function scheduled via uv_async mechanism, therefore * Function scheduled via uv_async mechanism, therefore
* it is invoked from the main JavaScript thread. * it is invoked from the main JavaScript thread.
* V8 context is available. * V8 context is available.
...@@ -169,7 +169,7 @@ void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) { ...@@ -169,7 +169,7 @@ void CallbackBridge<T, L>::dispatched_async_uv_callback(uv_async_t *req) {
template <typename T, typename L> template <typename T, typename L>
NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) { NAN_METHOD(CallbackBridge<T COMMA L>::ReturnCallback) {
/* /*
* Callback function invoked by the user code. * Callback function invoked by the user code.
* It is invoked from the main JavaScript thread. * It is invoked from the main JavaScript thread.
* V8 context is available. * V8 context is available.
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
#include "sass_types/factory.h" #include "sass_types/factory.h"
#include "sass_types/value.h" #include "sass_types/value.h"
Sass_Value* CustomFunctionBridge::post_process_return_value(v8::Local<v8::Value> val) const { Sass_Value* CustomFunctionBridge::post_process_return_value(v8::Local<v8::Value> _val) const {
SassTypes::Value *v_; SassTypes::Value *value = SassTypes::Factory::unwrap(_val);
if ((v_ = SassTypes::Factory::unwrap(val))) { if (value) {
return v_->get_sass_value(); return value->get_sass_value();
} else { } else {
return sass_make_error("A SassValue object was expected."); return sass_make_error("A SassValue object was expected.");
} }
...@@ -17,7 +17,10 @@ std::vector<v8::Local<v8::Value>> CustomFunctionBridge::pre_process_args(std::ve ...@@ -17,7 +17,10 @@ std::vector<v8::Local<v8::Value>> CustomFunctionBridge::pre_process_args(std::ve
std::vector<v8::Local<v8::Value>> argv = std::vector<v8::Local<v8::Value>>(); std::vector<v8::Local<v8::Value>> argv = std::vector<v8::Local<v8::Value>>();
for (void* value : in) { for (void* value : in) {
argv.push_back(SassTypes::Factory::create(static_cast<Sass_Value*>(value))->get_js_object()); Sass_Value* x = static_cast<Sass_Value*>(value);
SassTypes::Value* y = SassTypes::Factory::create(x);
argv.push_back(y->get_js_object());
} }
return argv; return argv;
......
...@@ -29,6 +29,7 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val ...@@ -29,6 +29,7 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val
imports[i] = sass_make_import_entry(0, 0, 0); imports[i] = sass_make_import_entry(0, 0, 0);
sass_import_set_error(imports[i], message, -1, -1); sass_import_set_error(imports[i], message, -1, -1);
free(message);
} }
else { else {
imports[i] = get_importer_entry(object); imports[i] = get_importer_entry(object);
...@@ -43,6 +44,7 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val ...@@ -43,6 +44,7 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val
imports[0] = sass_make_import_entry(0, 0, 0); imports[0] = sass_make_import_entry(0, 0, 0);
sass_import_set_error(imports[0], message, -1, -1); sass_import_set_error(imports[0], message, -1, -1);
free(message);
} }
else if (returned_value->IsObject()) { else if (returned_value->IsObject()) {
imports = sass_make_import_list(1); imports = sass_make_import_list(1);
...@@ -55,12 +57,12 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val ...@@ -55,12 +57,12 @@ SassImportList CustomImporterBridge::post_process_return_value(v8::Local<v8::Val
Sass_Import* CustomImporterBridge::check_returned_string(Nan::MaybeLocal<v8::Value> value, const char *msg) const Sass_Import* CustomImporterBridge::check_returned_string(Nan::MaybeLocal<v8::Value> value, const char *msg) const
{ {
v8::Local<v8::Value> checked; v8::Local<v8::Value> checked;
if (value.ToLocal(&checked)) { if (value.ToLocal(&checked)) {
if (!checked->IsUndefined() && !checked->IsString()) { if (!checked->IsUndefined() && !checked->IsString()) {
goto err; goto err;
} else { } else {
return nullptr; return nullptr;
} }
} }
err: err:
auto entry = sass_make_import_entry(0, 0, 0); auto entry = sass_make_import_entry(0, 0, 0);
......
...@@ -6,7 +6,9 @@ namespace SassTypes ...@@ -6,7 +6,9 @@ namespace SassTypes
Nan::Persistent<v8::Function> Boolean::constructor; Nan::Persistent<v8::Function> Boolean::constructor;
bool Boolean::constructor_locked = false; bool Boolean::constructor_locked = false;
Boolean::Boolean(bool v) : value(v) {} Boolean::Boolean(bool _value) {
value = sass_make_boolean(_value);
}
Boolean& Boolean::get_singleton(bool v) { Boolean& Boolean::get_singleton(bool v) {
static Boolean instance_false(false), instance_true(true); static Boolean instance_false(false), instance_true(true);
...@@ -15,7 +17,7 @@ namespace SassTypes ...@@ -15,7 +17,7 @@ namespace SassTypes
v8::Local<v8::Function> Boolean::get_constructor() { v8::Local<v8::Function> Boolean::get_constructor() {
Nan::EscapableHandleScope scope; Nan::EscapableHandleScope scope;
v8::Local<v8::Function> conslocal; v8::Local<v8::Function> conslocal;
if (constructor.IsEmpty()) { if (constructor.IsEmpty()) {
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
...@@ -42,16 +44,15 @@ namespace SassTypes ...@@ -42,16 +44,15 @@ namespace SassTypes
return scope.Escape(conslocal); return scope.Escape(conslocal);
} }
Sass_Value* Boolean::get_sass_value() {
return sass_make_boolean(value);
}
v8::Local<v8::Object> Boolean::get_js_object() { v8::Local<v8::Object> Boolean::get_js_object() {
return Nan::New(this->js_object); return Nan::New(this->js_object);
} }
NAN_METHOD(Boolean::New) { v8::Local<v8::Boolean> Boolean::get_js_boolean() {
return sass_boolean_get_value(this->value) ? Nan::True() : Nan::False();
}
NAN_METHOD(Boolean::New) {
if (info.IsConstructCall()) { if (info.IsConstructCall()) {
if (constructor_locked) { if (constructor_locked) {
return Nan::ThrowTypeError("Cannot instantiate SassBoolean"); return Nan::ThrowTypeError("Cannot instantiate SassBoolean");
...@@ -67,9 +68,6 @@ namespace SassTypes ...@@ -67,9 +68,6 @@ namespace SassTypes
} }
NAN_METHOD(Boolean::GetValue) { NAN_METHOD(Boolean::GetValue) {
Boolean *out; info.GetReturnValue().Set(Boolean::Unwrap<Boolean>(info.This())->get_js_boolean());
if ((out = static_cast<Boolean*>(Factory::unwrap(info.This())))) {
info.GetReturnValue().Set(Nan::New(out->value));
}
} }
} }
...@@ -12,7 +12,6 @@ namespace SassTypes ...@@ -12,7 +12,6 @@ namespace SassTypes
static Boolean& get_singleton(bool); static Boolean& get_singleton(bool);
static v8::Local<v8::Function> get_constructor(); static v8::Local<v8::Function> get_constructor();
Sass_Value* get_sass_value();
v8::Local<v8::Object> get_js_object(); v8::Local<v8::Object> get_js_object();
static NAN_METHOD(New); static NAN_METHOD(New);
...@@ -21,11 +20,11 @@ namespace SassTypes ...@@ -21,11 +20,11 @@ namespace SassTypes
private: private:
Boolean(bool); Boolean(bool);
bool value;
Nan::Persistent<v8::Object> js_object; Nan::Persistent<v8::Object> js_object;
static Nan::Persistent<v8::Function> constructor; static Nan::Persistent<v8::Function> constructor;
static bool constructor_locked; static bool constructor_locked;
v8::Local<v8::Boolean> get_js_boolean();
}; };
} }
......
...@@ -62,19 +62,19 @@ namespace SassTypes ...@@ -62,19 +62,19 @@ namespace SassTypes
} }
NAN_METHOD(Color::GetR) { NAN_METHOD(Color::GetR) {
info.GetReturnValue().Set(sass_color_get_r(unwrap(info.This())->value)); info.GetReturnValue().Set(sass_color_get_r(Color::Unwrap<Color>(info.This())->value));
} }
NAN_METHOD(Color::GetG) { NAN_METHOD(Color::GetG) {
info.GetReturnValue().Set(sass_color_get_g(unwrap(info.This())->value)); info.GetReturnValue().Set(sass_color_get_g(Color::Unwrap<Color>(info.This())->value));
} }
NAN_METHOD(Color::GetB) { NAN_METHOD(Color::GetB) {
info.GetReturnValue().Set(sass_color_get_b(unwrap(info.This())->value)); info.GetReturnValue().Set(sass_color_get_b(Color::Unwrap<Color>(info.This())->value));
} }
NAN_METHOD(Color::GetA) { NAN_METHOD(Color::GetA) {
info.GetReturnValue().Set(sass_color_get_a(unwrap(info.This())->value)); info.GetReturnValue().Set(sass_color_get_a(Color::Unwrap<Color>(info.This())->value));
} }
NAN_METHOD(Color::SetR) { NAN_METHOD(Color::SetR) {
...@@ -86,7 +86,7 @@ namespace SassTypes ...@@ -86,7 +86,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number"); return Nan::ThrowTypeError("Supplied value should be a number");
} }
sass_color_set_r(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust()); sass_color_set_r(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
} }
NAN_METHOD(Color::SetG) { NAN_METHOD(Color::SetG) {
...@@ -98,7 +98,7 @@ namespace SassTypes ...@@ -98,7 +98,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number"); return Nan::ThrowTypeError("Supplied value should be a number");
} }
sass_color_set_g(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust()); sass_color_set_g(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
} }
NAN_METHOD(Color::SetB) { NAN_METHOD(Color::SetB) {
...@@ -110,7 +110,7 @@ namespace SassTypes ...@@ -110,7 +110,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number"); return Nan::ThrowTypeError("Supplied value should be a number");
} }
sass_color_set_b(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust()); sass_color_set_b(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
} }
NAN_METHOD(Color::SetA) { NAN_METHOD(Color::SetA) {
...@@ -122,6 +122,6 @@ namespace SassTypes ...@@ -122,6 +122,6 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number"); return Nan::ThrowTypeError("Supplied value should be a number");
} }
sass_color_set_a(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust()); sass_color_set_a(Color::Unwrap<Color>(info.This())->value, Nan::To<double>(info[0]).FromJust());
} }
} }
...@@ -61,11 +61,12 @@ namespace SassTypes ...@@ -61,11 +61,12 @@ namespace SassTypes
} }
Value* Factory::unwrap(v8::Local<v8::Value> obj) { Value* Factory::unwrap(v8::Local<v8::Value> obj) {
// Todo: non-SassValue objects could easily fall under that condition, need to be more specific. if (obj->IsObject()) {
if (!obj->IsObject() || obj.As<v8::Object>()->InternalFieldCount() != 1) { v8::Local<v8::Object> v8_obj = obj.As<v8::Object>();
if (v8_obj->InternalFieldCount() == 1) {
return SassTypes::Value::Unwrap<Value>(v8_obj);
}
}
return NULL; return NULL;
}
return static_cast<Value*>(Nan::GetInternalFieldPointer(obj.As<v8::Object>(), 0));
} }
} }
...@@ -47,7 +47,7 @@ namespace SassTypes ...@@ -47,7 +47,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied index should be an integer"); return Nan::ThrowTypeError("Supplied index should be an integer");
} }
Sass_Value* list = unwrap(info.This())->value; Sass_Value* list = List::Unwrap<List>(info.This())->value;
size_t index = Nan::To<uint32_t>(info[0]).FromJust(); size_t index = Nan::To<uint32_t>(info[0]).FromJust();
...@@ -73,14 +73,14 @@ namespace SassTypes ...@@ -73,14 +73,14 @@ namespace SassTypes
Value* sass_value = Factory::unwrap(info[1]); Value* sass_value = Factory::unwrap(info[1]);
if (sass_value) { if (sass_value) {
sass_list_set_value(unwrap(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value()); sass_list_set_value(List::Unwrap<List>(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
} else { } else {
Nan::ThrowTypeError("A SassValue is expected as the list item"); Nan::ThrowTypeError("A SassValue is expected as the list item");
} }
} }
NAN_METHOD(List::GetSeparator) { NAN_METHOD(List::GetSeparator) {
info.GetReturnValue().Set(sass_list_get_separator(unwrap(info.This())->value) == SASS_COMMA); info.GetReturnValue().Set(sass_list_get_separator(List::Unwrap<List>(info.This())->value) == SASS_COMMA);
} }
NAN_METHOD(List::SetSeparator) { NAN_METHOD(List::SetSeparator) {
...@@ -92,10 +92,10 @@ namespace SassTypes ...@@ -92,10 +92,10 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a boolean"); return Nan::ThrowTypeError("Supplied value should be a boolean");
} }
sass_list_set_separator(unwrap(info.This())->value, Nan::To<bool>(info[0]).FromJust() ? SASS_COMMA : SASS_SPACE); sass_list_set_separator(List::Unwrap<List>(info.This())->value, Nan::To<bool>(info[0]).FromJust() ? SASS_COMMA : SASS_SPACE);
} }
NAN_METHOD(List::GetLength) { NAN_METHOD(List::GetLength) {
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_list_get_length(unwrap(info.This())->value))); info.GetReturnValue().Set(Nan::New<v8::Number>(sass_list_get_length(List::Unwrap<List>(info.This())->value)));
} }
} }
...@@ -37,7 +37,7 @@ namespace SassTypes ...@@ -37,7 +37,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied index should be an integer"); return Nan::ThrowTypeError("Supplied index should be an integer");
} }
Sass_Value* map = unwrap(info.This())->value; Sass_Value* map = Map::Unwrap<Map>(info.This())->value;
size_t index = Nan::To<uint32_t>(info[0]).FromJust(); size_t index = Nan::To<uint32_t>(info[0]).FromJust();
...@@ -63,14 +63,13 @@ namespace SassTypes ...@@ -63,14 +63,13 @@ namespace SassTypes
Value* sass_value = Factory::unwrap(info[1]); Value* sass_value = Factory::unwrap(info[1]);
if (sass_value) { if (sass_value) {
sass_map_set_value(unwrap(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value()); sass_map_set_value(Map::Unwrap<Map>(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
} else { } else {
Nan::ThrowTypeError("A SassValue is expected as a map value"); Nan::ThrowTypeError("A SassValue is expected as a map value");
} }
} }
NAN_METHOD(Map::GetKey) { NAN_METHOD(Map::GetKey) {
if (info.Length() != 1) { if (info.Length() != 1) {
return Nan::ThrowTypeError("Expected just one argument"); return Nan::ThrowTypeError("Expected just one argument");
} }
...@@ -79,7 +78,7 @@ namespace SassTypes ...@@ -79,7 +78,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied index should be an integer"); return Nan::ThrowTypeError("Supplied index should be an integer");
} }
Sass_Value* map = unwrap(info.This())->value; Sass_Value* map = Map::Unwrap<Map>(info.This())->value;
size_t index = Nan::To<uint32_t>(info[0]).FromJust(); size_t index = Nan::To<uint32_t>(info[0]).FromJust();
...@@ -87,7 +86,9 @@ namespace SassTypes ...@@ -87,7 +86,9 @@ namespace SassTypes
return Nan::ThrowRangeError(Nan::New("Out of bound index").ToLocalChecked()); return Nan::ThrowRangeError(Nan::New("Out of bound index").ToLocalChecked());
} }
info.GetReturnValue().Set(Factory::create(sass_map_get_key(map, Nan::To<uint32_t>(info[0]).FromJust()))->get_js_object()); SassTypes::Value* obj = Factory::create(sass_map_get_key(map, Nan::To<uint32_t>(info[0]).FromJust()));
v8::Local<v8::Object> js_obj = obj->get_js_object();
info.GetReturnValue().Set(js_obj);
} }
NAN_METHOD(Map::SetKey) { NAN_METHOD(Map::SetKey) {
...@@ -105,13 +106,13 @@ namespace SassTypes ...@@ -105,13 +106,13 @@ namespace SassTypes
Value* sass_value = Factory::unwrap(info[1]); Value* sass_value = Factory::unwrap(info[1]);
if (sass_value) { if (sass_value) {
sass_map_set_key(unwrap(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value()); sass_map_set_key(Map::Unwrap<Map>(info.This())->value, Nan::To<uint32_t>(info[0]).FromJust(), sass_value->get_sass_value());
} else { } else {
Nan::ThrowTypeError("A SassValue is expected as a map key"); Nan::ThrowTypeError("A SassValue is expected as a map key");
} }
} }
NAN_METHOD(Map::GetLength) { NAN_METHOD(Map::GetLength) {
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_map_get_length(unwrap(info.This())->value))); info.GetReturnValue().Set(Nan::New<v8::Number>(sass_map_get_length(Map::Unwrap<Map>(info.This())->value)));
} }
} }
...@@ -6,7 +6,9 @@ namespace SassTypes ...@@ -6,7 +6,9 @@ namespace SassTypes
Nan::Persistent<v8::Function> Null::constructor; Nan::Persistent<v8::Function> Null::constructor;
bool Null::constructor_locked = false; bool Null::constructor_locked = false;
Null::Null() {} Null::Null() {
value = sass_make_null();
}
Null& Null::get_singleton() { Null& Null::get_singleton() {
static Null singleton_instance; static Null singleton_instance;
...@@ -37,10 +39,6 @@ namespace SassTypes ...@@ -37,10 +39,6 @@ namespace SassTypes
return scope.Escape(conslocal); return scope.Escape(conslocal);
} }
Sass_Value* Null::get_sass_value() {
return sass_make_null();
}
v8::Local<v8::Object> Null::get_js_object() { v8::Local<v8::Object> Null::get_js_object() {
return Nan::New(this->js_object); return Nan::New(this->js_object);
} }
......
...@@ -23,6 +23,10 @@ namespace SassTypes ...@@ -23,6 +23,10 @@ namespace SassTypes
} }
unit = create_string(raw_val[1]); unit = create_string(raw_val[1]);
*out = sass_make_number(value, unit);
delete unit;
return *out;
} }
} }
...@@ -37,11 +41,11 @@ namespace SassTypes ...@@ -37,11 +41,11 @@ namespace SassTypes
} }
NAN_METHOD(Number::GetValue) { NAN_METHOD(Number::GetValue) {
info.GetReturnValue().Set(Nan::New<v8::Number>(sass_number_get_value(unwrap(info.This())->value))); info.GetReturnValue().Set(Nan::New<v8::Number>(sass_number_get_value(Number::Unwrap<Number>(info.This())->value)));
} }
NAN_METHOD(Number::GetUnit) { NAN_METHOD(Number::GetUnit) {
info.GetReturnValue().Set(Nan::New<v8::String>(sass_number_get_unit(unwrap(info.This())->value)).ToLocalChecked()); info.GetReturnValue().Set(Nan::New<v8::String>(sass_number_get_unit(Number::Unwrap<Number>(info.This())->value)).ToLocalChecked());
} }
NAN_METHOD(Number::SetValue) { NAN_METHOD(Number::SetValue) {
...@@ -54,7 +58,7 @@ namespace SassTypes ...@@ -54,7 +58,7 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a number"); return Nan::ThrowTypeError("Supplied value should be a number");
} }
sass_number_set_value(unwrap(info.This())->value, Nan::To<double>(info[0]).FromJust()); sass_number_set_value(Number::Unwrap<Number>(info.This())->value, Nan::To<double>(info[0]).FromJust());
} }
NAN_METHOD(Number::SetUnit) { NAN_METHOD(Number::SetUnit) {
...@@ -66,6 +70,6 @@ namespace SassTypes ...@@ -66,6 +70,6 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a string"); return Nan::ThrowTypeError("Supplied value should be a string");
} }
sass_number_set_unit(unwrap(info.This())->value, create_string(info[0])); sass_number_set_unit(Number::Unwrap<Number>(info.This())->value, create_string(info[0]));
} }
} }
...@@ -12,122 +12,90 @@ namespace SassTypes ...@@ -12,122 +12,90 @@ namespace SassTypes
// Include this in any SassTypes::Value subclasses to handle all the heavy lifting of constructing JS // Include this in any SassTypes::Value subclasses to handle all the heavy lifting of constructing JS
// objects and wrapping sass values inside them // objects and wrapping sass values inside them
template <class T> template <class T>
class SassValueWrapper : public SassTypes::Value { /* class SassValueWrapper : public SassTypes::Value { */
public: class SassValueWrapper : public SassTypes::Value {
static char const* get_constructor_name() { return "SassValue"; } public:
static char const* get_constructor_name() { return "SassValue"; }
SassValueWrapper(Sass_Value*); SassValueWrapper(Sass_Value* v) : Value(v) { }
virtual ~SassValueWrapper(); v8::Local<v8::Object> get_js_object();
Sass_Value* get_sass_value(); static v8::Local<v8::Function> get_constructor();
v8::Local<v8::Object> get_js_object(); static v8::Local<v8::FunctionTemplate> get_constructor_template();
static NAN_METHOD(New);
static Sass_Value *fail(const char *, Sass_Value **);
static v8::Local<v8::Function> get_constructor(); /* private: */
static v8::Local<v8::FunctionTemplate> get_constructor_template(); static Nan::Persistent<v8::Function> constructor;
static NAN_METHOD(New); };
static Sass_Value *fail(const char *, Sass_Value **);
protected:
Sass_Value* value;
static T* unwrap(v8::Local<v8::Object>);
private:
static Nan::Persistent<v8::Function> constructor;
Nan::Persistent<v8::Object> js_object;
};
template <class T> template <class T>
Nan::Persistent<v8::Function> SassValueWrapper<T>::constructor; Nan::Persistent<v8::Function> SassValueWrapper<T>::constructor;
template <class T> template <class T>
SassValueWrapper<T>::SassValueWrapper(Sass_Value* v) { v8::Local<v8::Object> SassValueWrapper<T>::get_js_object() {
this->value = sass_clone_value(v); if (this->persistent().IsEmpty()) {
} v8::Local<v8::Object> wrapper = Nan::NewInstance(T::get_constructor()).ToLocalChecked();
this->Wrap(wrapper);
template <class T> }
SassValueWrapper<T>::~SassValueWrapper() {
this->js_object.Reset();
sass_delete_value(this->value);
}
template <class T> return this->handle();
Sass_Value* SassValueWrapper<T>::get_sass_value() { }
return sass_clone_value(this->value);
}
template <class T> template <class T>
v8::Local<v8::Object> SassValueWrapper<T>::get_js_object() { v8::Local<v8::FunctionTemplate> SassValueWrapper<T>::get_constructor_template() {
if (this->js_object.IsEmpty()) { Nan::EscapableHandleScope scope;
v8::Local<v8::Object> wrapper = Nan::NewInstance(T::get_constructor()).ToLocalChecked(); v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
delete static_cast<T*>(Nan::GetInternalFieldPointer(wrapper, 0)); tpl->SetClassName(Nan::New<v8::String>(T::get_constructor_name()).ToLocalChecked());
Nan::SetInternalFieldPointer(wrapper, 0, this); tpl->InstanceTemplate()->SetInternalFieldCount(1);
this->js_object.Reset(wrapper); T::initPrototype(tpl);
return scope.Escape(tpl);
} }
return Nan::New(this->js_object);
}
template <class T> template <class T>
v8::Local<v8::FunctionTemplate> SassValueWrapper<T>::get_constructor_template() { v8::Local<v8::Function> SassValueWrapper<T>::get_constructor() {
Nan::EscapableHandleScope scope; if (constructor.IsEmpty()) {
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); constructor.Reset(Nan::GetFunction(T::get_constructor_template()).ToLocalChecked());
tpl->SetClassName(Nan::New<v8::String>(T::get_constructor_name()).ToLocalChecked()); }
tpl->InstanceTemplate()->SetInternalFieldCount(1);
T::initPrototype(tpl);
return scope.Escape(tpl);
}
template <class T> return Nan::New(constructor);
v8::Local<v8::Function> SassValueWrapper<T>::get_constructor() {
if (constructor.IsEmpty()) {
constructor.Reset(Nan::GetFunction(T::get_constructor_template()).ToLocalChecked());
} }
return Nan::New(constructor);
}
template <class T> template <class T>
NAN_METHOD(SassValueWrapper<T>::New) { NAN_METHOD(SassValueWrapper<T>::New) {
std::vector<v8::Local<v8::Value>> localArgs(info.Length()); std::vector<v8::Local<v8::Value>> localArgs(info.Length());
for (auto i = 0; i < info.Length(); ++i) { for (auto i = 0; i < info.Length(); ++i) {
localArgs[i] = info[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);
Nan::SetInternalFieldPointer(info.This(), 0, obj);
obj->js_object.Reset(info.This());
} else {
return Nan::ThrowError(Nan::New<v8::String>(sass_error_get_message(value)).ToLocalChecked());
} }
} else { if (info.IsConstructCall()) {
v8::Local<v8::Function> cons = T::get_constructor(); Sass_Value* value;
v8::Local<v8::Object> inst; if (T::construct(localArgs, &value) != NULL) {
if (Nan::NewInstance(cons, info.Length(), &localArgs[0]).ToLocal(&inst)) { T* obj = new T(value);
info.GetReturnValue().Set(inst); sass_delete_value(value);
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
} else {
return Nan::ThrowError(Nan::New<v8::String>(sass_error_get_message(value)).ToLocalChecked());
}
} else { } else {
info.GetReturnValue().Set(Nan::Undefined()); v8::Local<v8::Function> cons = T::get_constructor();
v8::Local<v8::Object> inst;
if (Nan::NewInstance(cons, info.Length(), &localArgs[0]).ToLocal(&inst)) {
info.GetReturnValue().Set(inst);
} else {
info.GetReturnValue().Set(Nan::Undefined());
}
} }
} }
}
template <class T> template <class T>
T* SassValueWrapper<T>::unwrap(v8::Local<v8::Object> obj) { Sass_Value *SassValueWrapper<T>::fail(const char *reason, Sass_Value **out) {
/* This maybe NULL */ *out = sass_make_error(reason);
return static_cast<T*>(Factory::unwrap(obj)); return NULL;
} }
template <class T>
Sass_Value *SassValueWrapper<T>::fail(const char *reason, Sass_Value **out) {
*out = sass_make_error(reason);
return NULL;
}
} }
#endif #endif
...@@ -15,9 +15,14 @@ namespace SassTypes ...@@ -15,9 +15,14 @@ namespace SassTypes
} }
value = create_string(raw_val[0]); value = create_string(raw_val[0]);
*out = sass_make_string(value);
delete value;
return *out;
} else {
return *out = sass_make_string(value);
} }
return *out = sass_make_string(value);
} }
void String::initPrototype(v8::Local<v8::FunctionTemplate> proto) { void String::initPrototype(v8::Local<v8::FunctionTemplate> proto) {
...@@ -26,7 +31,7 @@ namespace SassTypes ...@@ -26,7 +31,7 @@ namespace SassTypes
} }
NAN_METHOD(String::GetValue) { NAN_METHOD(String::GetValue) {
info.GetReturnValue().Set(Nan::New<v8::String>(sass_string_get_value(unwrap(info.This())->value)).ToLocalChecked()); info.GetReturnValue().Set(Nan::New<v8::String>(sass_string_get_value(String::Unwrap<String>(info.This())->value)).ToLocalChecked());
} }
NAN_METHOD(String::SetValue) { NAN_METHOD(String::SetValue) {
...@@ -38,6 +43,6 @@ namespace SassTypes ...@@ -38,6 +43,6 @@ namespace SassTypes
return Nan::ThrowTypeError("Supplied value should be a string"); return Nan::ThrowTypeError("Supplied value should be a string");
} }
sass_string_set_value(unwrap(info.This())->value, create_string(info[0])); sass_string_set_value(String::Unwrap<String>(info.This())->value, create_string(info[0]));
} }
} }
...@@ -7,10 +7,35 @@ ...@@ -7,10 +7,35 @@
namespace SassTypes namespace SassTypes
{ {
// This is the interface that all sass values must comply with // This is the interface that all sass values must comply with
class Value { class Value : public Nan::ObjectWrap {
public: public:
virtual Sass_Value* get_sass_value() =0;
virtual v8::Local<v8::Object> get_js_object() =0; virtual v8::Local<v8::Object> 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;
}
}; };
} }
......
...@@ -310,7 +310,6 @@ describe('api', function() { ...@@ -310,7 +310,6 @@ describe('api', function() {
contents: '@import "b"' contents: '@import "b"'
}); });
} else { } else {
console.log(prev);
assert.equal(prev, '/Users/me/sass/lib/a.scss'); assert.equal(prev, '/Users/me/sass/lib/a.scss');
done({ done({
file: '/Users/me/sass/lib/b.scss', file: '/Users/me/sass/lib/b.scss',
......
/*eslint new-cap: ["error", { "capIsNew": false }]*/
'use strict';
var assert = require('assert');
var sass = require('../');
describe('sass.types', function() {
describe('Boolean', function() {
it('exists', function() {
assert(sass.types.Boolean);
});
it('names the constructor correctly', function() {
assert.equal(sass.types.Boolean.name, 'SassBoolean');
});
it('supports call constructor', function() {
var t = sass.types.Boolean(true);
assert.equal(t.toString(), '[object SassBoolean]');
var f = sass.types.Boolean(false);
assert.equal(f.toString(), '[object SassBoolean]');
});
it('has true and false singletons', function() {
assert.equal(sass.types.Boolean(true), sass.types.Boolean(true));
assert.equal(sass.types.Boolean(false), sass.types.Boolean(false));
assert.notEqual(sass.types.Boolean(false), sass.types.Boolean(true));
assert.equal(sass.types.Boolean(true), sass.types.Boolean.TRUE);
assert.equal(sass.types.Boolean(false), sass.types.Boolean.FALSE);
});
it('supports DOES NOT support new constructor', function() {
assert.throws(function() {
new sass.types.Boolean(true);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Cannot instantiate SassBoolean');
return true;
});
});
it('throws with incorrect constructor args', function() {
assert.throws(function() {
sass.types.Boolean();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected one boolean argument');
return true;
});
[1, 2, '', 'hi', {}, []].forEach(function(arg) {
assert.throws(function() {
sass.types.Boolean(arg);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected one boolean argument');
return true;
});
});
assert.throws(function() {
sass.types.Boolean(true, false);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected one boolean argument');
return true;
});
});
it('implements getValue', function() {
var t = sass.types.Boolean(true);
assert.equal(typeof t.getValue, 'function');
assert.equal(t.getValue(), true);
var f = sass.types.Boolean(false);
assert.equal(typeof f.getValue, 'function');
assert.equal(f.getValue(), false);
});
});
describe('Color', function() {
it('exists', function() {
assert(sass.types.Color);
});
it('names the constructor correctly', function() {
assert.equal(sass.types.Color.name, 'SassColor');
});
it('supports call constructor', function() {
var t = sass.types.Color();
assert.equal(t.toString(), '[object SassColor]');
});
it('supports new constructor', function() {
var t = new sass.types.Color(1);
assert.equal(t.toString(), '[object SassColor]');
});
it('supports variadic constructor args', function() {
var a = new sass.types.Color();
assert.equal(a.getR(), 0);
assert.equal(a.getG(), 0);
assert.equal(a.getB(), 0);
assert.equal(a.getA(), 1);
var b = new sass.types.Color(1);
assert.equal(b.getR(), 0);
assert.equal(b.getG(), 0);
assert.equal(b.getB(), 1);
assert.equal(b.getA(), 0); // why ?
assert.throws(function() {
new sass.types.Color(1, 2);
}, function(error) {
// assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Constructor should be invoked with either 0, 1, 3 or 4 arguments.');
return true;
});
var c = new sass.types.Color(1, 2, 3);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 2);
assert.equal(c.getB(), 3);
assert.equal(c.getA(), 1);
var d = new sass.types.Color(1, 2, 3, 4);
assert.equal(d.getR(), 1);
assert.equal(d.getG(), 2);
assert.equal(d.getB(), 3);
assert.equal(d.getA(), 4);
assert.throws(function() {
new sass.types.Color(1, 2, 3, 4, 5);
}, function(error) {
// assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Constructor should be invoked with either 0, 1, 3 or 4 arguments.');
return true;
});
});
it('supports get{R,G,B,A} and set{R,G,B,A}', function() {
var c = new sass.types.Color();
assert.equal(c.getR(), 0);
assert.equal(c.getG(), 0);
assert.equal(c.getB(), 0);
assert.equal(c.getA(), 1);
assert.equal(c.setR(1), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 0);
assert.equal(c.getB(), 0);
assert.equal(c.getA(), 1);
assert.equal(c.setG(1), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 1);
assert.equal(c.getB(), 0);
assert.equal(c.getA(), 1);
assert.equal(c.setB(1), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 1);
assert.equal(c.getB(), 1);
assert.equal(c.getA(), 1);
assert.equal(c.setA(0), undefined);
assert.equal(c.getR(), 1);
assert.equal(c.getG(), 1);
assert.equal(c.getB(), 1);
assert.equal(c.getA(), 0);
});
it('throws with incorrect set{R,G,B,A} arguments', function() {
var c = new sass.types.Color();
function assertJustOneArgument(cb) {
assert.throws(function() {
cb();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
}
function assertNumberArgument(arg, cb) {
assert.throws(function() {
cb();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a number');
return true;
}, 'argument was: ' + arg);
}
assertJustOneArgument(function() { c.setR(); });
assertJustOneArgument(function() { c.setG(); });
assertJustOneArgument(function() { c.setB(); });
assertJustOneArgument(function() { c.setA(); });
assertJustOneArgument(function() { c.setR(1, 2); });
assertJustOneArgument(function() { c.setG(1, 2); });
assertJustOneArgument(function() { c.setB(1, 2); });
assertJustOneArgument(function() { c.setA(1, 2); });
[true, false, '0', '1', '', 'omg', {}, []].forEach(function(arg) {
assertNumberArgument(arg, function() { c.setR(arg); });
assertNumberArgument(arg, function() { c.setG(arg); });
assertNumberArgument(arg, function() { c.setB(arg); });
assertNumberArgument(arg, function() { c.setA(arg); });
});
});
});
describe('Error', function() {
it('exists', function() {
assert(sass.types.Error);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Error.name, 'SassError');
});
it('supports call constructor', function() {
var e = sass.types.Error('Such Error');
assert.ok(e instanceof sass.types.Error);
assert.equal(e.toString(), '[object SassError]');
// TODO: I'm not sure this object works well, it likely needs to be fleshed out more...
});
it('supports new constructor', function() {
var e = new sass.types.Error('Such Error');
assert.ok(e instanceof sass.types.Error);
assert.equal(e.toString(), '[object SassError]');
// TODO: I'm not sure this object works well, it likely needs to be fleshed out more...
});
});
describe('List', function() {
it('exists', function() {
assert(sass.types.List);
});
it('has a corectly named constructor', function() {
assert.equal(sass.types.List.name, 'SassList');
});
it('support call constructor', function() {
var list = sass.types.List();
assert.ok(list instanceof sass.types.List);
assert.equal(list.toString(), '[object SassList]');
});
it('support new constructor', function() {
var list = new sass.types.List();
assert.ok(list instanceof sass.types.List);
assert.equal(list.toString(), '[object SassList]');
});
it('support variadic constructor', function() {
var a = new sass.types.List();
assert.equal(a.getLength(), 0);
assert.equal(a.getSeparator(), true);
var b = new sass.types.List(1);
assert.equal(b.getSeparator(), true);
assert.equal(b.getLength(), 1);
var c = new sass.types.List(1, true);
assert.equal(b.getLength(), 1);
assert.equal(c.getSeparator(), true);
var d = new sass.types.List(1, false);
assert.equal(b.getLength(), 1);
assert.equal(d.getSeparator(), false);
var e = new sass.types.List(1, true, 2);
assert.equal(b.getLength(), 1);
assert.equal(e.getSeparator(), true);
assert.throws(function() {
new sass.types.List('not-a-number');
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'First argument should be an integer.');
return true;
});
assert.throws(function() {
new sass.types.List(1, 'not-a-boolean');
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'Second argument should be a boolean.');
return true;
});
});
it('supports {get,set}Separator', function() {
var a = new sass.types.List();
assert.equal(a.getSeparator(), true);
assert.equal(a.setSeparator(true), undefined);
assert.equal(a.getSeparator(), true);
assert.equal(a.setSeparator(false), undefined);
assert.equal(a.getSeparator(), false);
assert.throws(function() {
a.setSeparator();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
[1, '', [], {}].forEach(function(arg) {
assert.throws(function() {
a.setSeparator(arg);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a boolean');
return true;
}, 'setSeparator(' + arg + ')');
});
});
it('supports setValue and getValue', function() {
var a = new sass.types.List();
assert.throws(function() {
a.getValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
['hi', [], {}].forEach(function(arg) {
assert.throws(function() {
a.getValue(arg);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied index should be an integer');
return true;
}, 'getValue(' + arg + ')');
});
assert.throws(function() {
a.getValue(0);
}, function(error) {
assert.ok(error instanceof RangeError);
assert.equal(error.message, 'Out of bound index');
return true;
});
assert.throws(function() {
a.getValue(-1);
}, function(error) {
assert.ok(error instanceof RangeError);
assert.equal(error.message, 'Out of bound index');
return true;
});
assert.throws(function() {
a.setValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected two arguments');
return true;
});
assert.throws(function() {
a.setValue(1);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected two arguments');
return true;
});
assert.throws(function() {
a.setValue(0, 'no-a-sass-value');
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a SassValue object');
return true;
});
});
// TODO: more complex set/get value scenarios
});
describe('Map', function() {
it('exists', function() {
assert(sass.types.Map);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Map.name, 'SassMap');
});
it('supports call constructor', function() {
var x = sass.types.Map();
assert.equal(x.toString(), '[object SassMap]');
});
it('supports new constructor', function() {
var x = new sass.types.Map();
assert.equal(x.toString(), '[object SassMap]');
});
it('supports an optional constructor argument', function() {
var x = new sass.types.Map();
var y = new sass.types.Map(1);
var z = new sass.types.Map(2, 3);
assert.throws(function() {
new sass.types.Map('OMG');
}, function(error) {
assert.equal(error.message, 'First argument should be an integer.');
// TODO: TypeError
return true;
});
assert.equal(x.getLength(), 0);
assert.equal(y.getLength(), 1);
assert.equal(z.getLength(), 2);
});
it('supports length', function() {
var y = new sass.types.Map(1);
var z = new sass.types.Map(2);
assert.equal(y.getLength(), 1);
assert.equal(z.getLength(), 2);
});
it('supports {get,set}Value {get,set}Key', function() {
var y = new sass.types.Map(1);
var omg = new sass.types.String('OMG');
y.setValue(0, omg);
console.log(y.getValue(0));
});
});
describe('Null', function() {
it('exists', function() {
assert(sass.types.Null);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Null.name, 'SassNull');
});
it('does not support new constructor', function() {
assert.throws(function() {
new sass.types.Null();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Cannot instantiate SassNull');
return true;
});
});
it('supports call constructor (and is a singleton)', function() {
assert.equal(sass.types.Null(), sass.types.Null());
assert.equal(sass.types.Null(), sass.types.Null.NULL);
});
});
describe('Number', function() {
it('exists', function() {
assert(sass.types.Number);
});
it('has a correctly named constructor', function() {
assert.equal(sass.types.Number.name, 'SassNumber');
});
it('supports new constructor', function() {
var number = new sass.types.Number();
assert.equal(number.toString(), '[object SassNumber]');
});
it('supports call constructor', function() {
var number = sass.types.Number();
assert.equal(number.toString(), '[object SassNumber]');
});
it('supports multiple constructor arguments', function() {
var a = new sass.types.Number();
var b = new sass.types.Number(1);
var c = new sass.types.Number(2, 'px');
assert.throws(function() {
new sass.types.Number('OMG');
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'First argument should be a number.');
return true;
});
assert.throws(function() {
new sass.types.Number(1, 2);
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'Second argument should be a string.');
return true;
});
assert.equal(a.getValue(), 0);
assert.equal(a.getUnit(), '');
assert.equal(b.getValue(), 1);
assert.equal(b.getUnit(), '');
assert.equal(c.getValue(), 2);
assert.equal(c.getUnit(), 'px');
});
it('supports get{Unit,Value}, set{Unit,Value}', function() {
var number = new sass.types.Number(1, 'px');
assert.equal(number.getValue(), 1);
assert.equal(number.getUnit(), 'px');
number.setValue(2);
assert.equal(number.getValue(), 2);
assert.equal(number.getUnit(), 'px');
number.setUnit('em');
assert.equal(number.getValue(), 2);
assert.equal(number.getUnit(), 'em');
assert.throws(function() {
number.setValue('OMG');
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a number');
return true;
});
assert.throws(function() {
number.setValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
assert.throws(function() {
number.setUnit();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
assert.throws(function() {
number.setUnit(1);
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Supplied value should be a string');
return true;
});
});
});
describe('String', function() {
it('exists', function() {
assert(sass.types.String);
});
it('has a properly named constructor', function() {
assert.equal(sass.types.String.name, 'SassString');
});
it('supports call constructor', function() {
var x = sass.types.String('OMG');
assert.equal(x.toString(), '[object SassString]');
assert.equal(x.getValue(), 'OMG');
});
it('supports new constructor', function() {
var x = new sass.types.String('OMG');
assert.equal(x.toString(), '[object SassString]');
assert.equal(x.getValue(), 'OMG');
});
it('supports multiple constructor arg combinations', function() {
new sass.types.String();
new sass.types.String('OMG');
new sass.types.String('OMG', 'NOPE');
[null, undefined, [], {}, function() { }].forEach(function(arg) {
assert.throws(function() {
new sass.types.String(arg);
}, function(error) {
// TODO: TypeError
assert.equal(error.message, 'Argument should be a string.');
return true;
});
});
});
it('supports {get,set}Value', function() {
var x = new sass.types.String();
assert.equal(x.getValue(), '');
assert.equal(x.setValue('hi'), undefined);
assert.equal(x.getValue(), 'hi');
assert.equal(x.setValue('bye'), undefined);
assert.equal(x.getValue(), 'bye');
assert.throws(function() {
x.setValue();
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
assert.throws(function() {
x.setValue('hi', 'hi');
}, function(error) {
assert.ok(error instanceof TypeError);
assert.equal(error.message, 'Expected just one argument');
return true;
});
});
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment