Skip to content

Commit e421bd8

Browse files
committed
src: enforce type checks on Napi::Value::As()
1 parent a8ad7e7 commit e421bd8

File tree

7 files changed

+394
-1
lines changed

7 files changed

+394
-1
lines changed

doc/value.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ Casts to another type of `Napi::Value`, when the actual type is known or
7878
assumed.
7979

8080
This conversion does not coerce the type. Calling any methods inappropriate for
81-
the actual value type will throw `Napi::Error`.
81+
the actual value type will throw `Napi::Error`. When C++ exceptions are
82+
disabled, the thrown error will not be reflected in C++ land.
83+
84+
In order to enforce expected type, use `Napi::Value::Is*()` methods to check
85+
the type before calling `Napi::Value::As()`, or compile with definition
86+
`NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` to enforce type checks.
8287

8388
### Env
8489

napi-inl.h

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,9 @@ inline bool Value::IsExternal() const {
742742

743743
template <typename T>
744744
inline T Value::As() const {
745+
#ifdef NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS
746+
T::CheckCast(_env, _value);
747+
#endif
745748
return T(_env, _value);
746749
}
747750

@@ -784,6 +787,16 @@ inline Boolean Boolean::New(napi_env env, bool val) {
784787
return Boolean(env, value);
785788
}
786789

790+
inline void Boolean::CheckCast(napi_env env, napi_value value) {
791+
NAPI_CHECK(value != nullptr, "Boolean::CheckCast", "empty value");
792+
793+
napi_valuetype type;
794+
napi_status status = napi_typeof(env, value, &type);
795+
NAPI_CHECK(status == napi_ok, "Boolean::CheckCast", "napi_typeof failed");
796+
NAPI_CHECK(
797+
type == napi_boolean, "Boolean::CheckCast", "value is not napi_boolean");
798+
}
799+
787800
inline Boolean::Boolean() : Napi::Value() {}
788801

789802
inline Boolean::Boolean(napi_env env, napi_value value)
@@ -811,6 +824,16 @@ inline Number Number::New(napi_env env, double val) {
811824
return Number(env, value);
812825
}
813826

827+
inline void Number::CheckCast(napi_env env, napi_value value) {
828+
NAPI_CHECK(value != nullptr, "Number::CheckCast", "empty value");
829+
830+
napi_valuetype type;
831+
napi_status status = napi_typeof(env, value, &type);
832+
NAPI_CHECK(status == napi_ok, "Number::CheckCast", "napi_typeof failed");
833+
NAPI_CHECK(
834+
type == napi_number, "Number::CheckCast", "value is not napi_number");
835+
}
836+
814837
inline Number::Number() : Value() {}
815838

816839
inline Number::Number(napi_env env, napi_value value) : Value(env, value) {}
@@ -897,6 +920,16 @@ inline BigInt BigInt::New(napi_env env,
897920
return BigInt(env, value);
898921
}
899922

923+
inline void BigInt::CheckCast(napi_env env, napi_value value) {
924+
NAPI_CHECK(value != nullptr, "BigInt::CheckCast", "empty value");
925+
926+
napi_valuetype type;
927+
napi_status status = napi_typeof(env, value, &type);
928+
NAPI_CHECK(status == napi_ok, "BigInt::CheckCast", "napi_typeof failed");
929+
NAPI_CHECK(
930+
type == napi_bigint, "BigInt::CheckCast", "value is not napi_bigint");
931+
}
932+
900933
inline BigInt::BigInt() : Value() {}
901934

902935
inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {}
@@ -946,6 +979,15 @@ inline Date Date::New(napi_env env, double val) {
946979
return Date(env, value);
947980
}
948981

982+
inline void Date::CheckCast(napi_env env, napi_value value) {
983+
NAPI_CHECK(value != nullptr, "Date::CheckCast", "empty value");
984+
985+
bool result;
986+
napi_status status = napi_is_date(env, value, &result);
987+
NAPI_CHECK(status == napi_ok, "Date::CheckCast", "napi_is_date failed");
988+
NAPI_CHECK(result, "Date::CheckCast", "value is not date");
989+
}
990+
949991
inline Date::Date() : Value() {}
950992

951993
inline Date::Date(napi_env env, napi_value value) : Value(env, value) {}
@@ -965,6 +1007,16 @@ inline double Date::ValueOf() const {
9651007
////////////////////////////////////////////////////////////////////////////////
9661008
// Name class
9671009
////////////////////////////////////////////////////////////////////////////////
1010+
inline void Name::CheckCast(napi_env env, napi_value value) {
1011+
NAPI_CHECK(value != nullptr, "Name::CheckCast", "empty value");
1012+
1013+
napi_valuetype type;
1014+
napi_status status = napi_typeof(env, value, &type);
1015+
NAPI_CHECK(status == napi_ok, "Name::CheckCast", "napi_typeof failed");
1016+
NAPI_CHECK(type == napi_string || type == napi_symbol,
1017+
"Name::CheckCast",
1018+
"value is not napi_string or napi_symbol");
1019+
}
9681020

9691021
inline Name::Name() : Value() {}
9701022

@@ -1024,6 +1076,16 @@ inline String String::New(napi_env env, const char16_t* val, size_t length) {
10241076
return String(env, value);
10251077
}
10261078

1079+
inline void String::CheckCast(napi_env env, napi_value value) {
1080+
NAPI_CHECK(value != nullptr, "String::CheckCast", "empty value");
1081+
1082+
napi_valuetype type;
1083+
napi_status status = napi_typeof(env, value, &type);
1084+
NAPI_CHECK(status == napi_ok, "String::CheckCast", "napi_typeof failed");
1085+
NAPI_CHECK(
1086+
type == napi_string, "String::CheckCast", "value is not napi_string");
1087+
}
1088+
10271089
inline String::String() : Name() {}
10281090

10291091
inline String::String(napi_env env, napi_value value) : Name(env, value) {}
@@ -1151,6 +1213,16 @@ inline MaybeOrValue<Symbol> Symbol::For(napi_env env, napi_value description) {
11511213
#endif
11521214
}
11531215

1216+
inline void Symbol::CheckCast(napi_env env, napi_value value) {
1217+
NAPI_CHECK(value != nullptr, "Symbol::CheckCast", "empty value");
1218+
1219+
napi_valuetype type;
1220+
napi_status status = napi_typeof(env, value, &type);
1221+
NAPI_CHECK(status == napi_ok, "Symbol::CheckCast", "napi_typeof failed");
1222+
NAPI_CHECK(
1223+
type == napi_symbol, "Symbol::CheckCast", "value is not napi_symbol");
1224+
}
1225+
11541226
inline Symbol::Symbol() : Name() {}
11551227

11561228
inline Symbol::Symbol(napi_env env, napi_value value) : Name(env, value) {}
@@ -1287,6 +1359,16 @@ inline Object Object::New(napi_env env) {
12871359
return Object(env, value);
12881360
}
12891361

1362+
inline void Object::CheckCast(napi_env env, napi_value value) {
1363+
NAPI_CHECK(value != nullptr, "Object::CheckCast", "empty value");
1364+
1365+
napi_valuetype type;
1366+
napi_status status = napi_typeof(env, value, &type);
1367+
NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed");
1368+
NAPI_CHECK(
1369+
type == napi_object, "Object::CheckCast", "value is not napi_object");
1370+
}
1371+
12901372
inline Object::Object() : Value() {}
12911373

12921374
inline Object::Object(napi_env env, napi_value value) : Value(env, value) {}
@@ -1705,6 +1787,18 @@ inline External<T> External<T>::New(napi_env env,
17051787
return External(env, value);
17061788
}
17071789

1790+
template <typename T>
1791+
inline void External<T>::CheckCast(napi_env env, napi_value value) {
1792+
NAPI_CHECK(value != nullptr, "External::CheckCast", "empty value");
1793+
1794+
napi_valuetype type;
1795+
napi_status status = napi_typeof(env, value, &type);
1796+
NAPI_CHECK(status == napi_ok, "External::CheckCast", "napi_typeof failed");
1797+
NAPI_CHECK(type == napi_external,
1798+
"External::CheckCast",
1799+
"value is not napi_external");
1800+
}
1801+
17081802
template <typename T>
17091803
inline External<T>::External() : Value() {}
17101804

@@ -1738,6 +1832,15 @@ inline Array Array::New(napi_env env, size_t length) {
17381832
return Array(env, value);
17391833
}
17401834

1835+
inline void Array::CheckCast(napi_env env, napi_value value) {
1836+
NAPI_CHECK(value != nullptr, "Array::CheckCast", "empty value");
1837+
1838+
bool result;
1839+
napi_status status = napi_is_array(env, value, &result);
1840+
NAPI_CHECK(status == napi_ok, "Array::CheckCast", "napi_is_array failed");
1841+
NAPI_CHECK(result, "Array::CheckCast", "value is not array");
1842+
}
1843+
17411844
inline Array::Array() : Object() {}
17421845

17431846
inline Array::Array(napi_env env, napi_value value) : Object(env, value) {}
@@ -1824,6 +1927,17 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
18241927
}
18251928
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
18261929

1930+
inline void ArrayBuffer::CheckCast(napi_env env, napi_value value) {
1931+
NAPI_CHECK(value != nullptr, "ArrayBuffer::CheckCast", "empty value");
1932+
1933+
bool result;
1934+
napi_status status = napi_is_arraybuffer(env, value, &result);
1935+
NAPI_CHECK(status == napi_ok,
1936+
"ArrayBuffer::CheckCast",
1937+
"napi_is_arraybuffer failed");
1938+
NAPI_CHECK(result, "ArrayBuffer::CheckCast", "value is not arraybuffer");
1939+
}
1940+
18271941
inline ArrayBuffer::ArrayBuffer() : Object() {}
18281942

18291943
inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value)
@@ -1891,6 +2005,16 @@ inline DataView DataView::New(napi_env env,
18912005
return DataView(env, value);
18922006
}
18932007

