Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions NativeScript/ffi/jni/jsi/conversion/ArrayElementAccessor.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "ArrayElementAccessor.h"
#include "JsArgToArrayConverter.h"
#include "ArgConverter.h"
#include "Coercion.h"
#include "Util.h"
#include "NativeScriptException.h"
#include "Runtime.h"
Expand Down Expand Up @@ -114,12 +115,12 @@ void ArrayElementAccessor::SetArrayElement(JsRuntime &rt, const JsValue &array,
// chain).
switch (arraySignature[1]) {
case 'Z': { //bool
jboolean v = static_cast<jboolean>(js_util::get_bool(value));
jboolean v = static_cast<jboolean>(coerce::ToBool(rt, value));
jenv.SetBooleanArrayRegion((jbooleanArray) arr, index, 1, &v);
break;
}
case 'B': { //byte
jbyte v = static_cast<jbyte>(js_util::get_int32(value));
jbyte v = static_cast<jbyte>(coerce::ToInt32(rt, value));
jenv.SetByteArrayRegion((jbyteArray) arr, index, 1, &v);
break;
}
Expand All @@ -134,27 +135,28 @@ void ArrayElementAccessor::SetArrayElement(JsRuntime &rt, const JsValue &array,
break;
}
case 'S': { //short
jshort v = static_cast<jshort>(js_util::get_int32(value));
jshort v = static_cast<jshort>(coerce::ToInt32(rt, value));
jenv.SetShortArrayRegion((jshortArray) arr, index, 1, &v);
break;
}
case 'I': { //int
jint v = static_cast<jint>(js_util::get_int32(value));
jint v = static_cast<jint>(coerce::ToInt32(rt, value));
jenv.SetIntArrayRegion((jintArray) arr, index, 1, &v);
break;
}
case 'J': { //long
jlong v = static_cast<jlong>(js_util::get_number(value));
jlong v = value.isObject() ? static_cast<jlong>(ArgConverter::ConvertToJavaLong(rt, value))
: static_cast<jlong>(coerce::ToInt64(rt, value));
jenv.SetLongArrayRegion((jlongArray) arr, index, 1, &v);
break;
}
case 'F': { //float
jfloat v = static_cast<jfloat>(js_util::get_number(value));
jfloat v = static_cast<jfloat>(coerce::ToDouble(rt, value));
jenv.SetFloatArrayRegion((jfloatArray) arr, index, 1, &v);
break;
}
case 'D': { //double
jdouble v = static_cast<jdouble>(js_util::get_number(value));
jdouble v = static_cast<jdouble>(coerce::ToDouble(rt, value));
jenv.SetDoubleArrayRegion((jdoubleArray) arr, index, 1, &v);
break;
}
Expand Down
72 changes: 72 additions & 0 deletions NativeScript/ffi/jni/jsi/conversion/Coercion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#ifndef COERCION_H_
#define COERCION_H_

#include <cmath>
#include <cstdint>
#include <limits>

#include "Engine.h"

namespace tns {
namespace coerce {

// Each conversion returns a value that is already the right type as it always did. Only when it is
// not is the value coerced the way the classic V8 runtime does (ToNumber, ToInt32, ToBoolean). A
// coercion that throws, such as a Symbol, yields 0 / false and leaves no exception behind.

inline double CoerceToDouble(JsRuntime &rt, const JsValue &value) {
try {
JsValue ctor = rt.global().getProperty(rt, "Number");
JsFunction fn = ctor.asObjectBorrowed(rt).asFunction(rt);
JsValue argument(rt, value);
JsValue result = fn.callWithThis(rt, rt.global(), &argument, 1);
return result.isNumber() ? result.getNumber() : std::numeric_limits<double>::quiet_NaN();
} catch (JsError &) {
return 0;
}
}

inline bool CoerceToBool(JsRuntime &rt, const JsValue &value) {
try {
JsValue ctor = rt.global().getProperty(rt, "Boolean");
JsFunction fn = ctor.asObjectBorrowed(rt).asFunction(rt);
JsValue argument(rt, value);
JsValue result = fn.callWithThis(rt, rt.global(), &argument, 1);
return result.isBool() && result.getBool();
} catch (JsError &) {
return false;
}
}

inline double ToDouble(JsRuntime &rt, const JsValue &value) {
return value.isNumber() ? value.getNumber() : CoerceToDouble(rt, value);
}

inline int32_t Int32FromDouble(double d) {
if (d >= -2147483648.0 && d <= 2147483647.0) return static_cast<int32_t>(d);
if (!std::isfinite(d)) return 0;
double wrapped = std::fmod(std::trunc(d), 4294967296.0);
if (wrapped < 0) wrapped += 4294967296.0;
return static_cast<int32_t>(static_cast<uint32_t>(wrapped));
}

inline int32_t ToInt32(JsRuntime &rt, const JsValue &value) {
return Int32FromDouble(ToDouble(rt, value));
}

inline int64_t ToInt64(JsRuntime &rt, const JsValue &value) {
double d = ToDouble(rt, value);
if (std::isnan(d)) return 0;
if (d >= 9223372036854775807.0) return std::numeric_limits<int64_t>::max();
if (d <= -9223372036854775808.0) return std::numeric_limits<int64_t>::min();
return static_cast<int64_t>(d);
}

inline bool ToBool(JsRuntime &rt, const JsValue &value) {
return value.isBool() ? value.getBool() : CoerceToBool(rt, value);
}

} // namespace coerce
} // namespace tns

#endif /* COERCION_H_ */
7 changes: 4 additions & 3 deletions NativeScript/ffi/jni/jsi/conversion/JsArgToArrayConverter.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "JsArgToArrayConverter.h"
#include "Coercion.h"
#include <sstream>
#include "ObjectManager.h"
#include "ArgConverter.h"
Expand Down Expand Up @@ -190,7 +191,7 @@ bool JsArgToArrayConverter::ConvertArg(JsRuntime &rt, const JsValue &arg, int in
auto strValue = ArgConverter::ConvertToString(rt, castValue);
longValue = atoll(strValue.c_str());
} else {
longValue = (jlong) js_util::get_number(castValue);
longValue = (jlong) coerce::ToInt64(rt, castValue);
}
}
javaObject = JType::NewLong(jEnv, longValue);
Expand All @@ -202,7 +203,7 @@ bool JsArgToArrayConverter::ConvertArg(JsRuntime &rt, const JsValue &arg, int in
castValue = NumericCasts::GetCastValue(rt, jsObj);
floatValue = 0;
if (!castValue.isUndefined()) {
floatValue = (jfloat) js_util::get_number(castValue);
floatValue = (jfloat) coerce::ToDouble(rt, castValue);
}
javaObject = JType::NewFloat(jEnv, floatValue);
SetConvertedObject(jEnv, index, javaObject);
Expand All @@ -213,7 +214,7 @@ bool JsArgToArrayConverter::ConvertArg(JsRuntime &rt, const JsValue &arg, int in
castValue = NumericCasts::GetCastValue(rt, jsObj);
doubleValue = 0;
if (!castValue.isUndefined()) {
doubleValue = (jdouble) js_util::get_number(castValue);
doubleValue = (jdouble) coerce::ToDouble(rt, castValue);
}
javaObject = JType::NewDouble(jEnv, doubleValue);
SetConvertedObject(jEnv, index, javaObject);
Expand Down
5 changes: 3 additions & 2 deletions NativeScript/ffi/jni/jsi/metadata/MetadataNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <android/log.h>
#include "NativeScriptException.h"
#include "MetadataNode.h"
#include "Coercion.h"
#include "CallbackHandlers.h"
#include "NativeScriptAssert.h"
#include "File.h"
Expand Down Expand Up @@ -187,7 +188,7 @@ JsValue MetadataNode::CreateJSWrapper(JsRuntime &rt, ObjectManager *objectManage
JsValue MetadataNode::ArrayGetterCallback(JsRuntime &rt, const JsValue &thisVal,
const JsValue *args, size_t argc) {
return Guarded(rt, [&]() -> JsValue {
int32_t indexValue = argc > 0 ? js_util::get_int32(args[0]) : 0;
int32_t indexValue = argc > 0 ? coerce::ToInt32(rt, args[0]) : 0;
auto node = GetInstanceMetadata(rt, thisVal);

return CallbackHandlers::GetArrayElement(rt, thisVal, indexValue, node->m_name);
Expand Down Expand Up @@ -221,7 +222,7 @@ JsValue MetadataNode::ArraySetterCallback(JsRuntime &rt, const JsValue &thisVal,
return Guarded(rt, [&]() -> JsValue {
if (argc < 2) return js_util::undefined();

int32_t indexValue = js_util::get_int32(args[0]);
int32_t indexValue = coerce::ToInt32(rt, args[0]);
auto node = GetInstanceMetadata(rt, thisVal);

CallbackHandlers::SetArrayElement(rt, thisVal, indexValue, node->m_name, args[1]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "Util.h"
#include "JniLocalRef.h"
#include "MetadataNode.h"
#include "Coercion.h"
#include "MethodCache.h"
#include "ArgConverter.h"
#include "JsArgConverter.h"
Expand Down Expand Up @@ -1833,8 +1834,8 @@ void CallbackHandlers::CallWorkerScopeOnErrorHandle(napi_env env, napi_value err
line);
}
} else if (!napi_util::is_null_or_undefined(env, result)) {
bool handled;
NAPI_GUARD(napi_get_value_bool(env, result, &handled)) {}
bool handled = false;
NAPI_GUARD(coerce::GetBool(env, result, &handled)) {}
if (handled) {
return;
}
Expand Down
38 changes: 28 additions & 10 deletions NativeScript/ffi/jni/napi/conversion/ArrayElementAccessor.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "ArrayElementAccessor.h"
#include "JsArgToArrayConverter.h"
#include "ArgConverter.h"
#include "Coercion.h"
#include "Util.h"
#include "NativeScriptException.h"
#include "Runtime.h"
Expand Down Expand Up @@ -142,7 +143,7 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
switch (arraySignature[1]) {
case 'Z': { //bool
bool b;
NAPI_GUARD(napi_get_value_bool(env, value, &b)) {
NAPI_GUARD(coerce::GetBool(env, value, &b)) {
return;
}
jboolean v = static_cast<jboolean>(b);
Expand All @@ -151,7 +152,7 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
}
case 'B': { //byte
int32_t i;
NAPI_GUARD(napi_get_value_int32(env, value, &i)) {
NAPI_GUARD(coerce::GetInt32(env, value, &i)) {
return;
}
jbyte v = static_cast<jbyte>(i);
Expand All @@ -160,8 +161,16 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
}
case 'C': { //char
size_t str_len;
NAPI_GUARD(napi_get_value_string_utf8(env, value, nullptr, 0, &str_len)) {
return;
status = napi_get_value_string_utf8(env, value, nullptr, 0, &str_len);
if (status != napi_ok) {
napi_value coerced;
NAPI_GUARD(coerce::ToString(env, value, &coerced)) {
return;
}
value = coerced;
NAPI_GUARD(napi_get_value_string_utf8(env, value, nullptr, 0, &str_len)) {
return;
}
}
string str(str_len, '\0');
NAPI_GUARD(napi_get_value_string_utf8(env, value, &str[0], str_len + 1, &str_len)) {
Expand All @@ -177,7 +186,7 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
}
case 'S': { //short
int32_t i;
NAPI_GUARD(napi_get_value_int32(env, value, &i)) {
NAPI_GUARD(coerce::GetInt32(env, value, &i)) {
return;
}
jshort v = static_cast<jshort>(i);
Expand All @@ -186,7 +195,7 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
}
case 'I': { //int
int32_t i;
NAPI_GUARD(napi_get_value_int32(env, value, &i)) {
NAPI_GUARD(coerce::GetInt32(env, value, &i)) {
return;
}
jint v = static_cast<jint>(i);
Expand All @@ -195,16 +204,25 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
}
case 'J': { //long
int64_t l;
NAPI_GUARD(napi_get_value_int64(env, value, &l)) {
return;
status = napi_get_value_int64(env, value, &l);
if (status != napi_ok) {
napi_valuetype valueType;
napi_typeof(env, value, &valueType);
if (valueType == napi_object) {
l = ArgConverter::ConvertToJavaLong(env, value);
} else {
NAPI_GUARD(coerce::GetInt64(env, value, &l)) {
return;
}
}
}
jlong v = static_cast<jlong>(l);
jenv.SetLongArrayRegion((jlongArray) arr, index, 1, &v);
break;
}
case 'F': { //float
double d;
NAPI_GUARD(napi_get_value_double(env, value, &d)) {
NAPI_GUARD(coerce::GetDouble(env, value, &d)) {
return;
}
jfloat v = static_cast<jfloat>(d);
Expand All @@ -213,7 +231,7 @@ void ArrayElementAccessor::SetArrayElement(napi_env env, napi_value array, uint3
}
case 'D': { //double
double d;
NAPI_GUARD(napi_get_value_double(env, value, &d)) {
NAPI_GUARD(coerce::GetDouble(env, value, &d)) {
return;
}
jdouble v = static_cast<jdouble>(d);
Expand Down
61 changes: 61 additions & 0 deletions NativeScript/ffi/jni/napi/conversion/Coercion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifndef COERCION_H_
#define COERCION_H_

#include "js_native_api.h"

namespace tns {
namespace coerce {

// Each getter tries the strict Node-API getter first, so a value of the expected type costs what it
// always did. Only when that fails is the value coerced the way the classic V8 runtime does
// (ToNumber, ToInt32, ToBoolean, ToString). If coercion fails too, the failing status is returned
// and any exception the coercion raised is cleared, so the caller can log it and carry on.

inline napi_status Recover(napi_env env, napi_status status) {
if (status == napi_pending_exception) {
napi_value ignored;
napi_get_and_clear_last_exception(env, &ignored);
}
return status;
}

inline napi_status GetDouble(napi_env env, napi_value value, double *out) {
if (napi_get_value_double(env, value, out) == napi_ok) return napi_ok;
napi_value number;
napi_status status = napi_coerce_to_number(env, value, &number);
if (status != napi_ok) return Recover(env, status);
return napi_get_value_double(env, number, out);
}

inline napi_status GetInt32(napi_env env, napi_value value, int32_t *out) {
if (napi_get_value_int32(env, value, out) == napi_ok) return napi_ok;
napi_value number;
napi_status status = napi_coerce_to_number(env, value, &number);
if (status != napi_ok) return Recover(env, status);
return napi_get_value_int32(env, number, out);
}

inline napi_status GetInt64(napi_env env, napi_value value, int64_t *out) {
if (napi_get_value_int64(env, value, out) == napi_ok) return napi_ok;
napi_value number;
napi_status status = napi_coerce_to_number(env, value, &number);
if (status != napi_ok) return Recover(env, status);
return napi_get_value_int64(env, number, out);
}

inline napi_status GetBool(napi_env env, napi_value value, bool *out) {
if (napi_get_value_bool(env, value, out) == napi_ok) return napi_ok;
napi_value boolean;
napi_status status = napi_coerce_to_bool(env, value, &boolean);
if (status != napi_ok) return Recover(env, status);
return napi_get_value_bool(env, boolean, out);
}

inline napi_status ToString(napi_env env, napi_value value, napi_value *out) {
return Recover(env, napi_coerce_to_string(env, value, out));
}

} // namespace coerce
} // namespace tns

#endif /* COERCION_H_ */
Loading
Loading