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
20 changes: 10 additions & 10 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ objc2-model-io = { path = "framework-crates/objc2-model-io", version = "0.3.1",
objc2-multipeer-connectivity = { path = "framework-crates/objc2-multipeer-connectivity", version = "0.3.1", default-features = false }
objc2-natural-language = { path = "framework-crates/objc2-natural-language", version = "0.3.1", default-features = false }
objc2-nearby-interaction = { path = "framework-crates/objc2-nearby-interaction", version = "0.3.1", default-features = false }
objc2-network = { path = "framework-crates/objc2-network", version = "0.3.1", default-features = false }
objc2-network-extension = { path = "framework-crates/objc2-network-extension", version = "0.3.1", default-features = false }
objc2-notification-center = { path = "framework-crates/objc2-notification-center", version = "0.3.1", default-features = false }
objc2-osa-kit = { path = "framework-crates/objc2-osa-kit", version = "0.3.1", default-features = false }
Expand Down Expand Up @@ -415,6 +416,7 @@ objc2-model-io = { path = "framework-crates/objc2-model-io" }
objc2-multipeer-connectivity = { path = "framework-crates/objc2-multipeer-connectivity" }
objc2-natural-language = { path = "framework-crates/objc2-natural-language" }
objc2-nearby-interaction = { path = "framework-crates/objc2-nearby-interaction" }
objc2-network = { path = "framework-crates/objc2-network" }
objc2-network-extension = { path = "framework-crates/objc2-network-extension" }
objc2-notification-center = { path = "framework-crates/objc2-notification-center" }
objc2-osa-kit = { path = "framework-crates/objc2-osa-kit" }
Expand Down
1 change: 0 additions & 1 deletion crates/header-translator/configs/skipped.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ InterfaceBuilderKit = "TODO. Developer-only"
XcodeKit = "TODO. Developer-only"
StoreKitTest = "TODO. Developer-only"

Network = "TODO, see [#646](https://github.com/madsmtm/objc2/issues/646)"
DeviceDiscoveryUI = "Needs Network first"

BrowserKit = "TODO"
Expand Down
18 changes: 14 additions & 4 deletions crates/header-translator/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use std::sync::OnceLock;
use std::{fmt, ptr};

use clang::{Entity, EntityKind};
use heck::ToTrainCase;
use heck::{ToKebabCase, ToTrainCase, ToUpperCamelCase};
use semver::Version;
use serde::{de, Deserialize, Deserializer};

use crate::name_translation::cf_no_ref;
use crate::name_translation::strip_needless_suffix;
use crate::stmt::{Counterpart, Derives};
use crate::{ItemIdentifier, Location};

Expand Down Expand Up @@ -130,7 +130,12 @@ impl Config {
self.libraries.values().find(|lib| lib.krate == krate)
}

pub fn replace_typedef_name(&self, id: ItemIdentifier, is_cf: bool) -> ItemIdentifier {
pub fn replace_typedef_name(
&self,
id: ItemIdentifier,
is_cf: bool,
is_nw: bool,
) -> ItemIdentifier {
let library_config = self.library(&id);
id.map_name(|name| {
library_config
Expand All @@ -152,7 +157,12 @@ impl Config {
// We'll have to manually keep the name of those in
// translation-config.toml.
if is_cf {
cf_no_ref(&name).to_string()
strip_needless_suffix(&name).to_string()
} else if is_nw {
// Rename network types to match Swift's naming scheme.
strip_needless_suffix(&name)
.to_upper_camel_case()
.replace("Nw", "NW")
} else {
name
}
Expand Down
20 changes: 20 additions & 0 deletions crates/header-translator/src/global_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,29 @@ fn update_module(
)
};

// Replace the first argument with self when translating standalone functions into instance methods
//
// nw_txt_record_t nw_txt_record_copy(nw_txt_record_t txt_record);
//
// To:
//
// impl NWTxtRecord {
// fn copy(&self) -> NWRetained<NWTxtRecord>;
// }
//
if id.library_name() == "Network"
&& arguments
.first()
.and_then(|(_, first_arg_ty)| first_arg_ty.implementable())
.is_some_and(|v| v == cf_item)
{
*first_arg_is_self = true;
}

// TODO(breaking): Remove in next version
if body.is_none()
&& id.library_name() != "Dispatch"
&& id.library_name() != "Network"
&& !link_name.contains("GetTypeID")
{
deprecated_fns.push(Stmt::FnDecl {
Expand Down
7 changes: 7 additions & 0 deletions crates/header-translator/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,13 @@ impl ItemTree {
})
}

pub fn network(name: impl Into<String>) -> Self {
Self::from_id(ItemIdentifier {
name: name.into(),
location: Location::new("Network"),
})
}

pub fn cf_string_macro() -> Self {
Self::from_id(ItemIdentifier {
name: "cf_string".into(),
Expand Down
119 changes: 106 additions & 13 deletions crates/header-translator/src/name_translation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,9 @@ pub(crate) fn enum_prefix<'a>(
ep
}

pub(crate) fn cf_no_ref(type_name: &str) -> &str {
type_name.strip_suffix("Ref").unwrap_or(type_name)
pub(crate) fn strip_needless_suffix(type_name: &str) -> &str {
let type_name = type_name.strip_suffix("Ref").unwrap_or(type_name);
type_name.strip_suffix("_t").unwrap_or(type_name)
}

/// Find the type onto whom a function should be inserted.
Expand All @@ -266,8 +267,71 @@ pub(crate) fn find_fn_implementor(
}
}

// Certain functions are mistakenly identified as constructors.
//
// For example:
//
// nw_txt_record_find_key_t nw_txt_record_find_key(nw_txt_record_t)
//
// This should be treated as an instance method of NWTxtRecord instead
//
// todo: needs some system
const nw_instance_methods: [&str; 46] = [
"nw_protocol_copy_ws_definition",
"nw_quic_get_application_error",
"nw_quic_get_application_error_reason",
"nw_quic_get_keepalive_interval",
"nw_quic_get_local_max_streams_bidirectional",
"nw_quic_get_local_max_streams_unidirectional",
"nw_quic_get_remote_idle_timeout",
"nw_quic_get_remote_max_streams_bidirectional",
"nw_quic_get_remote_max_streams_unidirectional",
"nw_quic_get_stream_application_error",
"nw_quic_get_stream_id",
"nw_quic_get_stream_type",
"nw_quic_get_stream_usable_datagram_frame_size",
"nw_quic_set_application_error",
"nw_quic_set_keepalive_interval",
"nw_quic_set_local_max_streams_bidirectional",
"nw_quic_set_local_max_streams_unidirectional",
"nw_quic_set_stream_application_error",
"nw_tcp_options_set_enable_keepalive",
"nw_tcp_options_set_keepalive_count",
"nw_tcp_options_set_keepalive_idle_time",
"nw_tcp_options_set_keepalive_interval",
"nw_tcp_options_set_no_delay",
"nw_tcp_options_set_no_options",
"nw_tcp_options_set_no_push",
"nw_txt_record_access_bytes",
"nw_txt_record_access_key",
"nw_txt_record_find_key",
"nw_udp_options_set_prefer_no_checksum",
"nw_ws_metadata_copy_server_response",
"nw_ws_metadata_get_close_code",
"nw_ws_metadata_get_opcode",
"nw_ws_metadata_set_close_code",
"nw_ws_metadata_set_pong_handler",
"nw_ws_options_add_additional_header",
"nw_ws_options_add_subprotocol",
"nw_ws_options_set_auto_reply_ping",
"nw_ws_options_set_client_request_handler",
"nw_ws_options_set_maximum_message_size",
"nw_ws_options_set_skip_handshake",
"nw_ws_request_enumerate_additional_headers",
"nw_ws_request_enumerate_subprotocols",
"nw_ws_response_add_additional_header",
"nw_ws_response_enumerate_additional_headers",
"nw_ws_response_get_selected_subprotocol",
"nw_ws_response_get_status",
];
if fn_location.library_name() == "Network" && nw_instance_methods.contains(&fn_name) {
if let Some(item) = first_arg_ty.implementable() {
return Some(item);
}
}

if let Some(item) = first_arg_ty.implementable() {
let type_name = cf_no_ref(&item.id().name).replace("Mutable", "");
let type_name = strip_needless_suffix(&item.id().name).replace("Mutable", "");
if is_method_candidate(fn_name, &type_name) {
// Only emit if in same crate (otherwise it requires a helper trait).
if fn_location.library_name() == item.id().library_name() {
Expand All @@ -281,7 +345,7 @@ pub(crate) fn find_fn_implementor(
if let Some(item) = result_type.implementable() {
// Allowing this means that things like `CGPathCreateMutableCopy`
// are considered part of `CFMutablePath`.
let type_name = cf_no_ref(&item.id().name).replace("Mutable", "");
let type_name = strip_needless_suffix(&item.id().name).replace("Mutable", "");

if is_method_candidate(fn_name, &type_name) {
// Only emit if in same crate (otherwise it requires a helper trait).
Expand Down Expand Up @@ -321,7 +385,7 @@ pub(crate) fn find_fn_implementor(
if fn_name.contains("CTFontManager") {
continue;
}
if is_method_candidate(fn_name, cf_no_ref(&item.id().name)) {
if is_method_candidate(fn_name, strip_needless_suffix(&item.id().name)) {
candidates.push(item.clone());
}
}
Expand Down Expand Up @@ -357,28 +421,40 @@ pub(crate) fn cf_fn_name(
omit_memory_management_words: bool,
) -> String {
let is_mutable = type_name.contains("Mutable");
let type_name = cf_no_ref(type_name).replace("Mutable", "");
let type_name = strip_needless_suffix(type_name).replace("Mutable", "");

debug_assert!(is_method_candidate(fn_name, &type_name));

let mut type_words = lowercase_words(&type_name);
// Hack: NWWS in network framework is not properly parsed into two words
let temp_type_name = if type_name.starts_with("NWWs") {
type_name.replace("NWWs", "NwWs")
} else {
type_name
};

let mut type_words = lowercase_words(&temp_type_name);
let mut words = lowercase_words(fn_name)
.skip_while(|fn_word| {
if let Some(type_word) = type_words.next() {
assert_eq!(*fn_word, type_word);
true
// This can fail for various types:
//
// type_name: nw_protocol_metadata
// fn_name: nw_quic_get_stream_id
//
//assert_eq!(*fn_word, type_word);
*fn_word == type_word
} else {
false
}
})
.collect::<VecDeque<_>>();

if type_words.count() != 0 {
panic!("function name must prefix type: {fn_name:?}, {type_name:?}");
}
// if type_words.count() != 0 {
// panic!("function name must prefix type: {fn_name:?}, {type_name:?}");
// }

if words.is_empty() {
return "".to_string();
return "new".to_owned();
}

// Keep "create" and "copy" if needed for the user to be able to determine
Expand Down Expand Up @@ -667,6 +743,23 @@ mod tests {
check("FooBar", "FooRef", "bar");
check("FooBar", "MutableFooRef", "bar");

check("sec_trust_create", "SecTrust", "new");
check("sec_trust_create", "SecTrustRef", "new");
assert_eq!(
cf_fn_name("sec_trust_create", "SecTrustRef", true, true),
"new"
);

assert_eq!(
cf_fn_name(
"nw_txt_record_find_key",
"nw_txt_record_find_key_t",
true,
true
),
"new"
);

// check("AbcDef", "AbcDef", "");
// check("Ac", "Bc", None);
}
Expand Down
Loading