Skip to content

Commit 617dbb6

Browse files
kfarnungjasongin
authored andcommitted
Add support for VS2013 (#65)
VS2013 issues * No constexpr support - Conditionally use constexpr if the compiler supports it - Updated tests to use a macro for this purpose * Requires copy constructor support for base classes of thrown objects - Added a protected copy constructor to ObjectReference and Reference<T> * Incorrect overload resolution for std::initializer_list - Used explicit types to invoke the correct overload * No literal support for char16_t - Added macro to convert the text properly * Lambdas are unable to capture function non-pointers - Updated the typedef to create a function pointer * Short lifetime for temporary objects passed to constructors - Updated the tests to create the objects and pass them to the constructor. This is closer to how they would be used anyway General improvements * Added console.log output to the test runner to make it more clear when and where tests are failing
1 parent 244abc3 commit 617dbb6

File tree

8 files changed

+133
-40
lines changed

8 files changed

+133
-40
lines changed

napi-inl.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -555,13 +555,13 @@ inline std::string String::Utf8Value() const {
555555
inline std::u16string String::Utf16Value() const {
556556
size_t length;
557557
napi_status status = napi_get_value_string_utf16(_env, _value, nullptr, 0, &length);
558-
NAPI_THROW_IF_FAILED(_env, status, u"");
558+
NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT(""));
559559

560560
std::u16string value;
561561
value.reserve(length + 1);
562562
value.resize(length);
563563
status = napi_get_value_string_utf16(_env, _value, &value[0], value.capacity(), nullptr);
564-
NAPI_THROW_IF_FAILED(_env, status, u"");
564+
NAPI_THROW_IF_FAILED(_env, status, NAPI_WIDE_TEXT(""));
565565
return value;
566566
}
567567

@@ -1490,7 +1490,7 @@ inline Error& Error::operator =(Error&& other) {
14901490
return *this;
14911491
}
14921492

1493-
inline Error::Error(const Error& other) : Error(other.Env(), other.Value()) {
1493+
inline Error::Error(const Error& other) : ObjectReference(other) {
14941494
}
14951495

14961496
inline Error& Error::operator =(Error& other) {
@@ -1644,6 +1644,22 @@ inline Reference<T>& Reference<T>::operator =(Reference<T>&& other) {
16441644
return *this;
16451645
}
16461646

1647+
template <typename T>
1648+
inline Reference<T>::Reference(const Reference<T>& other) {
1649+
_env = other.Env();
1650+
HandleScope scope(_env);
1651+
1652+
napi_value value = other.Value();
1653+
if (value != nullptr) {
1654+
// Copying is a limited scenario (currently only used for Error object) and always creates a
1655+
// strong reference to the given value even if the incoming reference is weak.
1656+
napi_status status = napi_create_reference(_env, value, 1, &_ref);
1657+
1658+
// TODO - Switch to napi_fatal_error() once it exists.
1659+
assert(status == napi_ok);
1660+
}
1661+
}
1662+
16471663
template <typename T>
16481664
inline Reference<T>::operator napi_ref() const {
16491665
return _ref;
@@ -1778,6 +1794,10 @@ inline ObjectReference& ObjectReference::operator =(ObjectReference&& other) {
17781794
return *this;
17791795
}
17801796

1797+
inline ObjectReference::ObjectReference(const ObjectReference& other)
1798+
: Reference<Object>(other) {
1799+
}
1800+
17811801
inline Napi::Value ObjectReference::Get(const char* utf8name) const {
17821802
EscapableHandleScope scope(_env);
17831803
return scope.Escape(Value().Get(utf8name));
@@ -2697,11 +2717,11 @@ inline FunctionReference& AsyncWorker::Callback() {
26972717
}
26982718

26992719
inline void AsyncWorker::OnOK() {
2700-
_callback.MakeCallback(_receiver.Value(), {});
2720+
_callback.MakeCallback(_receiver.Value(), std::initializer_list<napi_value>{});
27012721
}
27022722

27032723
inline void AsyncWorker::OnError(const Error& e) {
2704-
_callback.MakeCallback(_receiver.Value(), { e.Value() });
2724+
_callback.MakeCallback(_receiver.Value(), std::initializer_list<napi_value>{ e.Value() });
27052725
}
27062726

27072727
inline void AsyncWorker::SetError(const std::string& error) {

napi.h

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@
77
#include <string>
88
#include <vector>
99

10+
// VS2015 RTM has bugs with constexpr, so require min of VS2015 Update 3 (known good version)
11+
#if !defined(_MSC_VER) || _MSC_FULL_VER >= 190024210
12+
#define NAPI_HAS_CONSTEXPR 1
13+
#endif
14+
15+
// VS2013 does not support char16_t literal strings, so we'll work around it using wchar_t strings
16+
// and casting them. This is safe as long as the character sizes are the same.
17+
#if defined(_MSC_VER) && _MSC_VER <= 1800
18+
static_assert(sizeof(char16_t) == sizeof(wchar_t), "Size mismatch between char16_t and wchar_t");
19+
#define NAPI_WIDE_TEXT(x) reinterpret_cast<char16_t*>(L ## x)
20+
#else
21+
#define NAPI_WIDE_TEXT(x) u ## x
22+
#endif
23+
1024
// If C++ exceptions are not explicitly enabled or disabled, enable them
1125
// if exceptions were enabled in the compiler settings.
1226
#if !defined(NAPI_CPP_EXCEPTIONS) && !defined(NAPI_DISABLE_CPP_EXCEPTIONS)
@@ -60,7 +74,7 @@ namespace Napi {
6074
typedef TypedArrayOf<double> Float64Array; ///< Typed-array of 64-bit floating-point values
6175

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

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

673687
template <typename T>
674-
static constexpr napi_typedarray_type TypedArrayTypeForPrimitiveType() {
688+
static
689+
#if defined(NAPI_HAS_CONSTEXPR)
690+
constexpr
691+
#endif
692+
napi_typedarray_type TypedArrayTypeForPrimitiveType() {
675693
return std::is_same<T, int8_t>::value ? napi_int8_array
676694
: std::is_same<T, uint8_t>::value ? napi_uint8_array
677695
: std::is_same<T, int16_t>::value ? napi_int16_array
@@ -701,7 +719,11 @@ namespace Napi {
701719
static TypedArrayOf New(
702720
napi_env env, ///< N-API environment
703721
size_t elementLength, ///< Length of the created array, as a number of elements
722+
#if defined(NAPI_HAS_CONSTEXPR)
704723
napi_typedarray_type type = TypedArray::TypedArrayTypeForPrimitiveType<T>()
724+
#else
725+
napi_typedarray_type type
726+
#endif
705727
///< Type of array, if different from the default array type for the template parameter T.
706728
);
707729

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

@@ -830,7 +856,6 @@ namespace Napi {
830856
// A reference can be moved but cannot be copied.
831857
Reference(Reference<T>&& other);
832858
Reference<T>& operator =(Reference<T>&& other);
833-
Reference(const Reference<T>&) = delete;
834859
Reference<T>& operator =(Reference<T>&) = delete;
835860

836861
operator napi_ref() const;
@@ -855,6 +880,8 @@ namespace Napi {
855880
void SuppressDestruct();
856881

857882
protected:
883+
Reference(const Reference<T>&);
884+
858885
/// !cond INTERNAL
859886
napi_env _env;
860887
napi_ref _ref;
@@ -874,7 +901,6 @@ namespace Napi {
874901
ObjectReference& operator =(Reference<Object>&& other);
875902
ObjectReference(ObjectReference&& other);
876903
ObjectReference& operator =(ObjectReference&& other);
877-
ObjectReference(const ObjectReference&) = delete;
878904
ObjectReference& operator =(ObjectReference&) = delete;
879905

880906
Napi::Value Get(const char* utf8name) const;
@@ -897,6 +923,9 @@ namespace Napi {
897923
void Set(uint32_t index, const std::string& utf8value);
898924
void Set(uint32_t index, bool boolValue);
899925
void Set(uint32_t index, double numberValue);
926+
927+
protected:
928+
ObjectReference(const ObjectReference&);
900929
};
901930

902931
class FunctionReference: public Reference<Function> {

test/error.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ void DoNotCatch(const CallbackInfo& info) {
1111

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

1717
#ifdef NAPI_CPP_EXCEPTIONS

test/function.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Value CallWithVector(const CallbackInfo& info) {
6262
Value CallWithReceiverAndArgs(const CallbackInfo& info) {
6363
Function func = info[0].As<Function>();
6464
Value receiver = info[1];
65-
return func.Call(receiver, { info[2], info[3], info[4] });
65+
return func.Call(receiver, std::initializer_list<napi_value>{ info[2], info[3], info[4] });
6666
}
6767

6868
Value CallWithReceiverAndVector(const CallbackInfo& info) {
@@ -78,12 +78,12 @@ Value CallWithReceiverAndVector(const CallbackInfo& info) {
7878

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

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

8989
Value CallConstructorWithVector(const CallbackInfo& info) {

test/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,15 @@ let testModules = [
1313
];
1414

1515
if (typeof global.gc === 'function') {
16+
console.log('Starting test suite\n');
17+
1618
// Requiring each module runs tests in the module.
1719
testModules.forEach(name => {
20+
console.log(`Running test '${name}'`);
1821
require('./' + name);
1922
});
23+
24+
console.log('\nAll tests passed!');
2025
} else {
2126
// Make it easier to run with the correct (version-dependent) command-line args.
2227
const args = [ '--expose-gc', __filename ];

test/name.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using namespace Napi;
44

55
const char* testValueUtf8 = "123456789";
6-
const char16_t* testValueUtf16 = u"123456789";
6+
const char16_t* testValueUtf16 = NAPI_WIDE_TEXT("123456789");
77

88
Value EchoString(const CallbackInfo& info) {
99
String value = info[0].As<String>();

test/object.cc

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,26 @@ void DefineProperties(const CallbackInfo& info) {
3939
PropertyDescriptor::Function("function", TestFunction),
4040
});
4141
} else if (nameType.Utf8Value() == "string") {
42+
// VS2013 has lifetime issues when passing temporary objects into the constructor of another
43+
// object. It generates code to destruct the object as soon as the constructor call returns.
44+
// Since this isn't a common case for using std::string objects, I'm refactoring the test to
45+
// work around the issue.
46+
std::string str1("readonlyAccessor");
47+
std::string str2("readwriteAccessor");
48+
std::string str3("readonlyValue");
49+
std::string str4("readwriteValue");
50+
std::string str5("enumerableValue");
51+
std::string str6("configurableValue");
52+
std::string str7("function");
53+
4254
obj.DefineProperties({
43-
PropertyDescriptor::Accessor(std::string("readonlyAccessor"), TestGetter),
44-
PropertyDescriptor::Accessor(std::string("readwriteAccessor"), TestGetter, TestSetter),
45-
PropertyDescriptor::Value(std::string("readonlyValue"), trueValue),
46-
PropertyDescriptor::Value(std::string("readwriteValue"), trueValue, napi_writable),
47-
PropertyDescriptor::Value(std::string("enumerableValue"), trueValue, napi_enumerable),
48-
PropertyDescriptor::Value(std::string("configurableValue"), trueValue, napi_configurable),
49-
PropertyDescriptor::Function(std::string("function"), TestFunction),
55+
PropertyDescriptor::Accessor(str1, TestGetter),
56+
PropertyDescriptor::Accessor(str2, TestGetter, TestSetter),
57+
PropertyDescriptor::Value(str3, trueValue),
58+
PropertyDescriptor::Value(str4, trueValue, napi_writable),
59+
PropertyDescriptor::Value(str5, trueValue, napi_enumerable),
60+
PropertyDescriptor::Value(str6, trueValue, napi_configurable),
61+
PropertyDescriptor::Function(str7, TestFunction),
5062
});
5163
} else if (nameType.Utf8Value() == "value") {
5264
obj.DefineProperties({

test/typedarray.cc

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22

33
using namespace Napi;
44

5+
#if defined(NAPI_HAS_CONSTEXPR)
6+
#define NAPI_TYPEDARRAY_NEW(className, env, length, type) className::New(env, length)
7+
#define NAPI_TYPEDARRAY_NEW_BUFFER(className, env, length, buffer, bufferOffset, type) \
8+
className::New(env, length, buffer, bufferOffset)
9+
#else
10+
#define NAPI_TYPEDARRAY_NEW(className, env, length, type) className::New(env, length, type)
11+
#define NAPI_TYPEDARRAY_NEW_BUFFER(className, env, length, buffer, bufferOffset, type) \
12+
className::New(env, length, buffer, bufferOffset, type)
13+
#endif
14+
515
namespace {
616

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

1323
if (arrayType == "int8") {
14-
return buffer.IsUndefined() ? Int8Array::New(info.Env(), length)
15-
: Int8Array::New(info.Env(), length, buffer, bufferOffset);
24+
return buffer.IsUndefined() ?
25+
NAPI_TYPEDARRAY_NEW(Int8Array, info.Env(), length, napi_int8_array) :
26+
NAPI_TYPEDARRAY_NEW_BUFFER(Int8Array, info.Env(), length, buffer, bufferOffset,
27+
napi_int8_array);
1628
} else if (arrayType == "uint8") {
17-
return buffer.IsUndefined() ? Uint8Array::New(info.Env(), length)
18-
: Uint8Array::New(info.Env(), length, buffer, bufferOffset);
29+
return buffer.IsUndefined() ?
30+
NAPI_TYPEDARRAY_NEW(Uint8Array, info.Env(), length, napi_uint8_array) :
31+
NAPI_TYPEDARRAY_NEW_BUFFER(Uint8Array, info.Env(), length, buffer, bufferOffset,
32+
napi_uint8_array);
1933
} else if (arrayType == "uint8_clamped") {
20-
return buffer.IsUndefined() ? Uint8Array::New(info.Env(), length, napi_uint8_clamped_array)
21-
: Uint8Array::New(info.Env(), length, buffer, bufferOffset, napi_uint8_clamped_array);
34+
return buffer.IsUndefined() ?
35+
Uint8Array::New(info.Env(), length, napi_uint8_clamped_array) :
36+
Uint8Array::New(info.Env(), length, buffer, bufferOffset, napi_uint8_clamped_array);
2237
} else if (arrayType == "int16") {
23-
return buffer.IsUndefined() ? Int16Array::New(info.Env(), length)
24-
: Int16Array::New(info.Env(), length, buffer, bufferOffset);
38+
return buffer.IsUndefined() ?
39+
NAPI_TYPEDARRAY_NEW(Int16Array, info.Env(), length, napi_int16_array) :
40+
NAPI_TYPEDARRAY_NEW_BUFFER(Int16Array, info.Env(), length, buffer, bufferOffset,
41+
napi_int16_array);
2542
} else if (arrayType == "uint16") {
26-
return buffer.IsUndefined() ? Uint16Array::New(info.Env(), length)
27-
: Uint16Array::New(info.Env(), length, buffer, bufferOffset);
43+
return buffer.IsUndefined() ?
44+
NAPI_TYPEDARRAY_NEW(Uint16Array, info.Env(), length, napi_uint16_array) :
45+
NAPI_TYPEDARRAY_NEW_BUFFER(Uint16Array, info.Env(), length, buffer, bufferOffset,
46+
napi_uint16_array);
2847
} else if (arrayType == "int32") {
29-
return buffer.IsUndefined() ? Int32Array::New(info.Env(), length)
30-
: Int32Array::New(info.Env(), length, buffer, bufferOffset);
48+
return buffer.IsUndefined() ?
49+
NAPI_TYPEDARRAY_NEW(Int32Array, info.Env(), length, napi_int32_array) :
50+
NAPI_TYPEDARRAY_NEW_BUFFER(Int32Array, info.Env(), length, buffer, bufferOffset,
51+
napi_int32_array);
3152
} else if (arrayType == "uint32") {
32-
return buffer.IsUndefined() ? Uint32Array::New(info.Env(), length)
33-
: Uint32Array::New(info.Env(), length, buffer, bufferOffset);
53+
return buffer.IsUndefined() ?
54+
NAPI_TYPEDARRAY_NEW(Uint32Array, info.Env(), length, napi_uint32_array) :
55+
NAPI_TYPEDARRAY_NEW_BUFFER(Uint32Array, info.Env(), length, buffer, bufferOffset,
56+
napi_uint32_array);
3457
} else if (arrayType == "float32") {
35-
return buffer.IsUndefined() ? Float32Array::New(info.Env(), length)
36-
: Float32Array::New(info.Env(), length, buffer, bufferOffset);
58+
return buffer.IsUndefined() ?
59+
NAPI_TYPEDARRAY_NEW(Float32Array, info.Env(), length, napi_float32_array) :
60+
NAPI_TYPEDARRAY_NEW_BUFFER(Float32Array, info.Env(), length, buffer, bufferOffset,
61+
napi_float32_array);
3762
} else if (arrayType == "float64") {
38-
return buffer.IsUndefined() ? Float64Array::New(info.Env(), length)
39-
: Float64Array::New(info.Env(), length, buffer, bufferOffset);
63+
return buffer.IsUndefined() ?
64+
NAPI_TYPEDARRAY_NEW(Float64Array, info.Env(), length, napi_float64_array) :
65+
NAPI_TYPEDARRAY_NEW_BUFFER(Float64Array, info.Env(), length, buffer, bufferOffset,
66+
napi_float64_array);
4067
} else {
4168
Error::New(info.Env(), "Invalid typed-array type.").ThrowAsJavaScriptException();
4269
return Value();
4370
}
4471
}
4572

4673
Value CreateInvalidTypedArray(const CallbackInfo& info) {
47-
return Int8Array::New(info.Env(), 1, ArrayBuffer(), 0);
74+
return NAPI_TYPEDARRAY_NEW_BUFFER(Int8Array, info.Env(), 1, ArrayBuffer(), 0, napi_int8_array);
4875
}
4976

5077
Value GetTypedArrayType(const CallbackInfo& info) {

0 commit comments

Comments
 (0)