0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-20 12:02:18 +02:00

bridge: Add support for arrays as arguments and return values

Specifically, adding support for *references to fixed-sized arrays* as
arguments (pointer-to-fixed-size-array in C, byte[] in Java, Buffer in
Node), and fixed-sized array values as results (out-pointer-to-fixed-
sized-array in C, byte[] in Java, Buffer in Node). This will be used
for some zkgroup primitive values.
This commit is contained in:
Jordan Rose 2021-10-28 13:18:35 -07:00
parent f1da238532
commit c5ba4ecf48
7 changed files with 97 additions and 4 deletions

View File

@ -39,7 +39,8 @@ ignore_this_warning = re.compile(
"("
r"WARN: Can't find .*\. This usually means that this type was incompatible or not found\.|"
r"WARN: Missing `\[defines\]` entry for `feature = \".*\"` in cbindgen config\.|"
r"WARN: Skip libsignal-bridge::_ - \(not `pub`\)\."
r"WARN: Skip libsignal-bridge::_ - \(not `pub`\)\.|"
r"WARN: Couldn't find path for Array\(Path\(GenericPath \{ .+ \}\), Name\(\"LEN\"\)\), skipping associated constants"
")")
unknown_warning = False

View File

@ -39,6 +39,9 @@ def translate_to_ts(typ):
if typ in type_map:
return type_map[typ]
if typ.startswith('[u8;') or typ.startswith('&[u8;'):
return 'Buffer'
if typ.startswith('&mutdyn'):
return typ[7:]

View File

