ssheldon / rust-objc Goto Github PK
View Code? Open in Web Editor NEWObjective-C Runtime bindings and wrapper for Rust.
Home Page: https://crates.io/crates/objc
License: MIT License
Objective-C Runtime bindings and wrapper for Rust.
Home Page: https://crates.io/crates/objc
License: MIT License
With #25 I put in the work to get the tests working on iOS, but since they aren't run automatically by Travis they don't get run that often.
Running these tests on Travis would be tricky, since we need a Rust compiler that supports iOS. This might have to wait until there's easier cross-compilation support by just downloading the standard library for iOS, which was mentioned as goal for 2016:
We’re shooting for push-button cross-compiles. The idea is that compiling Rust code for another target should be easy:
- Download a precompiled version of
libstd
for the target in question, if you don’t already have it.- Execute
cargo build --target=foo
.
Hi,
I'm making progress on setting up a travis build job, and it promptly discovered a few warnings when building rust-objc with rust beta or nightly:
src/runtime.rs:108:38: 108:43 warning: found non-foreign-function-safe member in struct marked #[repr(C)]: found zero-size struct in foreign module, consider adding a member to this struct, #[warn(improper_ctypes)] on by default
src/runtime.rs:108 pub fn class_getName(cls: *const Class) -> *const c_char;
^~~~~
That's caused by PrivateMarker
being a unit struct. So it affects Class
, Object
, Ivar
, and Method
. Not sure what to do here. It's not really a dangerous thing since you only ever manipulate pointers to these structs, but it would still be nice to have this handled properly.
There used to be an IdSlice
trait for doing this, but it was removed in 4118e96. This isn't possible until rust-lang/rust#5016 is completed, because until then an Id
is a different size than a reference.
It would be better if selectors were resolved at compile time instead of using sel_registerName
IntoMethodImp::method_encoding
returns a String
, but it should return something more strongly typed. Perhaps an iterator over the Encodings of its arguments or a boxed slice of them?
This crate is mainly just a wrapper around the objc runtime dylib, so it shouldn't need much else. If we can make it no_std
then it might be usable in more places, perhaps even in the rust compiler itself.
The current std dependencies that aren't part of libcore are:
&str
via CString
in the runtime module and the declare moduleCStr
in the runtime module and for Encoding
String
in Encoding
to store dynamically-sized, runtime defined type encodingsString
when declaring methods to represent their type encodings in the declare moduleString
in the representation of MessageError
Error
trait for MessageError
std::os::raw
types instead of the libc
types in the runtime module and for implementing the Encode
traitCurrently, the extern function implementing a method in the method!
macro uses the first part of the selector as its name. Unfortunately, if two extern functions anywhere have the same name, rustc crashes. This is described in rust-lang/rust#16403, which was closed as a duplicate of rust-lang/rust#10883, and the discussion there seems to indicate it's an expected error and won't be fixed.
What license(s) is this work released under?
It seems rust-objc's msg_send!
doesn't let you call methods with unnamed parameters.
For example, CIImage
has the following methods:
- (instancetype)initWithImageProvider:(id)p size:(size_t)width :(size_t)height format:(CIFormat)f colorSpace:(CGColorSpaceRef)cs options:(NSDictionary<NSString *,id> *)options;
+ (CIImage *)imageWithImageProvider:(id)p size:(size_t)width :(size_t)height format:(CIFormat)f colorSpace:(CGColorSpaceRef)cs options:(NSDictionary<NSString *,id> *)options;
CAMediaTimingFunction
has the following methods:
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
The 3rd parameter of each of the CIImage
methods, and the 2nd, 3rd and 4th parameters of the CAMediaTimingFunction
methods don't have a label (even though they have an internal name). msg_send!
won't let you call such methods.
The type Id<Object>
implements Deref
and DerefMut
, returning references of type Object
. Object
is defined as an opaque empty enum:
pub struct Object {
_priv: PrivateMarker,
}
enum PrivateMarker { }
This is unsound, and may make rustc produce wildly different results in the future. At no point should anyone have a reference to a type that is uninhabited.
The following methods return an encoding but don't use the Encoding
struct:
Ivar::type_encoding
Method::return_type
Method::argument_type
The EncodePtr
trait may not be necessary. Currently, its main purpose is to express that reference/pointers to any NSObject may be encoded. This can partially be described using where clauses, like where *mut Self: Encode
.
Unfortunately, this doesn't allow expressing where &'a Self: Encode
because the lifetime is unconstrained, but working around this by casting to a pointer might not be so bad.
The main issue with this is rust-lang/rust#20671; even if a where clause is added to the INSObject trait, it doesn't propagate to subclasses.
The owned Id
struct should implement DerefMut
so that its object can be mutated. However, when DerefMut
is implemented, dereferencing to &mut
is always chosen over &
, which breaks many uses of Id
, so this should wait for rust-lang/rust#12825.
Implementing Message
doesn't require actually implementing anything, and then implementing INSObject
doesn't require any unsafe code. Safe code therefore can implement INSObject
for types that it shouldn't and invoke undefined behavior by calling the INSObject
methods.
impl Message for uint { }
impl INSObject for uint {
fn class_name() -> ClassName<uint> {
ClassName("uint")
}
}
fn main() {
let x = 0u;
let hash_code = x.hash_code();
}
It is very convenient to expose a C# class to objc by using attributes. (Usage: https://developer.xamarin.com/guides/ios/advanced_topics/registrar/ Implement: https://github.com/mono/cocoa-sharp/) .
Rust has the same attribute system like C#. So is it possible to have these attributes in Rust?
One of the benefits or needs is to use solid type instead of marked type connect between Rust and objc, and to wrap Rust struct method instead of static method for objc message receiving.
cocoa-sharp has these tricks.
encode::<*const Class>
gives @
, but it should be #
. This happens because a Class
is Message
, and any Message
pointer is encoded as @
.
Implementing Encode
for structs that are a composition of other Encode
types is hard because the output must be static.
It might also be good to make a new type for the return value of Encode
so that future abstractions can be built around it.
Reported by @bvssvni at rust-windowing/glutin#359, compiling on the 2015-04-03 nightly causes an ICE:
src/declare.rs:138:21: 138:67 error: internal compiler error: coherence failed to report ambiguity: cannot locate the impl of the trait `core::marker::MarkerTrait` for the type `<F as declare::IntoMethodImp>::Ret`
src/declare.rs:138 let mut types = <<F as IntoMethodImp>::Ret as Encode>::encode().as_str().to_string();
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: run with `RUST_BACKTRACE=1` for a backtrace
thread 'rustc' panicked at 'Box<Any>', /Users/rustbuild/src/rust-buildbot/slave/nightly-dist-rustc-mac/build/src/libsyntax/diagnostic.rs:130
stack backtrace:
1: 0x111246987 - sys::backtrace::write::h8872f4f658a62d46iJC
2: 0x1112748a3 - panicking::on_panic::h845201cbea1c5a54uxI
3: 0x111198cee - rt::unwind::begin_unwind_inner::hfc7848f7429ac5d45eI
4: 0x1109711ae - rt::unwind::begin_unwind::h396107918539692450
5: 0x11097115b - diagnostic::SpanHandler::span_bug::hc2e54b04fea5078eYmB
6: 0x10e42d836 - middle::traits::error_reporting::report_fulfillment_error::hded3e0d571682b65FQN
7: 0x10e299c01 - middle::traits::error_reporting::report_fulfillment_errors::hd4a5143b2dcf3b1bVPN
8: 0x10d7dc432 - check::vtable::select_all_fcx_obligations_or_error::h68378d314e6e162eX1b
9: 0x10d89b010 - check::check_bare_fn::hcbfa2ceb51ac809fvPn
10: 0x10d894942 - check::check_item::hcd4af901dd683671g8n
11: 0x10d8992f2 - visit::walk_item::h14171734647403984035
12: 0x10d970751 - check_crate::closure.36252
13: 0x10d96ad17 - check_crate::h34c23998ac3af75d1oC
14: 0x10d68a2e5 - driver::phase_3_run_analysis_passes::h1e90d1b6a6af8247gGa
15: 0x10d66ea15 - driver::compile_input::h43156fddb0e7acceQba
16: 0x10d72c985 - run_compiler::hf5bcc2a2a9004c41S4b
17: 0x10d72a152 - boxed::F.FnBox<A>::call_box::h9818158584254676665
18: 0x10d729647 - rt::unwind::try::try_fn::h13069897533439949939
19: 0x1112febe8 - rust_try_inner
20: 0x1112febd5 - rust_try
21: 0x10d72993a - boxed::F.FnBox<A>::call_box::h16467879214397932316
22: 0x11125dd8d - sys::thread::create::thread_start::h81aa05d82aaf9eebbeH
23: 0x7fff865d8267 - _pthread_body
24: 0x7fff865d81e4 - _pthread_start
Looks to be this ICE: rust-lang/rust#23853
Support for other architectures was added in e1a9133
..2e9192f
, so these architectures should also be tested.
This is difficult because Rust's test compile down to an executable, and arbitrary executables can't be run on iOS. rust-lang/rfcs#816 will likely be required to run the existing tests on iOS.
Encode
should be implemented for more types. For example, it currently doesn't support pointers to many value types; this could easily be added for all value types, except it'd conflict with the implementation for C strings.
Specialization, as described in rust-lang/rfcs#1210, would allow implementing this.
For example, the following code:
let mut a: Id<NSString> = INSString::from_str("a");
let mut b: Id<NSString> = INSString::from_str("b");
println!("{} {}", a, b);
mem::swap(a.deref_mut(), b.deref_mut());
println!("{} {}", a, b);
Expected output:
a b
b a
Actual output:
a b
a b
We cannot have the expected output happen, because an NSObject's memory address cannot change after it has been constructed.
Features for crates are usually used for optional functionality. GNUstep support is currently implemented as a feature, but that might not be the right way to think about it. Using GNUstep on Linux isn't really optional, and there doesn't seem to be a use case for using it on OSX when Apple's Objective-C runtime is available.
Perhaps this would be better handled as a configuration, not a feature. A build script could detect whether Apple's runtime is available or the GNUstep runtime and emit cargo:rustc-cfg=gnustep
accordingly. It's not clear to me how this detection should be done.
The Id
struct was moved into an external crate in 81a972f, but it seems fundamental enough to exist in this crate in some form.
It was removed because, although its design satisfied the requirements of my foundation wrapper, it isn't compatible with the cocoa crate. The cocoa crate is a lower-level wrapper that operates with raw pointers, so it expects objects as *mut Object
where the Id
struct could be dereferenced to &Object
.
The smart pointer should return with a design that will also be usable by cocoa and other libraries without requiring that users put a lot of effort into determining a safe interface for them.
Implementing derive for custom structs is tedious, but all the information it needs is in the declaration of the struct: the name, and the type and order of its members. We should be able to create a custom derive procedural macro to allow #[derive(Encode)]
.
The MessageArguments
trait could be reworked to only have one method: given an IMP
, id
, and SEL
, call the IMP
with yourself as arguments. This will work for messaging superclasses, as well, and might allow a clearer separation for GNUstep-specific logic.
Is this worth changing? Is it's purpose clear then? Seems a bit silly to have to pass an IMP
when, given the object and selector, the implementation can be looked up.
Currently primitive types have encodings that do not allocate (they're defined from static strings) but compositions like structs either have to jump through hoops and use different encoding strings for different architectures or accept the cost of allocating a string and pushing the encoding of their fields.
Is there a better way to do this, where encodings from structs could more simply be composed without allocating?
The encode!
macro should return an Encoding
without any unsafe blocks, instead of returning a str
like it currently does.
It currently allows creating some invalid encodings that would have to be fixed:
encode!() == ""
encode!(u8, u8) == "CC"
I believe that, currently, Object
, Class
, etc are Send
and Sync
because they don't contain any types to specify otherwise. Is this correct? At least, it seems like not all objects are Sync
.
Implementing #10 would currently require that any type used in a method argument must implement Encode
. This is difficult for cases like functions, whose ObjC type encoding is just '?' but it's hard to implement Encode
for all fn
s (because there's fn() -> R
, extern fn() -> R
, unsafe extern fn() -> R
, unsafe fn() -> R
, fn(A) -> R
, etc).
It'd be simplest if encode<fn()> == "?"
without implementing Encode
for fn()
, and then if Encode
isn't implemented when it should be, it would just be an error at runtime.
Currently, in the runtime and declare modules, the API takes &str
for ease of use. However, this requires copying the string into a buffer and adding a nul terminator byte. It might be desirable for some cases to have a way to pass in strings which are already known to be nul-terminated so that this allocation can be avoided.
probably msg_send macro is using type inference rules which will be removed in the next coming rustc.
my current rustc version is rustc 1.19.0-nightly (d015610db 2017-06-05)
complete warning is:
At runtime, msg_send!
could verify that the encoding of arguments and the return type matches those specified by the Objective-C runtime. This would help catch errors from mismatched types.
Adding a method to a ClassDecl
isn't ergonomic because you need to cast the function to a function pointer, like so:
add_method(sel!(setFoo:), my_obj_set_foo as extern fn(&mut Object, Sel, u32))
This is a bummer because you need to repeat the types that you've already specified.
This wasn't always the case for Rust, but this behavior changed in rust-lang/rust#19891. rust-lang/rust#21086 was opened about this, but was closed as expected behavior.
Hello,
I'm trying to add a selector for a menuitem command. Basically I have this:
let itemtitle = NSString::alloc(nil).init_str("PREFS");
let no_key = NSString::alloc(nil).init_str("");
let action = sel!(myhandler);
let item = NSMenuItem::alloc(nil).initWithTitle_action_keyEquivalent_(itemtitle, action, no_key);
menu.addItem_(item);
The menu works, but the item is disabled, which I think is because my object does not respond to the "myhandler" selector. I'm not quite sure how to use this API to add one. I tried implementing a custom object for the selector based on this example (https://github.com/SSheldon/rust-objc-foundation/blob/master/examples/custom_class.rs#L34), but it has no effect; I guess that is because I am defining the selector on "MYObject" and not the menu item. Is this kind of selector implementation supported by the library at the moment?
I'm working on a project that interops with Objective C and got stuck on figuring out how to pass a block [0] as a param to a method. As the comment in cocoa-rs
mentions, I am "unsure how to bind to blocks" [1]. I think the next step is looking at how PyObjC implements this and finding a solution for rust. I got as far as reading through the Objective C docs to realize it is an object, but unclear how it is constructed, or invoked. Any ideas or things I should read to try and implement this?
[0] https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html
[1] https://github.com/servo/cocoa-rs/blob/master/src/appkit.rs#L2265
Currently it's possible to create an Objective-C class and add methods to it without any unsafe code, but that seems like something that could invoke unsafe behavior.
PhantomFn
bounds were replaced with Sized
in ef565e4 as a way to remove PhantomFn
while still compiling on the beta. These Sized
bounds didn't impact anything but aren't actually necessary, so they should be removed from:
Encode
Message
Ownership
Declaring root classes can now be done since superclasses were made optional in 154d679. This might deserve a bit more thought:
+initialize
method to be present and will try to call it, should an empty one be provided by default? Should it be a required parameter when creating a root class?This issue is similar to #6, but since Block
isn't 0-sized it doesn't just cause unexpected behavior, it can actually cause undefined behavior by modifying the memory representation of a block.
let a: &mut Block<_, _> = ...;
let b: &mut Block<_, _> = ...;
mem::swap(a, b);
None of the modules are documented.
CGError CGWarpMouseCursorPosition ( CGPoint newCursorPosition );
I just want to execute this, with this library. Can you give me an example?
Currently, with the exception feature enabled, when an exception is thrown the panic starts from within the implementation of messaging. This should be raised to the macro, so that the first thing the user sees is which line of their code was the issue without having to re-run with backtraces on.
This might also be a good way to surface verification errors once they're finalized in #38.
I thought I saw objects being way too big when declared by this crate, with lots of empty space between each ivar. Maybe a log2
needs to be thrown at the alignment before declaring an ivar?
I had envisioned the Id
struct would own its object so you could get a mutable reference to the object, and also that NSArray
would own the objects it contains. However, this doesn't work if NSArray
also implements NSCopying
, because NSCopying
is a shallow copy. This means I could copy an NSArray
, turn them both into Vec<Id<T>>
, and then have two Id
s for the same object which would allow aliasing &mut
references.
Verifying argument types for messages was added back with #10. It should remain behind a feature, but before it ships a couple things should be done:
String
isn't a very rust-y error type.dee2e48#diff-00e732ca6ab3c3211cacd023da8d7b48L4 is not on crates.io yet, but I think it’s breaking for dependents like glutin who rely on the Encode trait being implemented for libc types. That’s OK for c_char which is just a type alias of u8 or i8, but not for c_void which is unfortunately a different type in std::os::raw and libc: rust-lang/libc#71
I’d recommend bringing back the libc dependency (unfortunate, but the alternative will be a pain) and implementing the trait for both c_void
types.
Currently, functions like INSArray
's into_vec
cannot be methods because they must consume an Id<Self>
. For example, into_vec
is currently written as:
fn into_vec(array: Id<Self>) -> ...
This means it must be called as INSArray::into_vec(array)
. Ideally, it'd be nice to write this as:
fn into_vec(self: Id<Self>) -> ...
Then it'd be possible to just call array.into_vec()
.
In the May 6th meetings notes, it was mentioned this was apparently possible in a patch at the time: https://github.com/rust-lang/meeting-minutes/blob/master/weekly-meetings/2014-05-06.md#boxself, but this functionality doesn't appear to have shipped.
IntoMethodImp::into_imp
can fail and currently just returns unit in that case; it should return an actual error describing mismatched arguments.
I'm writing a library where I would like to hide the use of objc in the implementation. In the library, I define a macro that internally uses msg_send!
and sel!
I've managed to hide most objc dependencies using $crate
references, but due to the way macro expansion works, I can't hide msg_send!
and sel!
, which means that the call site needs to have an extern crate objc
to use them.
Here is the call site for my macro (notice the extern crate objc earlier):
https://github.com/jmquigs/rs-barfly/blob/master/examples/simple/src/main.rs#l24
And here is the macro definition:
https://github.com/jmquigs/rs-barfly/blob/master/src/lib.rs#l170
(notice how it uses msg_send!
)
Is it feasible to export a send_msg()
function from objc so that msg_send!
can be bypassed? I looked at using "expanded" code directly, but quickly ran into dependencies on MessageArguments
and (worse) the associated platform modules. So this may be non-trivial to implement.
Thanks in advance
The EncodeArguments
trait was added in c6756b5. It seems generally useful since it can be used for declaring methods, too (045a49d), so it's worth stabilizing and making available even without the verify feature.
Some questions:
id
and SEL
as the first two encodings? Or should this be a more generic "encodable-tuple" trait?id
and SEL
are included, should it be named EncodeMessageArguments? To be explicit that this is what the encodings will be when these are used as arguments to a message.A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.