Skip to content

Commit

Permalink
src: enforce type checks on Napi::Value::As() (#1281)
Browse files Browse the repository at this point in the history
  • Loading branch information
legendecas authored Apr 6, 2023
1 parent d9faac7 commit 5adb896
Show file tree
Hide file tree
Showing 6 changed files with 399 additions and 1 deletion.
8 changes: 7 additions & 1 deletion doc/value.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ Casts to another type of `Napi::Value`, when the actual type is known or
assumed.

This conversion does not coerce the type. Calling any methods inappropriate for
the actual value type will throw `Napi::Error`.
the actual value type will throw `Napi::Error`. When C++ exceptions are
disabled, the thrown error will not be reflected before control returns to
JavaScript.

In order to enforce expected type, use `Napi::Value::Is*()` methods to check
the type before calling `Napi::Value::As()`, or compile with definition
`NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS` to enforce type checks.

### Env

Expand Down
180 changes: 180 additions & 0 deletions napi-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,9 @@ inline bool Value::IsExternal() const {

template <typename T>
inline T Value::As() const {
#ifdef NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS
T::CheckCast(_env, _value);
#endif
return T(_env, _value);
}

Expand Down Expand Up @@ -784,6 +787,16 @@ inline Boolean Boolean::New(napi_env env, bool val) {
return Boolean(env, value);
}

inline void Boolean::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Boolean::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "Boolean::CheckCast", "napi_typeof failed");
NAPI_CHECK(
type == napi_boolean, "Boolean::CheckCast", "value is not napi_boolean");
}

inline Boolean::Boolean() : Napi::Value() {}

inline Boolean::Boolean(napi_env env, napi_value value)
Expand Down Expand Up @@ -811,6 +824,16 @@ inline Number Number::New(napi_env env, double val) {
return Number(env, value);
}

inline void Number::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Number::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "Number::CheckCast", "napi_typeof failed");
NAPI_CHECK(
type == napi_number, "Number::CheckCast", "value is not napi_number");
}

inline Number::Number() : Value() {}

inline Number::Number(napi_env env, napi_value value) : Value(env, value) {}
Expand Down Expand Up @@ -897,6 +920,16 @@ inline BigInt BigInt::New(napi_env env,
return BigInt(env, value);
}

inline void BigInt::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "BigInt::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "BigInt::CheckCast", "napi_typeof failed");
NAPI_CHECK(
type == napi_bigint, "BigInt::CheckCast", "value is not napi_bigint");
}

inline BigInt::BigInt() : Value() {}

inline BigInt::BigInt(napi_env env, napi_value value) : Value(env, value) {}
Expand Down Expand Up @@ -946,6 +979,15 @@ inline Date Date::New(napi_env env, double val) {
return Date(env, value);
}

inline void Date::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Date::CheckCast", "empty value");

bool result;
napi_status status = napi_is_date(env, value, &result);
NAPI_CHECK(status == napi_ok, "Date::CheckCast", "napi_is_date failed");
NAPI_CHECK(result, "Date::CheckCast", "value is not date");
}

inline Date::Date() : Value() {}

inline Date::Date(napi_env env, napi_value value) : Value(env, value) {}
Expand All @@ -965,6 +1007,16 @@ inline double Date::ValueOf() const {
////////////////////////////////////////////////////////////////////////////////
// Name class
////////////////////////////////////////////////////////////////////////////////
inline void Name::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Name::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "Name::CheckCast", "napi_typeof failed");
NAPI_CHECK(type == napi_string || type == napi_symbol,
"Name::CheckCast",
"value is not napi_string or napi_symbol");
}

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

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

inline void String::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "String::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "String::CheckCast", "napi_typeof failed");
NAPI_CHECK(
type == napi_string, "String::CheckCast", "value is not napi_string");
}

inline String::String() : Name() {}

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

inline void Symbol::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Symbol::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "Symbol::CheckCast", "napi_typeof failed");
NAPI_CHECK(
type == napi_symbol, "Symbol::CheckCast", "value is not napi_symbol");
}

inline Symbol::Symbol() : Name() {}

inline Symbol::Symbol(napi_env env, napi_value value) : Name(env, value) {}
Expand Down Expand Up @@ -1313,6 +1385,16 @@ inline Object Object::New(napi_env env) {
return Object(env, value);
}

inline void Object::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Object::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "Object::CheckCast", "napi_typeof failed");
NAPI_CHECK(
type == napi_object, "Object::CheckCast", "value is not napi_object");
}

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

inline Object::Object(napi_env env, napi_value value)
Expand Down Expand Up @@ -1719,6 +1801,18 @@ inline External<T> External<T>::New(napi_env env,
return External(env, value);
}

template <typename T>
inline void External<T>::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "External::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "External::CheckCast", "napi_typeof failed");
NAPI_CHECK(type == napi_external,
"External::CheckCast",
"value is not napi_external");
}

template <typename T>
inline External<T>::External() : TypeTaggable() {}

Expand Down Expand Up @@ -1752,6 +1846,15 @@ inline Array Array::New(napi_env env, size_t length) {
return Array(env, value);
}

inline void Array::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Array::CheckCast", "empty value");

