Skip to content

Commit 5adb896

Browse files
authored
src: enforce type checks on Napi::Value::As() (#1281)
1 parent d9faac7 commit 5adb896

File tree

6 files changed

+399
-1
lines changed

6 files changed

+399
-1
lines changed

doc/value.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,13 @@ 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 before control returns to
83+
JavaScript.
84+
85+
In order to enforce expected type, use `Napi::Value::Is*()` methods to check
86+
the type before calling `Napi::Value::As()`, or compile with definition
87+
`NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` to enforce type checks.
8288

8389
### Env
8490

napi-inl.h

+180
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) {}
@@ -1313,6 +1385,16 @@ inline Object Object::New(napi_env env) {
13131385
return Object(env, value);
13141386
}
13151387

1388+
inline void Object::CheckCast(napi_env env, napi_value value) {
1389+
NAPI_CHECK(value != nullptr, "Object::CheckCast", "empty value");
1390+
1391+
napi_valuetype type;
1392+
napi_status status = napi_typeof(env, value, &type);
1393+
NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed");
1394+
NAPI_CHECK(
1395+
type == napi_object, "Object::CheckCast", "value is not napi_object");
1396+
}
1397+
13161398
inline Object::Object() : TypeTaggable() {}
13171399

13181400
inline Object::Object(napi_env env, napi_value value)
@@ -1719,6 +1801,18 @@ inline External<T> External<T>::New(napi_env env,
17191801
return External(env, value);
17201802
}
17211803

1804+
template <typename T>
1805+
inline void External<T>::CheckCast(napi_env env, napi_value value) {
1806+
NAPI_CHECK(value != nullptr, "External::CheckCast", "empty value");
1807+
1808+
napi_valuetype type;
1809+
napi_status status = napi_typeof(env, value, &type);
1810+
NAPI_CHECK(status == napi_ok, "External::CheckCast", "napi_typeof failed");
1811+
NAPI_CHECK(type == napi_external,
1812+
"External::CheckCast",
1813+
"value is not napi_external");
1814+
}
1815+
17221816
template <typename T>
17231817
inline External<T>::External() : TypeTaggable() {}
17241818

@@ -1752,6 +1846,15 @@ inline Array Array::New(napi_env env, size_t length) {
17521846
return Array(env, value);
17531847
}
17541848

1849+
inline void Array::CheckCast(napi_env env, napi_value value) {
1850+
NAPI_CHECK(value != nullptr, "Array::CheckCast", "empty value");
1851+
1852+
bool result;
1853+
napi_status status = napi_is_array(env, value, &result);
1854+
NAPI_CHECK(status == napi_ok, "Array::CheckCast", "napi_is_array failed");
1855+
NAPI_CHECK(result, "Array::CheckCast", "value is not array");
1856+
}
1857+
17551858
inline Array::Array() : Object() {}
17561859

17571860
inline Array::Array(napi_env env, napi_value value) : Object(env, value) {}
@@ -1838,6 +1941,17 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
18381941
}
18391942
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
18401943

1944+
inline void ArrayBuffer::CheckCast(napi_env env, napi_value value) {
1945+
NAPI_CHECK(value != nullptr, "ArrayBuffer::CheckCast", "empty value");
1946+
1947+
bool result;
1948+
napi_status status = napi_is_arraybuffer(env, value, &result);
1949+
NAPI_CHECK(status == napi_ok,
1950+
"ArrayBuffer::CheckCast",
1951+
"napi_is_arraybuffer failed");
1952+
NAPI_CHECK(result, "ArrayBuffer::CheckCast", "value is not arraybuffer");
1953+
}
1954+
18411955
inline ArrayBuffer::ArrayBuffer() : Object() {}
18421956

18431957
inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value)
@@ -1905,6 +2019,16 @@ inline DataView DataView::New(napi_env env,
19052019
return DataView(env, value);
19062020
}
19072021

2022+
inline void DataView::CheckCast(napi_env env, napi_value value) {
2023+
NAPI_CHECK(value != nullptr, "DataView::CheckCast", "empty value");
2024+
2025+
bool result;
2026+
napi_status status = napi_is_dataview(env, value, &result);
2027+
NAPI_CHECK(
2028+
status == napi_ok, "DataView::CheckCast", "napi_is_dataview failed");
2029+
NAPI_CHECK(result, "DataView::CheckCast", "value is not dataview");
2030+
}
2031+
19082032
inline DataView::DataView() : Object() {}
19092033

