Skip to content

Commit

Permalink
feat: support restore stack struct
Browse files Browse the repository at this point in the history
  • Loading branch information
zhangyuang committed Nov 17, 2024
1 parent 12ccb61 commit 2cd1def
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 57 deletions.
37 changes: 37 additions & 0 deletions mac_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const { load, open, DataType } = require("./index");

// 定义 CGRect 结构体
const CGRect = {
origin: {
x: DataType.Double,
y: DataType.Double,
ffiTypeTag: DataType.StackStruct,
},
size: {
width: DataType.Double,
height: DataType.Double,
ffiTypeTag: DataType.StackStruct,
},
ffiTypeTag: DataType.StackStruct,
};

// 获取主屏幕尺寸的函数
const getMainDisplaySize = async () => {
open({
library: "ApplicationServices",
path: "/System/Library/Frameworks/ApplicationServices.framework/ApplicationServices",
});

const bounds = await load({
library: "ApplicationServices",
funcName: "CGDisplayBounds",
paramsType: [DataType.I32], // 不支持 U32 ,使用 I32 报错
paramsValue: [1],
retType: CGRect,
});
return bounds;
};

if (process.platform === "darwin") {
getMainDisplaySize().then(console.log);
}
16 changes: 8 additions & 8 deletions src/datatype/create_struct.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::string::{string_to_c_string, string_to_c_w_string};
use crate::define::*;
use crate::utils::dataprocess::is_stack_struct;
use crate::utils::{
calculate_struct_size, get_array_desc, get_array_value, get_ffi_tag, get_js_external_wrap_data,
get_size_align,
Expand Down Expand Up @@ -124,10 +125,10 @@ pub unsafe fn generate_c_struct(
offset += size + padding;
size
}
RsArgsValue::Object(mut val) => {
if let FFITag::Array = get_ffi_tag(&val) {
let array_desc = get_array_desc(&val);
let array_value = get_array_value(&mut val).unwrap();
RsArgsValue::Object(mut obj_value) => {
if let FFITag::Array = get_ffi_tag(&obj_value) {
let array_desc = get_array_desc(&obj_value);
let array_value = get_array_value(&mut obj_value).unwrap();
let FFIARRARYDESC {
array_type,
array_len,
Expand Down Expand Up @@ -261,8 +262,7 @@ pub unsafe fn generate_c_struct(
} else {
let is_stack_struct =
if let Some(RsArgsValue::Object(field_type)) = struct_type.get(&field) {
field_type.get(FFI_TAG_FIELD)
== Some(&RsArgsValue::I32(ReserveDataType::StackStruct.to_i32()))
is_stack_struct(field_type)
} else {
false
};
Expand All @@ -273,7 +273,7 @@ pub unsafe fn generate_c_struct(
let (size, align) = calculate_struct_size(val_type);
let padding = (align - (offset % align)) % align;
field_ptr = field_ptr.offset(padding as isize);
generate_c_struct(env, val_type, val, Some(field_ptr))?;
generate_c_struct(env, val_type, obj_value, Some(field_ptr))?;
offset += size + padding;
size
} else {
Expand All @@ -284,7 +284,7 @@ pub unsafe fn generate_c_struct(
let padding = (align - (offset % align)) % align;
field_ptr = field_ptr.offset(padding as isize);
if let RsArgsValue::Object(val_type) = struct_type.get(&field).unwrap() {
let obj_ptr = generate_c_struct(env, val_type, val, None)?;
let obj_ptr = generate_c_struct(env, val_type, obj_value, None)?;
(field_ptr as *mut *const c_void).write(obj_ptr);
}
offset += size + padding;
Expand Down
5 changes: 2 additions & 3 deletions src/datatype/pointer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::utils::{
calculate_struct_size, get_array_desc, get_ffi_tag, get_func_desc, get_size_align,
is_stack_struct,
};
use indexmap::IndexMap;
use libc::{c_double, c_float, c_int, c_void, free};
Expand Down Expand Up @@ -257,9 +258,7 @@ unsafe fn free_struct_memory(
}
_ => {
// struct
if obj.get(FFI_TAG_FIELD)
== Some(&RsArgsValue::I32(ReserveDataType::StackStruct.to_i32()))
{
if is_stack_struct(obj) {
let (size, align) = calculate_struct_size(&obj);
let padding = (align - (offset % align)) % align;
field_ptr = field_ptr.offset(padding as isize);
Expand Down
4 changes: 1 addition & 3 deletions src/datatype/restore_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,9 +249,7 @@ pub unsafe fn create_rs_struct_from_pointer(
};
} else {
// raw object
if sub_obj_type.get(FFI_TAG_FIELD)
== Some(&RsArgsValue::I32(ReserveDataType::StackStruct.to_i32()))
{
if is_stack_struct(&sub_obj_type) {
let (size, align) = calculate_struct_size(sub_obj_type);
let padding = (align - (offset % align)) % align;
field_ptr = field_ptr.offset(padding as isize);
Expand Down
18 changes: 18 additions & 0 deletions src/define.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,24 @@ pub struct OpenParams {
pub path: String,
}

pub struct FFITypeCleanup {
pub struct_type_box: Option<*mut ffi_type>,
pub elements_box: Option<*mut Vec<*mut ffi_type>>,
}

impl Drop for FFITypeCleanup {
fn drop(&mut self) {
unsafe {
if let Some(struct_type_box) = self.struct_type_box.take() {
let _ = Box::from_raw(struct_type_box);
}
if let Some(elements_box) = self.elements_box.take() {
let _ = Box::from_raw(elements_box);
}
}
}
}

pub const ARRAY_LENGTH_TAG: &str = "length";
pub const ARRAY_TYPE_TAG: &str = "type";
pub const ARRAY_DYNAMIC_TAG: &str = "dynamicArray";
Expand Down
82 changes: 57 additions & 25 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ use define::*;
use dlopen::symbor::{Library, Symbol};
use libffi_sys::{
ffi_abi_FFI_DEFAULT_ABI, ffi_call, ffi_cif, ffi_prep_cif, ffi_type, ffi_type_double,
ffi_type_float, ffi_type_pointer, ffi_type_sint32, ffi_type_sint64, ffi_type_uint64,
ffi_type_uint8, ffi_type_void,
ffi_type_enum_STRUCT, ffi_type_float, ffi_type_pointer, ffi_type_sint32, ffi_type_sint64,
ffi_type_uint64, ffi_type_uint8, ffi_type_void,
};
use napi::{Env, JsExternal, JsUnknown, Result};
use std::collections::HashMap;
use std::ffi::c_void;
use std::rc::Rc;
use utils::dataprocess::{
get_arg_types_values, get_js_external_wrap_data, get_js_unknown_from_pointer, get_value_pointer,
type_define_to_rs_args,
is_stack_struct, type_define_to_rs_args,
};

static mut LIBRARY_MAP: Option<
Expand Down Expand Up @@ -52,15 +52,12 @@ unsafe fn create_pointer(env: Env, params: CreatePointerParams) -> Result<Vec<Js
.collect()
}



#[napi]
unsafe fn is_null_pointer(env: Env, js_external: JsExternal) -> Result<bool> {
let ptr = get_js_external_wrap_data(&env, js_external)?;
Ok(ptr.is_null())
let ptr = get_js_external_wrap_data(&env, js_external)?;
Ok(ptr.is_null())
}


#[napi]
unsafe fn restore_pointer(env: Env, params: StorePointerParams) -> Result<Vec<JsUnknown>> {
let StorePointerParams {
Expand Down Expand Up @@ -226,25 +223,59 @@ unsafe fn load(env: Env, params: FFIParams) -> napi::Result<JsUnknown> {
let (mut arg_types, arg_values) = get_arg_types_values(Rc::clone(&params_type_rs), params_value)?;
let mut arg_values_c_void = get_value_pointer(&env, Rc::clone(&params_type_rs), arg_values)?;
let ret_type_rs = type_define_to_rs_args(&env, ret_type)?;
let r_type: *mut ffi_type = match ret_type_rs {
RsArgsValue::I32(number) => {
let ret_data_type = number.to_basic_data_type();
match ret_data_type {
BasicDataType::U8 => &mut ffi_type_uint8 as *mut ffi_type,
BasicDataType::I32 => &mut ffi_type_sint32 as *mut ffi_type,
BasicDataType::I64 | BasicDataType::BigInt => &mut ffi_type_sint64 as *mut ffi_type,
BasicDataType::U64 => &mut ffi_type_uint64 as *mut ffi_type,
BasicDataType::String | BasicDataType::WString => &mut ffi_type_pointer as *mut ffi_type,
BasicDataType::Void => &mut ffi_type_void as *mut ffi_type,
BasicDataType::Float => &mut ffi_type_float as *mut ffi_type,
BasicDataType::Double => &mut ffi_type_double as *mut ffi_type,
BasicDataType::Boolean => &mut ffi_type_uint8 as *mut ffi_type,
BasicDataType::External => &mut ffi_type_pointer as *mut ffi_type,
let mut ffi_type_cleanup = FFITypeCleanup {
struct_type_box: None,
elements_box: None,
};
unsafe fn get_ffi_type(
ret_type_rs: &RsArgsValue,
ffi_type_cleanup: &mut FFITypeCleanup,
) -> *mut ffi_type {
match ret_type_rs {
RsArgsValue::I32(number) => {
let ret_data_type = number.to_basic_data_type();
match ret_data_type {
BasicDataType::U8 => &mut ffi_type_uint8 as *mut ffi_type,
BasicDataType::I32 => &mut ffi_type_sint32 as *mut ffi_type,
BasicDataType::I64 | BasicDataType::BigInt => &mut ffi_type_sint64 as *mut ffi_type,
BasicDataType::U64 => &mut ffi_type_uint64 as *mut ffi_type,
BasicDataType::String | BasicDataType::WString => &mut ffi_type_pointer as *mut ffi_type,
BasicDataType::Void => &mut ffi_type_void as *mut ffi_type,
BasicDataType::Float => &mut ffi_type_float as *mut ffi_type,
BasicDataType::Double => &mut ffi_type_double as *mut ffi_type,
BasicDataType::Boolean => &mut ffi_type_uint8 as *mut ffi_type,
BasicDataType::External => &mut ffi_type_pointer as *mut ffi_type,
}
}
RsArgsValue::Object(struct_type) => {
if is_stack_struct(struct_type) {
let mut elements: Vec<*mut ffi_type> = struct_type
.iter()
.filter(|(field_name, _)| field_name != &FFI_TAG_FIELD)
.map(|(_, field_type)| get_ffi_type(field_type, ffi_type_cleanup))
.collect();
elements.push(std::ptr::null_mut());
let struct_type_box = ffi_type {
size: 0,
alignment: 0,
type_: ffi_type_enum_STRUCT as u16,
elements: elements.as_mut_ptr(),
};
let elements_ptr = Box::into_raw(Box::new(elements));
let struct_type_ptr = Box::into_raw(Box::new(struct_type_box));
ffi_type_cleanup.elements_box = Some(elements_ptr);
ffi_type_cleanup.struct_type_box = Some(struct_type_ptr);
struct_type_ptr as *mut ffi_type
} else {
&mut ffi_type_pointer as *mut ffi_type
}
}
_ => &mut ffi_type_void as *mut ffi_type,
}
RsArgsValue::Object(_) => &mut ffi_type_pointer as *mut ffi_type,
_ => &mut ffi_type_void as *mut ffi_type,
};
}

let r_type = get_ffi_type(&ret_type_rs, &mut ffi_type_cleanup);

let mut cif = ffi_cif {
abi: ffi_abi_FFI_DEFAULT_ABI,
nargs: params_type_len as u32,
Expand All @@ -265,6 +296,7 @@ unsafe fn load(env: Env, params: FFIParams) -> napi::Result<JsUnknown> {
#[cfg(all(target_arch = "arm"))]
vfp_args: [0; 16],
};

ffi_prep_cif(
&mut cif,
ffi_abi_FFI_DEFAULT_ABI,
Expand Down
38 changes: 25 additions & 13 deletions src/utils/dataprocess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ pub unsafe fn get_js_external_wrap_data(env: &Env, js_external: JsExternal) -> R
Ok(*p)
}

pub fn is_stack_struct(obj: &IndexMap<String, RsArgsValue>) -> bool {
if let Some(tag) = obj.get(FFI_TAG_FIELD) {
tag == &RsArgsValue::I32(ReserveDataType::StackStruct.to_i32())
} else {
false
}
}
pub fn get_ffi_tag(obj: &IndexMap<String, RsArgsValue>) -> FFITag {
if obj.get(FFI_TAG_FIELD).is_none() {
return FFITag::Unknown;
Expand Down Expand Up @@ -85,9 +92,6 @@ pub fn get_func_desc(obj: &IndexMap<String, RsArgsValue>) -> FFIFUNCDESC {
FFIFUNCDESC { need_free }
}

pub fn js_number_to_i32(js_number: JsNumber) -> Result<i32> {
js_number.try_into()
}
pub unsafe fn get_arg_types_values(
params_type: Rc<Vec<RsArgsValue>>,
params_value: Vec<JsUnknown>,
Expand Down Expand Up @@ -721,9 +725,9 @@ pub unsafe fn type_object_to_rs_struct(
pub unsafe fn type_define_to_rs_args(env: &Env, type_define: JsUnknown) -> Result<RsArgsValue> {
let params_type_value_type = type_define.get_type()?;
let ret_value = match params_type_value_type {
ValueType::Number => RsArgsValue::I32(js_number_to_i32(
create_js_value_unchecked::<JsNumber>(type_define)?,
)?),
ValueType::Number => {
RsArgsValue::I32(create_js_value_unchecked::<JsNumber>(type_define)?.try_into()?)
}
ValueType::Object => RsArgsValue::Object(type_object_to_rs_struct(
env,
&create_js_value_unchecked::<JsObject>(type_define)?,
Expand Down Expand Up @@ -757,9 +761,7 @@ pub unsafe fn get_js_unknown_from_pointer(
rs_value_to_js_unknown(&env, RsArgsValue::String(ptr_str))
}
BasicDataType::WString => {
let ptr_str = WideCString::from_ptr_str(*(ptr as *mut *const WideChar))
.to_string_lossy()
.to_string();
let ptr_str = WideCString::from_ptr_str(*(ptr as *mut *const WideChar)).to_string_lossy();
rs_value_to_js_unknown(&env, RsArgsValue::WString(ptr_str))
}
BasicDataType::U8 => rs_value_to_js_unknown(env, RsArgsValue::U8(*(ptr as *mut u8))),
Expand All @@ -783,9 +785,9 @@ pub unsafe fn get_js_unknown_from_pointer(
}
}
}
RsArgsValue::Object(obj) => {
if let FFITag::Array = get_ffi_tag(&obj) {
let array_desc = get_array_desc(&obj);
RsArgsValue::Object(sub_obj_type) => {
if let FFITag::Array = get_ffi_tag(&sub_obj_type) {
let array_desc = get_array_desc(&sub_obj_type);
// array
let FFIARRARYDESC {
array_type,
Expand Down Expand Up @@ -816,7 +818,17 @@ pub unsafe fn get_js_unknown_from_pointer(
}
} else {
// raw object
let rs_struct = create_rs_struct_from_pointer(env, *(ptr as *mut *mut c_void), &obj, false);
let is_stack_struct = is_stack_struct(&sub_obj_type);
let rs_struct = create_rs_struct_from_pointer(
env,
if is_stack_struct {
ptr
} else {
*(ptr as *mut *mut c_void)
},
&sub_obj_type,
false,
);
rs_value_to_js_unknown(env, RsArgsValue::Object(rs_struct))
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/utils/object_utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::dataprocess::{get_array_desc, get_ffi_tag};
use super::dataprocess::{get_array_desc, get_ffi_tag, is_stack_struct};
use crate::define::*;
use crate::{RefDataType, RsArgsValue, FFIARRARYDESC};
use indexmap::IndexMap;
Expand Down Expand Up @@ -81,9 +81,7 @@ pub fn calculate_struct_size(struct_type: &IndexMap<String, RsArgsValue>) -> (us
return calculate_pointer(size, align, offset);
}
} else {
if obj.get(FFI_TAG_FIELD)
== Some(&RsArgsValue::I32(ReserveDataType::StackStruct.to_i32()))
{
if is_stack_struct(obj) {
let (type_size, type_align) = calculate_struct_size(obj);
let align = align.max(type_align);
let padding = (type_align - (offset % type_align)) % type_align;
Expand Down
2 changes: 1 addition & 1 deletion test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,4 +583,4 @@ const unitTest = () => {

unitTest();

exports.unitTest = unitTest;
exports.unitTest = unitTest;

0 comments on commit 2cd1def

Please sign in to comment.