@ -253,6 +253,21 @@ impl ResultTypeInfo for uuid::Uuid {
}
}
impl<const LEN: usize> SimpleArgTypeInfo for &'_ [u8; LEN] {
type ArgType = *const [u8; LEN];
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn convert_from(arg: *const [u8; LEN]) -> SignalFfiResult<Self> {
unsafe { arg.as_ref() }.ok_or(SignalFfiError::NullPointer)
}
}
impl<const LEN: usize> ResultTypeInfo for [u8; LEN] {
type ResultType = [u8; LEN];
fn convert_into(self) -> SignalFfiResult<Self::ResultType> {
Ok(self)
}
}
macro_rules! store {
($name:ident) => {
paste! {
@ -519,6 +534,7 @@ macro_rules! ffi_arg_type {
(Context) => (*mut libc::c_void);
(Timestamp) => (u64);
(Uuid) => (*const [u8; 16]);
(&[u8; $len:expr]) => (*const [u8; $len]);
(&[& $typ:ty]) => (*const *const $typ);
(&mut dyn $typ:ty) => (*const paste!(ffi::[<Ffi $typ Struct>]));
(& $typ:ty) => (*const $typ);
@ -554,5 +570,6 @@ macro_rules! ffi_result_type {
(Option<$typ:ty>) => (*mut $typ);
(Timestamp) => (u64);
(Uuid) => ([u8; 16]);
([u8; $len:expr]) => ([u8; $len]);
( $typ:ty ) => (*mut $typ);
}

View File

@ -552,6 +552,36 @@ impl ResultTypeInfo for Option<Vec<u8>> {
}
}
impl<'storage, 'context: 'storage, const LEN: usize> ArgTypeInfo<'storage, 'context>
for &'storage [u8; LEN]
{
type ArgType = jbyteArray;
type StoredType = AutoArray<'context, 'context, jbyte>;
fn borrow(env: &'context JNIEnv, foreign: Self::ArgType) -> SignalJniResult<Self::StoredType> {
Ok(env.get_byte_array_elements(foreign, ReleaseMode::NoCopyBack)?)
}
fn load_from(
_env: &JNIEnv,
stored: &'storage mut Self::StoredType,
) -> SignalJniResult<&'storage [u8; LEN]> {
unsafe { std::slice::from_raw_parts(stored.as_ptr() as *const u8, stored.size()? as usize) }
.try_into()
.map_err(|_| SignalJniError::DeserializationFailed(std::any::type_name::<[u8; LEN]>()))
}
}
impl<const LEN: usize> ResultTypeInfo for [u8; LEN] {
type ResultType = jbyteArray;
fn convert_into(self, env: &JNIEnv) -> SignalJniResult<Self::ResultType> {
self.as_ref().convert_into(env)
}
fn convert_into_jobject(signal_jni_result: &SignalJniResult<Self::ResultType>) -> JObject {
signal_jni_result
.as_ref()
.map_or(JObject::null(), |&jobj| JObject::from(jobj))
}
}
impl ResultTypeInfo for uuid::Uuid {
type ResultType = jobject;
fn convert_into(self, env: &JNIEnv) -> SignalJniResult<Self::ResultType> {
@ -864,6 +894,9 @@ macro_rules! jni_arg_type {
(&mut [u8]) => {
jni::jbyteArray
};
(&[u8; $len:expr]) => {
jni::jbyteArray
};
(Context) => {
jni::JObject
};
@ -953,6 +986,9 @@ macro_rules! jni_result_type {
(Vec<u8>) => {
jni::jbyteArray
};
([u8; $len:expr]) => {
jni::jbyteArray
};
(Option<$typ:tt>) => {
jni_result_type!($typ)
};

View File

@ -24,6 +24,7 @@ pub enum SignalJniError {
SignalCrypto(SignalCryptoError),
Jni(jni::errors::Error),
BadJniParameter(&'static str),
DeserializationFailed(&'static str),
UnexpectedJniResultType(&'static str, &'static str),
NullHandle,
IntegerOverflow(String),
@ -46,6 +47,9 @@ impl fmt::Display for SignalJniError {
SignalJniError::IntegerOverflow(m) => {
write!(f, "integer overflow during conversion of {}", m)
}
SignalJniError::DeserializationFailed(ty) => {
write!(f, "failed to deserialize {}", ty)
}
SignalJniError::HsmEnclave(e) => {
write!(f, "{}", e)
}

View File

@ -220,9 +220,8 @@ fn throw_error(env: &JNIEnv, error: SignalJniError) {
SignalJniError::Signal(SignalProtocolError::InvalidArgument(_))
| SignalJniError::SignalCrypto(SignalCryptoError::UnknownAlgorithm(_, _))
| SignalJniError::SignalCrypto(SignalCryptoError::InvalidInputSize)
| SignalJniError::SignalCrypto(SignalCryptoError::InvalidNonceSize) => {
"java/lang/IllegalArgumentException"
}
| SignalJniError::SignalCrypto(SignalCryptoError::InvalidNonceSize)
| SignalJniError::DeserializationFailed(_) => "java/lang/IllegalArgumentException",
SignalJniError::IntegerOverflow(_)
| SignalJniError::Jni(_)

View File

@ -608,6 +608,39 @@ impl<'a> ResultTypeInfo<'a> for Vec<u8> {
}
}
/// Loads from a JsBuffer, assuming it won't be mutated while in use.
/// See [`AssumedImmutableBuffer`].
impl<'storage, 'context: 'storage, const LEN: usize> ArgTypeInfo<'storage, 'context>
for &'storage [u8; LEN]
{
type ArgType = JsBuffer;
type StoredType = AssumedImmutableBuffer<'context>;
fn borrow(
cx: &mut FunctionContext,
foreign: Handle<'context, Self::ArgType>,
) -> NeonResult<Self::StoredType> {
let result = AssumedImmutableBuffer::new(cx, foreign);
if result.buffer.len() != LEN {
cx.throw_error(format!(
"buffer has incorrect length {} (expected {})",
result.buffer.len(),
LEN
))?;
}
Ok(result)
}
fn load_from(stored: &'storage mut Self::StoredType) -> Self {
stored.buffer.try_into().expect("checked length already")
}
}
impl<'a, const LEN: usize> ResultTypeInfo<'a> for [u8; LEN] {
type ResultType = JsBuffer;
fn convert_into(self, cx: &mut impl Context<'a>) -> NeonResult<Handle<'a, Self::ResultType>> {
self.as_ref().convert_into(cx)
}
}
impl<'a, T: ResultTypeInfo<'a>> ResultTypeInfo<'a> for NeonResult<T> {
type ResultType = T::ResultType;
fn convert_into(self, cx: &mut impl Context<'a>) -> NeonResult<Handle<'a, Self::ResultType>> {