Skip to content

Commit d8523a7

Browse files
authored
src: enable type cast checks in tests (#1581)
1 parent 48a43de commit d8523a7

22 files changed

+146
-104
lines changed

doc/value.md

+13
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,19 @@ In order to enforce expected type, use `Napi::Value::Is*()` methods to check
8686
the type before calling `Napi::Value::As()`, or compile with definition
8787
`NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` to enforce type checks.
8888

89+
### UnsafeAs
90+
91+
```cpp
92+
template <typename T> T Napi::Value::UnsafeAs() const;
93+
```
94+
95+
Casts to another type of `Napi::Value`, when the actual type is known or
96+
assumed.
97+
98+
This conversion does not coerce the type. This does not check the type even if
99+
`NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` is defined. This indicates intentional
100+
unsafe type cast. Use `Napi::Value::As()` if possible.
101+
89102
### Env
90103

91104
```cpp

napi-inl.h

+19-3
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,16 @@ inline T Value::As() const {
896896
return T(_env, _value);
897897
}
898898

899+
template <typename T>
900+
inline T Value::UnsafeAs() const {
901+
return T(_env, _value);
902+
}
903+
904+
// static
905+
inline void Value::CheckCast(napi_env /* env */, napi_value value) {
906+
NAPI_CHECK(value != nullptr, "Value::CheckCast", "empty value");
907+
}
908+
899909
inline MaybeOrValue<Boolean> Value::ToBoolean() const {
900910
napi_value result;
901911
napi_status status = napi_coerce_to_bool(_env, _value, &result);
@@ -1303,12 +1313,15 @@ inline Symbol Symbol::New(napi_env env, napi_value description) {
13031313

13041314
inline MaybeOrValue<Symbol> Symbol::WellKnown(napi_env env,
13051315
const std::string& name) {
1316+
// No need to check if the return value is a symbol or undefined.
1317+
// Well known symbols are definite and it is an develop time error
1318+
// if the symbol does not exist.
13061319
#if defined(NODE_ADDON_API_ENABLE_MAYBE)
13071320
Value symbol_obj;
13081321
Value symbol_value;
13091322
if (Napi::Env(env).Global().Get("Symbol").UnwrapTo(&symbol_obj) &&
13101323
symbol_obj.As<Object>().Get(name).UnwrapTo(&symbol_value)) {
1311-
return Just<Symbol>(symbol_value.As<Symbol>());
1324+
return Just<Symbol>(symbol_value.UnsafeAs<Symbol>());
13121325
}
13131326
return Nothing<Symbol>();
13141327
#else
@@ -1317,7 +1330,7 @@ inline MaybeOrValue<Symbol> Symbol::WellKnown(napi_env env,
13171330
.Get("Symbol")
13181331
.As<Object>()
13191332
.Get(name)
1320-
.As<Symbol>();
1333+
.UnsafeAs<Symbol>();
13211334
#endif
13221335
}
13231336

@@ -1535,7 +1548,10 @@ inline void Object::CheckCast(napi_env env, napi_value value) {
15351548
napi_valuetype type;
15361549
napi_status status = napi_typeof(env, value, &type);
15371550
NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed");
1538-
NAPI_INTERNAL_CHECK_EQ(type, napi_object, "%d", "Object::CheckCast");
1551+
NAPI_INTERNAL_CHECK(type == napi_object || type == napi_function,
1552+
"Object::CheckCast",
1553+
"Expect napi_object or napi_function, but got %d.",
1554+
type);
15391555
}
15401556

15411557
inline Object::Object() : TypeTaggable() {}

napi.h

+6
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ class Value {
461461
template <typename T>
462462
static Value From(napi_env env, const T& value);
463463

464+
static void CheckCast(napi_env env, napi_value value);
465+
464466
/// Converts to a Node-API value primitive.
465467
///
466468
/// If the instance is _empty_, this returns `nullptr`.
@@ -527,6 +529,10 @@ class Value {
527529
template <typename T>
528530
T As() const;
529531

532+
// Unsafe Value::As(), should be avoided.
533+
template <typename T>
534+
T UnsafeAs() const;
535+
530536
MaybeOrValue<Boolean> ToBoolean()
531537
const; ///< Coerces a value to a JavaScript boolean.
532538
MaybeOrValue<Number> ToNumber()

test/async_worker.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ class TestWorkerWithUserDefRecv : public AsyncWorker {
2020
static void DoWorkWithAsyncRes(const CallbackInfo& info) {
2121
Object recv = info[0].As<Object>();
2222
Function cb = info[1].As<Function>();
23-
Object resource = info[2].As<Object>();
23+
Value resource = info[2];
2424

2525
TestWorkerWithUserDefRecv* worker = nullptr;
2626
if (resource == info.Env().Null()) {
2727
worker = new TestWorkerWithUserDefRecv(recv, cb, "TestResource");
2828
} else {
29-
worker =
30-
new TestWorkerWithUserDefRecv(recv, cb, "TestResource", resource);
29+
worker = new TestWorkerWithUserDefRecv(
30+
recv, cb, "TestResource", resource.As<Object>());
3131
}
3232

3333
worker->Queue();

test/binding.gyp

+2-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@
106106
{
107107
'target_name': 'binding',
108108
'dependencies': ['../node_addon_api.gyp:node_addon_api_except'],
109-
'sources': ['>@(build_sources)']
109+
'sources': ['>@(build_sources)'],
110+
'defines': ['NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS']
110111
},
111112
{
112113
'target_name': 'binding_noexcept',

test/function.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function test (binding) {
8989
assert.deepStrictEqual(args, [7, 8, 9]);
9090

9191
assert.throws(() => {
92-
binding.callWithInvalidReceiver();
92+
binding.callWithInvalidReceiver(() => {});
9393
}, /Invalid (pointer passed as )?argument/);
9494

9595
obj = binding.callConstructorWithArgs(testConstructor, 5, 6, 7);

test/globalObject/global_object_delete_property.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@ using namespace Napi;
55

66
Value DeletePropertyWithCStyleStringAsKey(const CallbackInfo& info) {
77
Object globalObject = info.Env().Global();
8-
String key = info[0].As<String>();
8+
String key = info[0].UnsafeAs<String>();
99
return Boolean::New(
1010
info.Env(), MaybeUnwrap(globalObject.Delete(key.Utf8Value().c_str())));
1111
}
1212

1313
Value DeletePropertyWithCppStyleStringAsKey(const CallbackInfo& info) {
1414
Object globalObject = info.Env().Global();
15-
String key = info[0].As<String>();
15+
String key = info[0].UnsafeAs<String>();
1616
return Boolean::New(info.Env(),
1717
MaybeUnwrap(globalObject.Delete(key.Utf8Value())));
1818
}
1919

2020
Value DeletePropertyWithInt32AsKey(const CallbackInfo& info) {
2121
Object globalObject = info.Env().Global();
22-
Number key = info[0].As<Number>();
22+
Number key = info[0].UnsafeAs<Number>();
2323
return Boolean::New(info.Env(),
2424
MaybeUnwrap(globalObject.Delete(key.Uint32Value())));
2525
}
2626

2727
Value DeletePropertyWithNapiValueAsKey(const CallbackInfo& info) {
2828
Object globalObject = info.Env().Global();
29-
Name key = info[0].As<Name>();
29+
Name key = info[0].UnsafeAs<Name>();
3030
return Boolean::New(info.Env(), MaybeUnwrap(globalObject.Delete(key)));
3131
}

test/globalObject/global_object_get_property.cc

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@ using namespace Napi;
55

66
Value GetPropertyWithNapiValueAsKey(const CallbackInfo& info) {
77
Object globalObject = info.Env().Global();
8-
Name key = info[0].As<Name>();
8+
Name key = info[0].UnsafeAs<Name>();
99
return MaybeUnwrap(globalObject.Get(key));
1010
}
1111

1212
Value GetPropertyWithInt32AsKey(const CallbackInfo& info) {
1313
Object globalObject = info.Env().Global();
14-
Number key = info[0].As<Napi::Number>();
14+
Number key = info[0].UnsafeAs<Napi::Number>();
1515
return MaybeUnwrapOr(globalObject.Get(key.Uint32Value()), Value());
1616
}
1717

1818
Value GetPropertyWithCStyleStringAsKey(const CallbackInfo& info) {
1919
Object globalObject = info.Env().Global();
20-
String cStrkey = info[0].As<String>();
20+
String cStrkey = info[0].UnsafeAs<String>();
2121
return MaybeUnwrapOr(globalObject.Get(cStrkey.Utf8Value().c_str()), Value());
2222
}
2323

2424
Value GetPropertyWithCppStyleStringAsKey(const CallbackInfo& info) {
2525
Object globalObject = info.Env().Global();
26-
String cppStrKey = info[0].As<String>();
26+
String cppStrKey = info[0].UnsafeAs<String>();
2727
return MaybeUnwrapOr(globalObject.Get(cppStrKey.Utf8Value()), Value());
2828
}
2929

test/globalObject/global_object_has_own_property.cc

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ using namespace Napi;
55

66
Value HasPropertyWithCStyleStringAsKey(const CallbackInfo& info) {
77
Object globalObject = info.Env().Global();
8-
String key = info[0].As<String>();
8+
String key = info[0].UnsafeAs<String>();
99
return Boolean::New(
1010
info.Env(),
1111
MaybeUnwrapOr(globalObject.HasOwnProperty(key.Utf8Value().c_str()),
@@ -14,15 +14,15 @@ Value HasPropertyWithCStyleStringAsKey(const CallbackInfo& info) {
1414

1515
Value HasPropertyWithCppStyleStringAsKey(const CallbackInfo& info) {
1616
Object globalObject = info.Env().Global();
17-
String key = info[0].As<String>();
17+
String key = info[0].UnsafeAs<String>();
1818
return Boolean::New(
1919
info.Env(),
2020
MaybeUnwrapOr(globalObject.HasOwnProperty(key.Utf8Value()), false));
2121
}
2222

2323
Value HasPropertyWithNapiValueAsKey(const CallbackInfo& info) {
2424
Object globalObject = info.Env().Global();
25-
Name key = info[0].As<Name>();
25+
Name key = info[0].UnsafeAs<Name>();
2626
return Boolean::New(info.Env(),
2727
MaybeUnwrap(globalObject.HasOwnProperty(key)));
2828
}

test/globalObject/global_object_set_property.cc

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ using namespace Napi;
44

55
void SetPropertyWithCStyleStringAsKey(const CallbackInfo& info) {
66
Object globalObject = info.Env().Global();
7-
String key = info[0].As<String>();
7+
String key = info[0].UnsafeAs<String>();
88
Value value = info[1];
99
globalObject.Set(key.Utf8Value().c_str(), value);
1010
}
1111

1212
void SetPropertyWithCppStyleStringAsKey(const CallbackInfo& info) {
1313
Object globalObject = info.Env().Global();
14-
String key = info[0].As<String>();
14+
String key = info[0].UnsafeAs<String>();
1515
Value value = info[1];
1616
globalObject.Set(key.Utf8Value(), value);
1717
}
1818

1919
void SetPropertyWithInt32AsKey(const CallbackInfo& info) {
2020
Object globalObject = info.Env().Global();
21-
Number key = info[0].As<Number>();
21+
Number key = info[0].UnsafeAs<Number>();
2222
Value value = info[1];
2323
globalObject.Set(key.Uint32Value(), value);
2424
}
2525

2626
void SetPropertyWithNapiValueAsKey(const CallbackInfo& info) {
2727
Object globalObject = info.Env().Global();
28-
Name key = info[0].As<Name>();
28+
Name key = info[0].UnsafeAs<Name>();
2929
Value value = info[1];
3030
globalObject.Set(key, value);
31-
}
31+
}

test/name.cc

+10-8
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,21 @@ Value EchoString(const CallbackInfo& info) {
2121

2222
Value CreateString(const CallbackInfo& info) {
2323
String encoding = info[0].As<String>();
24-
Number length = info[1].As<Number>();
24+
Value length = info[1];
2525

2626
if (encoding.Utf8Value() == "utf8") {
2727
if (length.IsUndefined()) {
2828
return String::New(info.Env(), testValueUtf8);
2929
} else {
30-
return String::New(info.Env(), testValueUtf8, length.Uint32Value());
30+
return String::New(
31+
info.Env(), testValueUtf8, length.As<Number>().Uint32Value());
3132
}
3233
} else if (encoding.Utf8Value() == "utf16") {
3334
if (length.IsUndefined()) {
3435
return String::New(info.Env(), testValueUtf16);
3536
} else {
36-
return String::New(info.Env(), testValueUtf16, length.Uint32Value());
37+
return String::New(
38+
info.Env(), testValueUtf16, length.As<Number>().Uint32Value());
3739
}
3840
} else {
3941
Error::New(info.Env(), "Invalid encoding.").ThrowAsJavaScriptException();
@@ -44,20 +46,20 @@ Value CreateString(const CallbackInfo& info) {
4446
Value CheckString(const CallbackInfo& info) {
4547
String value = info[0].As<String>();
4648
String encoding = info[1].As<String>();
47-
Number length = info[2].As<Number>();
49+
Value length = info[2];
4850

4951
if (encoding.Utf8Value() == "utf8") {
5052
std::string testValue = testValueUtf8;
5153
if (!length.IsUndefined()) {
52-
testValue = testValue.substr(0, length.Uint32Value());
54+
testValue = testValue.substr(0, length.As<Number>().Uint32Value());
5355
}
5456

5557
std::string stringValue = value;
5658
return Boolean::New(info.Env(), stringValue == testValue);
5759
} else if (encoding.Utf8Value() == "utf16") {
5860
std::u16string testValue = testValueUtf16;
5961
if (!length.IsUndefined()) {
60-
testValue = testValue.substr(0, length.Uint32Value());
62+
testValue = testValue.substr(0, length.As<Number>().Uint32Value());
6163
}
6264

6365
std::u16string stringValue = value;
@@ -69,10 +71,10 @@ Value CheckString(const CallbackInfo& info) {
6971
}
7072

7173
Value CreateSymbol(const CallbackInfo& info) {
72-
String description = info[0].As<String>();
74+
Value description = info[0];
7375

7476
if (!description.IsUndefined()) {
75-
return Symbol::New(info.Env(), description);
77+
return Symbol::New(info.Env(), description.As<String>());
7678
} else {
7779
return Symbol::New(info.Env());
7880
}

test/object/delete_property.cc

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,34 @@
44
using namespace Napi;
55

66
Value DeletePropertyWithUint32(const CallbackInfo& info) {
7-
Object obj = info[0].As<Object>();
7+
Object obj = info[0].UnsafeAs<Object>();
88
Number key = info[1].As<Number>();
99
return Boolean::New(info.Env(), MaybeUnwrap(obj.Delete(key.Uint32Value())));
1010
}
1111

1212
Value DeletePropertyWithNapiValue(const CallbackInfo& info) {
13-
Object obj = info[0].As<Object>();
13+
Object obj = info[0].UnsafeAs<Object>();
1414
Name key = info[1].As<Name>();
1515
return Boolean::New(
1616
info.Env(),
1717
MaybeUnwrapOr(obj.Delete(static_cast<napi_value>(key)), false));
1818
}
1919

2020
Value DeletePropertyWithNapiWrapperValue(const CallbackInfo& info) {
21-
Object obj = info[0].As<Object>();
21+
Object obj = info[0].UnsafeAs<Object>();
2222
Name key = info[1].As<Name>();
2323
return Boolean::New(info.Env(), MaybeUnwrapOr(obj.Delete(key), false));
2424
}
2525

2626
Value DeletePropertyWithCStyleString(const CallbackInfo& info) {
27-
Object obj = info[0].As<Object>();
27+
Object obj = info[0].UnsafeAs<Object>();
2828
String jsKey = info[1].As<String>();
2929
return Boolean::New(
3030
info.Env(), MaybeUnwrapOr(obj.Delete(jsKey.Utf8Value().c_str()), false));
3131
}
3232

3333
Value DeletePropertyWithCppStyleString(const CallbackInfo& info) {
34-
Object obj = info[0].As<Object>();
34+
Object obj = info[0].UnsafeAs<Object>();
3535
String jsKey = info[1].As<String>();
3636
return Boolean::New(info.Env(),
3737
MaybeUnwrapOr(obj.Delete(jsKey.Utf8Value()), false));

test/object/get_property.cc

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,31 @@
44
using namespace Napi;
55

66
Value GetPropertyWithNapiValue(const CallbackInfo& info) {
7-
Object obj = info[0].As<Object>();
7+
Object obj = info[0].UnsafeAs<Object>();
88
Name key = info[1].As<Name>();
99
return MaybeUnwrapOr(obj.Get(static_cast<napi_value>(key)), Value());
1010
}
1111

1212
Value GetPropertyWithNapiWrapperValue(const CallbackInfo& info) {
13-
Object obj = info[0].As<Object>();
13+
Object obj = info[0].UnsafeAs<Object>();
1414
Name key = info[1].As<Name>();
1515
return MaybeUnwrapOr(obj.Get(key), Value());
1616
}
1717

1818
Value GetPropertyWithUint32(const CallbackInfo& info) {
19-
Object obj = info[0].As<Object>();
19+
Object obj = info[0].UnsafeAs<Object>();
2020
Number key = info[1].As<Number>();
2121
return MaybeUnwrap(obj.Get(key.Uint32Value()));
2222
}
2323

2424
Value GetPropertyWithCStyleString(const CallbackInfo& info) {
25-
Object obj = info[0].As<Object>();
25+
Object obj = info[0].UnsafeAs<Object>();
2626
String jsKey = info[1].As<String>();
2727
return MaybeUnwrapOr(obj.Get(jsKey.Utf8Value().c_str()), Value());
2828
}
2929

3030
Value GetPropertyWithCppStyleString(const CallbackInfo& info) {
31-
Object obj = info[0].As<Object>();
31+
Object obj = info[0].UnsafeAs<Object>();
3232
String jsKey = info[1].As<String>();
3333
return MaybeUnwrapOr(obj.Get(jsKey.Utf8Value()), Value());
3434
}

0 commit comments

Comments
 (0)