Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for VS2013 #65

Merged
merged 1 commit into from
Jun 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,13 +555,13 @@ inline std::string String::Utf8Value() const {
inline std::u16string String::Utf16Value() const {
size_t length;
napi_status status = napi_get_value_string_utf16(_env, _value, nullptr, 0, &length);
NAPI_THROW_IF_FAILED(_env, status, u"");
NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT(""));

std::u16string value;
value.reserve(length + 1);
value.resize(length);
status = napi_get_value_string_utf16(_env, _value, &value[0], value.capacity(), nullptr);
NAPI_THROW_IF_FAILED(_env, status, u"");
NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT(""));
return value;
}

Expand Down Expand Up @@ -1490,7 +1490,7 @@ inline Error& Error::operator =(Error&& other) {
return *this;
}

inline Error::Error(const Error& other) : Error(other.Env(), other.Value()) {
inline Error::Error(const Error& other) : ObjectReference(other) {
}

inline Error& Error::operator =(Error& other) {
Expand Down Expand Up @@ -1644,6 +1644,22 @@ inline Reference<T>& Reference<T>::operator =(Reference<T>&& other) {
return *this;
}

template <typename T>
inline Reference<T>::Reference(const Reference<T>& other) {
_env = other.Env();
HandleScope scope(_env);

napi_value value = other.Value();
if (value != nullptr) {
// Copying is a limited scenario (currently only used for Error object) and always creates a
// strong reference to the given value even if the incoming reference is weak.
napi_status status = napi_create_reference(_env, value, 1, &_ref);

// TODO - Switch to napi_fatal_error() once it exists.
assert(status == napi_ok);
}
}

template <typename T>
inline Reference<T>::operator napi_ref() const {
return _ref;
Expand Down Expand Up @@ -1778,6 +1794,10 @@ inline ObjectReference& ObjectReference::operator =(ObjectReference&& other) {
return *this;
}

inline ObjectReference::ObjectReference(const ObjectReference& other)
: Reference<Object>(other) {
}

inline Napi::Value ObjectReference::Get(const char* utf8name) const {
EscapableHandleScope scope(_env);
return scope.Escape(Value().Get(utf8name));
Expand Down Expand Up @@ -2697,11 +2717,11 @@ inline FunctionReference& AsyncWorker::Callback() {
}

inline void AsyncWorker::OnOK() {
_callback.MakeCallback(_receiver.Value(), {});
_callback.MakeCallback(_receiver.Value(), std::initializer_list<napi_value>{});
}

inline void AsyncWorker::OnError(const Error& e) {
_callback.MakeCallback(_receiver.Value(), { e.Value() });
_callback.MakeCallback(_receiver.Value(), std::initializer_list<napi_value>{ e.Value() });
}

inline void AsyncWorker::SetError(const std::string& error) {
Expand Down
37 changes: 33 additions & 4 deletions napi.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@
#include <string>
#include <vector>

// VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known good version)
#if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210
#define NAPI_HAS_CONSTEXPR 1
#endif

// VS2013 does not support char16_t literal strings, so we'll work around it using wchar_t strings
// and casting them. This is safe as long as the character sizes are the same.
#if defined(_MSC_VER) && _MSC_VER <= 1800
static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16_t and wchar_t");
#define NAPI_WIDE_TEXT(x) reinterpret_cast<char16_t*>(L ## x)
#else
#define NAPI_WIDE_TEXT(x) u ## x
#endif

// If C++ exceptions are not explicitly enabled or disabled, enable them
// if exceptions were enabled in the compiler settings.
#if !defined(NAPI_CPP_EXCEPTIONS) && !defined(NAPI_DISABLE_CPP_EXCEPTIONS)
Expand Down Expand Up @@ -60,7 +74,7 @@ namespace Napi {
typedef TypedArrayOf<double> Float64Array; ///< Typed-array of 64-bit floating-point values

/// Defines the signature of a N-API C++ module's registration callback (init) function.
typedef void ModuleRegisterCallback(Env env, Object exports, Object module);
typedef void (*ModuleRegisterCallback)(Env env, Object exports, Object module);

/// Environment for N-API values and operations.
///
Expand Down Expand Up @@ -671,7 +685,11 @@ namespace Napi {
static const napi_typedarray_type unknown_array_type = static_cast<napi_typedarray_type>(-1);

template <typename T>
static constexpr napi_typedarray_type TypedArrayTypeForPrimitiveType() {
static
#if defined(NAPI_HAS_CONSTEXPR)
constexpr
#endif
napi_typedarray_type TypedArrayTypeForPrimitiveType() {
return std::is_same<T, int8_t>::value ? napi_int8_array
: std::is_same<T, uint8_t>::value ? napi_uint8_array
: std::is_same<T, int16_t>::value ? napi_int16_array
Expand Down Expand Up @@ -701,7 +719,11 @@ namespace Napi {
static TypedArrayOf New(
napi_env env, ///< N-API environment
size_t elementLength, ///< Length of the created array, as a number of elements
#if defined(NAPI_HAS_CONSTEXPR)
napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType<T>()
#else
napi_typedarray_type type
#endif
///< Type of array, if different from the default array type for the template parameter T.
);

Expand All @@ -716,7 +738,11 @@ namespace Napi {
size_t elementLength, ///< Length of the created array, as a number of elements
Napi::ArrayBuffer arrayBuffer, ///< Backing array buffer instance to use
size_t bufferOffset, ///< Offset into the array buffer where the typed-array starts
#if defined(NAPI_HAS_CONSTEXPR)
napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType<T>()
#else
napi_typedarray_type type
#endif
///< Type of array, if different from the default array type for the template parameter T.
);

Expand Down Expand Up @@ -830,7 +856,6 @@ namespace Napi {
// A reference can be moved but cannot be copied.
Reference(Reference<T>&& other);
Reference<T>& operator =(Reference<T>&& other);
Reference(const Reference<T>&) = delete;
Reference<T>& operator =(Reference<T>&) = delete;

operator napi_ref() const;
Expand All @@ -855,6 +880,8 @@ namespace Napi {
void SuppressDestruct();

protected:
Reference(const Reference<T>&);

/// !cond INTERNAL
napi_env _env;
napi_ref _ref;
Expand All @@ -874,7 +901,6 @@ namespace Napi {
ObjectReference& operator =(Reference<Object>&& other);
ObjectReference(ObjectReference&& other);
ObjectReference& operator =(ObjectReference&& other);
ObjectReference(const ObjectReference&) = delete;
ObjectReference& operator =(ObjectReference&) = delete;

Napi::Value Get(const char* utf8name) const;
Expand All @@ -897,6 +923,9 @@ namespace Napi {
void Set(uint32_t index, const std::string& utf8value);
void Set(uint32_t index, bool boolValue);
void Set(uint32_t index, double numberValue);

protected:
ObjectReference(const ObjectReference&);
};

class FunctionReference: public Reference<Function> {
Expand Down
2 changes: 1 addition & 1 deletion test/error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void DoNotCatch(const CallbackInfo& info) {

void ThrowApiError(const CallbackInfo& info) {
// Attempting to call an empty function value will throw an API error.
Function(info.Env(), nullptr).Call({});
Function(info.Env(), nullptr).Call(std::initializer_list<napi_value>{});
}

#ifdef NAPI_CPP_EXCEPTIONS
Expand Down
6 changes: 3 additions & 3 deletions test/function.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Value CallWithVector(const CallbackInfo& info) {
Value CallWithReceiverAndArgs(const CallbackInfo& info) {
Function func = info[0].As<Function>();
Value receiver = info[1];
return func.Call(receiver, { info[2], info[3], info[4] });
return func.Call(receiver, std::initializer_list<napi_value>{ info[2], info[3], info[4] });
}

Value CallWithReceiverAndVector(const CallbackInfo& info) {
Expand All @@ -78,12 +78,12 @@ Value CallWithReceiverAndVector(const CallbackInfo& info) {

Value CallWithInvalidReceiver(const CallbackInfo& info) {
Function func = info[0].As<Function>();
return func.Call(Value(), {});
return func.Call(Value(), std::initializer_list<napi_value>{});
}

Value CallConstructorWithArgs(const CallbackInfo& info) {
Function func = info[0].As<Function>();
return func.New({ info[1], info[2], info[3] });
return func.New(std::initializer_list<napi_value>{ info[1], info[2], info[3] });
}

Value CallConstructorWithVector(const CallbackInfo& info) {
Expand Down
5 changes: 5 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@ let testModules = [
];

if (typeof global.gc === 'function') {
console.log('Starting test suite\n');

// Requiring each module runs tests in the module.
testModules.forEach(name => {
console.log(`Running test '${name}'`);
require('./' + name);
});

console.log('\nAll tests passed!');
} else {
// Make it easier to run with the correct (version-dependent) command-line args.
const args = [ '--expose-gc', __filename ];
Expand Down
2 changes: 1 addition & 1 deletion test/name.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using namespace Napi;

const char* testValueUtf8 = "123456789";
const char16_t* testValueUtf16 = u"123456789";
const char16_t* testValueUtf16 = NAPI_WIDE_TEXT("123456789");

Value EchoString(const CallbackInfo& info) {
String value = info[0].As<String>();
Expand Down
26 changes: 19 additions & 7 deletions test/object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,26 @@ void DefineProperties(const CallbackInfo& info) {
PropertyDescriptor::Function("function", TestFunction),
});
} else if (nameType.Utf8Value() == "string") {
// VS2013 has lifetime issues when passing temporary objects into the constructor of another
// object. It generates code to destruct the object as soon as the constructor call returns.
// Since this isn't a common case for using std::string objects, I'm refactoring the test to
// work around the issue.
std::string str1("readonlyAccessor");
std::string str2("readwriteAccessor");
std::string str3("readonlyValue");
std::string str4("readwriteValue");
std::string str5("enumerableValue");
std::string str6("configurableValue");
std::string str7("function");

obj.DefineProperties({
PropertyDescriptor::Accessor(std::string("readonlyAccessor"), TestGetter),
PropertyDescriptor::Accessor(std::string("readwriteAccessor"), TestGetter, TestSetter),
PropertyDescriptor::Value(std::string("readonlyValue"), trueValue),
PropertyDescriptor::Value(std::string("readwriteValue"), trueValue, napi_writable),
PropertyDescriptor::Value(std::string("enumerableValue"), trueValue, napi_enumerable),
PropertyDescriptor::Value(std::string("configurableValue"), trueValue, napi_configurable),
PropertyDescriptor::Function(std::string("function"), TestFunction),
PropertyDescriptor::Accessor(str1, TestGetter),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unfortunate, and seems likely to confuse many people (if they are using VS 2013). The way I wanted this to work for the most common case is you should be able to just pass in a string literal:

PropertyDescriptor::Accessor("readonlyAccessor", TestGetter),

Did you try that? I'd assume the behavior is the same as the explicit call to std::string(), but I'm not sure.

If that doesn't work, then I think it would be best to add overloads of each of those methods that accept a const char* instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasongin there is already an overload for const char *, that's what the other leg of the conditional tests. It just gets chained to for the std::string constructor. Does that resolve your concerns?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry I didn't look at the broader context in this file! Yes, that resolves my concerns then.

PropertyDescriptor::Accessor(str2, TestGetter, TestSetter),
PropertyDescriptor::Value(str3, trueValue),
PropertyDescriptor::Value(str4, trueValue, napi_writable),
PropertyDescriptor::Value(str5, trueValue, napi_enumerable),
PropertyDescriptor::Value(str6, trueValue, napi_configurable),
PropertyDescriptor::Function(str7, TestFunction),
});
} else if (nameType.Utf8Value() == "value") {
obj.DefineProperties({
Expand Down
65 changes: 46 additions & 19 deletions test/typedarray.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

using namespace Napi;

#if defined(NAPI_HAS_CONSTEXPR)
#define NAPI_TYPEDARRAY_NEW(className, env, length, type) className::New(env, length)
#define NAPI_TYPEDARRAY_NEW_BUFFER(className, env, length, buffer, bufferOffset, type) \
className::New(env, length, buffer, bufferOffset)
#else
#define NAPI_TYPEDARRAY_NEW(className, env, length, type) className::New(env, length, type)
#define NAPI_TYPEDARRAY_NEW_BUFFER(className, env, length, buffer, bufferOffset, type) \
className::New(env, length, buffer, bufferOffset, type)
#endif

namespace {

Value CreateTypedArray(const CallbackInfo& info) {
Expand All @@ -11,40 +21,57 @@ Value CreateTypedArray(const CallbackInfo& info) {
size_t bufferOffset = info[3].IsUndefined() ? 0 : info[3].As<Number>().Uint32Value();

if (arrayType == "int8") {
return buffer.IsUndefined() ? Int8Array::New(info.Env(), length)
: Int8Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Int8Array, info.Env(), length, napi_int8_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Int8Array, info.Env(), length, buffer, bufferOffset,
napi_int8_array);
} else if (arrayType == "uint8") {
return buffer.IsUndefined() ? Uint8Array::New(info.Env(), length)
: Uint8Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Uint8Array, info.Env(), length, napi_uint8_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Uint8Array, info.Env(), length, buffer, bufferOffset,
napi_uint8_array);
} else if (arrayType == "uint8_clamped") {
return buffer.IsUndefined() ? Uint8Array::New(info.Env(), length, napi_uint8_clamped_array)
: Uint8Array::New(info.Env(), length, buffer, bufferOffset, napi_uint8_clamped_array);
return buffer.IsUndefined() ?
Uint8Array::New(info.Env(), length, napi_uint8_clamped_array) :
Uint8Array::New(info.Env(), length, buffer, bufferOffset, napi_uint8_clamped_array);
} else if (arrayType == "int16") {
return buffer.IsUndefined() ? Int16Array::New(info.Env(), length)
: Int16Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Int16Array, info.Env(), length, napi_int16_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Int16Array, info.Env(), length, buffer, bufferOffset,
napi_int16_array);
} else if (arrayType == "uint16") {
return buffer.IsUndefined() ? Uint16Array::New(info.Env(), length)
: Uint16Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Uint16Array, info.Env(), length, napi_uint16_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Uint16Array, info.Env(), length, buffer, bufferOffset,
napi_uint16_array);
} else if (arrayType == "int32") {
return buffer.IsUndefined() ? Int32Array::New(info.Env(), length)
: Int32Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Int32Array, info.Env(), length, napi_int32_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Int32Array, info.Env(), length, buffer, bufferOffset,
napi_int32_array);
} else if (arrayType == "uint32") {
return buffer.IsUndefined() ? Uint32Array::New(info.Env(), length)
: Uint32Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Uint32Array, info.Env(), length, napi_uint32_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Uint32Array, info.Env(), length, buffer, bufferOffset,
napi_uint32_array);
} else if (arrayType == "float32") {
return buffer.IsUndefined() ? Float32Array::New(info.Env(), length)
: Float32Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Float32Array, info.Env(), length, napi_float32_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Float32Array, info.Env(), length, buffer, bufferOffset,
napi_float32_array);
} else if (arrayType == "float64") {
return buffer.IsUndefined() ? Float64Array::New(info.Env(), length)
: Float64Array::New(info.Env(), length, buffer, bufferOffset);
return buffer.IsUndefined() ?
NAPI_TYPEDARRAY_NEW(Float64Array, info.Env(), length, napi_float64_array) :
NAPI_TYPEDARRAY_NEW_BUFFER(Float64Array, info.Env(), length, buffer, bufferOffset,
napi_float64_array);
} else {
Error::New(info.Env(), "Invalid typed-array type.").ThrowAsJavaScriptException();
return Value();
}
}

Value CreateInvalidTypedArray(const CallbackInfo& info) {
return Int8Array::New(info.Env(), 1, ArrayBuffer(), 0);
return NAPI_TYPEDARRAY_NEW_BUFFER(Int8Array, info.Env(), 1, ArrayBuffer(), 0, napi_int8_array);
}

Value GetTypedArrayType(const CallbackInfo& info) {
Expand Down