2008+
inline void DataView::CheckCast(napi_env env, napi_value value) {
2009+
NAPI_CHECK(value != nullptr, "DataView::CheckCast", "empty value");
2010+
2011+
bool result;
2012+
napi_status status = napi_is_dataview(env, value, &result);
2013+
NAPI_CHECK(
2014+
status == napi_ok, "DataView::CheckCast", "napi_is_dataview failed");
2015+
NAPI_CHECK(result, "DataView::CheckCast", "value is not dataview");
2016+
}
2017+
18942018
inline DataView::DataView() : Object() {}
18952019

18962020
inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
@@ -2025,6 +2149,15 @@ inline void DataView::WriteData(size_t byteOffset, T value) const {
20252149
////////////////////////////////////////////////////////////////////////////////
20262150
// TypedArray class
20272151
////////////////////////////////////////////////////////////////////////////////
2152+
inline void TypedArray::CheckCast(napi_env env, napi_value value) {
2153+
NAPI_CHECK(value != nullptr, "TypedArray::CheckCast", "empty value");
2154+
2155+
bool result;
2156+
napi_status status = napi_is_typedarray(env, value, &result);
2157+
NAPI_CHECK(
2158+
status == napi_ok, "TypedArray::CheckCast", "napi_is_typedarray failed");
2159+
NAPI_CHECK(result, "TypedArray::CheckCast", "value is not typedarray");
2160+
}
20282161

20292162
inline TypedArray::TypedArray()
20302163
: Object(), _type(napi_typedarray_type::napi_int8_array), _length(0) {}
@@ -2107,6 +2240,23 @@ inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const {
21072240
////////////////////////////////////////////////////////////////////////////////
21082241
// TypedArrayOf<T> class
21092242
////////////////////////////////////////////////////////////////////////////////
2243+
template <typename T>
2244+
inline void TypedArrayOf<T>::CheckCast(napi_env env, napi_value value) {
2245+
TypedArray::CheckCast(env, value);
2246+
napi_typedarray_type type;
2247+
napi_status status = napi_get_typedarray_info(
2248+
env, value, &type, nullptr, nullptr, nullptr, nullptr);
2249+
NAPI_CHECK(status == napi_ok,
2250+
"TypedArrayOf::CheckCast",
2251+
"napi_is_typedarray failed");
2252+
2253+
NAPI_CHECK(
2254+
(type == TypedArrayTypeForPrimitiveType<T>() ||
2255+
(type == napi_uint8_clamped_array && std::is_same<T, uint8_t>::value)),
2256+
"TypedArrayOf::CheckCast",
2257+
"Array type must match the template parameter. (Uint8 arrays may "
2258+
"optionally have the \"clamped\" array type.)");
2259+
}
21102260

21112261
template <typename T>
21122262
inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
@@ -2280,6 +2430,17 @@ inline Function Function::New(napi_env env,
22802430
return New(env, cb, utf8name.c_str(), data);
22812431
}
22822432

2433+
inline void Function::CheckCast(napi_env env, napi_value value) {
2434+
NAPI_CHECK(value != nullptr, "Function::CheckCast", "empty value");
2435+
2436+
napi_valuetype type;
2437+
napi_status status = napi_typeof(env, value, &type);
2438+
NAPI_CHECK(status == napi_ok, "Function::CheckCast", "napi_typeof failed");
2439+
NAPI_CHECK(type == napi_function,
2440+
"Function::CheckCast",
2441+
"value is not napi_function");
2442+
}
2443+
22832444
inline Function::Function() : Object() {}
22842445

22852446
inline Function::Function(napi_env env, napi_value value)
@@ -2426,6 +2587,15 @@ inline void Promise::Deferred::Reject(napi_value value) const {
24262587
NAPI_THROW_IF_FAILED_VOID(_env, status);
24272588
}
24282589

2590+
inline void Promise::CheckCast(napi_env env, napi_value value) {
2591+
NAPI_CHECK(value != nullptr, "Promise::CheckCast", "empty value");
2592+
2593+
bool result;
2594+
napi_status status = napi_is_promise(env, value, &result);
2595+
NAPI_CHECK(status == napi_ok, "Promise::CheckCast", "napi_is_promise failed");
2596+
NAPI_CHECK(result, "Promise::CheckCast", "value is not promise");
2597+
}
2598+
24292599
inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) {}
24302600

24312601
////////////////////////////////////////////////////////////////////////////////
@@ -2598,6 +2768,16 @@ inline Buffer<T> Buffer<T>::Copy(napi_env env, const T* data, size_t length) {
25982768
return Buffer<T>(env, value);
25992769
}
26002770

2771+
template <typename T>
2772+
inline void Buffer<T>::CheckCast(napi_env env, napi_value value) {
2773+
NAPI_CHECK(value != nullptr, "Buffer::CheckCast", "empty value");
2774+
2775+
bool result;
2776+
napi_status status = napi_is_buffer(env, value, &result);
2777+
NAPI_CHECK(status == napi_ok, "Buffer::CheckCast", "napi_is_buffer failed");
2778+
NAPI_CHECK(result, "Buffer::CheckCast", "value is not buffer");
2779+
}
2780+
26012781
template <typename T>
26022782
inline Buffer<T>::Buffer() : Uint8Array(), _length(0), _data(nullptr) {}
26032783

0 commit comments

Comments
 (0)