19102034
inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
@@ -2039,6 +2163,15 @@ inline void DataView::WriteData(size_t byteOffset, T value) const {
20392163
////////////////////////////////////////////////////////////////////////////////
20402164
// TypedArray class
20412165
////////////////////////////////////////////////////////////////////////////////
2166+
inline void TypedArray::CheckCast(napi_env env, napi_value value) {
2167+
NAPI_CHECK(value != nullptr, "TypedArray::CheckCast", "empty value");
2168+
2169+
bool result;
2170+
napi_status status = napi_is_typedarray(env, value, &result);
2171+
NAPI_CHECK(
2172+
status == napi_ok, "TypedArray::CheckCast", "napi_is_typedarray failed");
2173+
NAPI_CHECK(result, "TypedArray::CheckCast", "value is not typedarray");
2174+
}
20422175

20432176
inline TypedArray::TypedArray()
20442177
: Object(), _type(napi_typedarray_type::napi_int8_array), _length(0) {}
@@ -2121,6 +2254,23 @@ inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const {
21212254
////////////////////////////////////////////////////////////////////////////////
21222255
// TypedArrayOf<T> class
21232256
////////////////////////////////////////////////////////////////////////////////
2257+
template <typename T>
2258+
inline void TypedArrayOf<T>::CheckCast(napi_env env, napi_value value) {
2259+
TypedArray::CheckCast(env, value);
2260+
napi_typedarray_type type;
2261+
napi_status status = napi_get_typedarray_info(
2262+
env, value, &type, nullptr, nullptr, nullptr, nullptr);
2263+
NAPI_CHECK(status == napi_ok,
2264+
"TypedArrayOf::CheckCast",
2265+
"napi_is_typedarray failed");
2266+
2267+
NAPI_CHECK(
2268+
(type == TypedArrayTypeForPrimitiveType<T>() ||
2269+
(type == napi_uint8_clamped_array && std::is_same<T, uint8_t>::value)),
2270+
"TypedArrayOf::CheckCast",
2271+
"Array type must match the template parameter. (Uint8 arrays may "
2272+
"optionally have the \"clamped\" array type.)");
2273+
}
21242274

21252275
template <typename T>
21262276
inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
@@ -2294,6 +2444,17 @@ inline Function Function::New(napi_env env,
22942444
return New(env, cb, utf8name.c_str(), data);
22952445
}
22962446

2447+
inline void Function::CheckCast(napi_env env, napi_value value) {
2448+
NAPI_CHECK(value != nullptr, "Function::CheckCast", "empty value");
2449+
2450+
napi_valuetype type;
2451+
napi_status status = napi_typeof(env, value, &type);
2452+
NAPI_CHECK(status == napi_ok, "Function::CheckCast", "napi_typeof failed");
2453+
NAPI_CHECK(type == napi_function,
2454+
"Function::CheckCast",
2455+
"value is not napi_function");
2456+
}
2457+
22972458
inline Function::Function() : Object() {}
22982459

22992460
inline Function::Function(napi_env env, napi_value value)
@@ -2440,6 +2601,15 @@ inline void Promise::Deferred::Reject(napi_value value) const {
24402601
NAPI_THROW_IF_FAILED_VOID(_env, status);
24412602
}
24422603

2604+
inline void Promise::CheckCast(napi_env env, napi_value value) {
2605+
NAPI_CHECK(value != nullptr, "Promise::CheckCast", "empty value");
2606+
2607+
bool result;
2608+
napi_status status = napi_is_promise(env, value, &result);
2609+
NAPI_CHECK(status == napi_ok, "Promise::CheckCast", "napi_is_promise failed");
2610+
NAPI_CHECK(result, "Promise::CheckCast", "value is not promise");
2611+
}
2612+
24432613
inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) {}
24442614

24452615
////////////////////////////////////////////////////////////////////////////////
@@ -2612,6 +2782,16 @@ inline Buffer<T> Buffer<T>::Copy(napi_env env, const T* data, size_t length) {
26122782
return Buffer<T>(env, value);
26132783
}
26142784

2785+
template <typename T>
2786+
inline void Buffer<T>::CheckCast(napi_env env, napi_value value) {
2787+
NAPI_CHECK(value != nullptr, "Buffer::CheckCast", "empty value");
2788+
2789+
bool result;
2790+
napi_status status = napi_is_buffer(env, value, &result);
2791+
NAPI_CHECK(status == napi_ok, "Buffer::CheckCast", "napi_is_buffer failed");
2792+
NAPI_CHECK(result, "Buffer::CheckCast", "value is not buffer");
2793+
}
2794+
26152795
template <typename T>
26162796
inline Buffer<T>::Buffer() : Uint8Array(), _length(0), _data(nullptr) {}
26172797

0 commit comments

Comments
 (0)