bool result;
napi_status status = napi_is_array(env, value, &result);
NAPI_CHECK(status == napi_ok, "Array::CheckCast", "napi_is_array failed");
NAPI_CHECK(result, "Array::CheckCast", "value is not array");
}

inline Array::Array() : Object() {}

inline Array::Array(napi_env env, napi_value value) : Object(env, value) {}
Expand Down Expand Up @@ -1838,6 +1941,17 @@ inline ArrayBuffer ArrayBuffer::New(napi_env env,
}
#endif // NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED

inline void ArrayBuffer::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "ArrayBuffer::CheckCast", "empty value");

bool result;
napi_status status = napi_is_arraybuffer(env, value, &result);
NAPI_CHECK(status == napi_ok,
"ArrayBuffer::CheckCast",
"napi_is_arraybuffer failed");
NAPI_CHECK(result, "ArrayBuffer::CheckCast", "value is not arraybuffer");
}

inline ArrayBuffer::ArrayBuffer() : Object() {}

inline ArrayBuffer::ArrayBuffer(napi_env env, napi_value value)
Expand Down Expand Up @@ -1905,6 +2019,16 @@ inline DataView DataView::New(napi_env env,
return DataView(env, value);
}

inline void DataView::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "DataView::CheckCast", "empty value");

bool result;
napi_status status = napi_is_dataview(env, value, &result);
NAPI_CHECK(
status == napi_ok, "DataView::CheckCast", "napi_is_dataview failed");
NAPI_CHECK(result, "DataView::CheckCast", "value is not dataview");
}

inline DataView::DataView() : Object() {}

inline DataView::DataView(napi_env env, napi_value value) : Object(env, value) {
Expand Down Expand Up @@ -2039,6 +2163,15 @@ inline void DataView::WriteData(size_t byteOffset, T value) const {
////////////////////////////////////////////////////////////////////////////////
// TypedArray class
////////////////////////////////////////////////////////////////////////////////
inline void TypedArray::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "TypedArray::CheckCast", "empty value");

bool result;
napi_status status = napi_is_typedarray(env, value, &result);
NAPI_CHECK(
status == napi_ok, "TypedArray::CheckCast", "napi_is_typedarray failed");
NAPI_CHECK(result, "TypedArray::CheckCast", "value is not typedarray");
}

inline TypedArray::TypedArray()
: Object(), _type(napi_typedarray_type::napi_int8_array), _length(0) {}
Expand Down Expand Up @@ -2121,6 +2254,23 @@ inline Napi::ArrayBuffer TypedArray::ArrayBuffer() const {
////////////////////////////////////////////////////////////////////////////////
// TypedArrayOf<T> class
////////////////////////////////////////////////////////////////////////////////
template <typename T>
inline void TypedArrayOf<T>::CheckCast(napi_env env, napi_value value) {
TypedArray::CheckCast(env, value);
napi_typedarray_type type;
napi_status status = napi_get_typedarray_info(
env, value, &type, nullptr, nullptr, nullptr, nullptr);
NAPI_CHECK(status == napi_ok,
"TypedArrayOf::CheckCast",
"napi_is_typedarray failed");

NAPI_CHECK(
(type == TypedArrayTypeForPrimitiveType<T>() ||
(type == napi_uint8_clamped_array && std::is_same<T, uint8_t>::value)),
"TypedArrayOf::CheckCast",
"Array type must match the template parameter. (Uint8 arrays may "
"optionally have the \"clamped\" array type.)");
}

template <typename T>
inline TypedArrayOf<T> TypedArrayOf<T>::New(napi_env env,
Expand Down Expand Up @@ -2294,6 +2444,17 @@ inline Function Function::New(napi_env env,
return New(env, cb, utf8name.c_str(), data);
}

inline void Function::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Function::CheckCast", "empty value");

napi_valuetype type;
napi_status status = napi_typeof(env, value, &type);
NAPI_CHECK(status == napi_ok, "Function::CheckCast", "napi_typeof failed");
NAPI_CHECK(type == napi_function,
"Function::CheckCast",
"value is not napi_function");
}

inline Function::Function() : Object() {}

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

inline void Promise::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Promise::CheckCast", "empty value");

bool result;
napi_status status = napi_is_promise(env, value, &result);
NAPI_CHECK(status == napi_ok, "Promise::CheckCast", "napi_is_promise failed");
NAPI_CHECK(result, "Promise::CheckCast", "value is not promise");
}

inline Promise::Promise(napi_env env, napi_value value) : Object(env, value) {}

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

template <typename T>
inline void Buffer<T>::CheckCast(napi_env env, napi_value value) {
NAPI_CHECK(value != nullptr, "Buffer::CheckCast", "empty value");

bool result;
napi_status status = napi_is_buffer(env, value, &result);
NAPI_CHECK(status == napi_ok, "Buffer::CheckCast", "napi_is_buffer failed");
NAPI_CHECK(result, "Buffer::CheckCast", "value is not buffer");
}

template <typename T>
inline Buffer<T>::Buffer() : Uint8Array(), _length(0), _data(nullptr) {}

Expand Down
Loading

0 comments on commit 5adb896

Please sign in to comment.