-
Notifications
You must be signed in to change notification settings - Fork 696
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for 'unsafe fields' #20
Conversation
Thanks for the pull request, and welcome! The Servo team is excited to review your changes, and you should hear from @emilio (or someone else) soon. |
An example diff when run on Stylo: diff --git a/ports/geckolib/gecko_bindings/structs_debug.rs b/ports/geckolib/gecko_bindings/structs_debug.rs
index e0763c1..f641604 100644
--- a/ports/geckolib/gecko_bindings/structs_debug.rs
+++ b/ports/geckolib/gecko_bindings/structs_debug.rs
@@ -4908,8 +4908,22 @@ fn bindgen_test_layout_nsStyleCoord() {
#[repr(C)]
#[derive(Debug)]
pub struct nsStyleSides {
- pub mUnits: [nsStyleUnit; 4usize],
- pub mValues: [nsStyleUnion; 4usize],
+ mUnits: [nsStyleUnit; 4usize],
+ mValues: [nsStyleUnion; 4usize],
+}
+impl nsStyleSides {
+ #[inline]
+ pub unsafe fn get_mUnits(&self) -> &[nsStyleUnit; 4usize] { &self.mUnits }
+ pub unsafe fn get_mUnits_mut(&mut self) -> &mut [nsStyleUnit; 4usize] {
+ &mut self.mUnits
+ }
+ #[inline]
+ pub unsafe fn get_mValues(&self) -> &[nsStyleUnion; 4usize] {
+ &self.mValues
+ }
+ pub unsafe fn get_mValues_mut(&mut self) -> &mut [nsStyleUnion; 4usize] {
+ &mut self.mValues
+ }
}
#[test]
fn bindgen_test_layout_nsStyleSides() {
@@ -4924,8 +4938,22 @@ fn bindgen_test_layout_nsStyleSides() {
#[repr(C)]
#[derive(Debug)]
pub struct nsStyleCorners {
- pub mUnits: [nsStyleUnit; 8usize],
- pub mValues: [nsStyleUnion; 8usize],
+ mUnits: [nsStyleUnit; 8usize],
+ mValues: [nsStyleUnion; 8usize],
+}
+impl nsStyleCorners {
+ #[inline]
+ pub unsafe fn get_mUnits(&self) -> &[nsStyleUnit; 8usize] { &self.mUnits }
+ pub unsafe fn get_mUnits_mut(&mut self) -> &mut [nsStyleUnit; 8usize] {
+ &mut self.mUnits
+ }
+ #[inline]
+ pub unsafe fn get_mValues(&self) -> &[nsStyleUnion; 8usize] {
+ &self.mValues
+ }
+ pub unsafe fn get_mValues_mut(&mut self) -> &mut [nsStyleUnion; 8usize] {
+ &mut self.mValues
+ }
}
#[test]
fn bindgen_test_layout_nsStyleCorners() {
diff --git a/ports/geckolib/gecko_bindings/structs_release.rs b/ports/geckolib/gecko_bindings/structs_release.rs
index 16d9046..e6c7af5 100644
--- a/ports/geckolib/gecko_bindings/structs_release.rs
+++ b/ports/geckolib/gecko_bindings/structs_release.rs
@@ -4887,8 +4887,22 @@ fn bindgen_test_layout_nsStyleCoord() {
#[repr(C)]
#[derive(Debug)]
pub struct nsStyleSides {
- pub mUnits: [nsStyleUnit; 4usize],
- pub mValues: [nsStyleUnion; 4usize],
+ mUnits: [nsStyleUnit; 4usize],
+ mValues: [nsStyleUnion; 4usize],
+}
+impl nsStyleSides {
+ #[inline]
+ pub unsafe fn get_mUnits(&self) -> &[nsStyleUnit; 4usize] { &self.mUnits }
+ pub unsafe fn get_mUnits_mut(&mut self) -> &mut [nsStyleUnit; 4usize] {
+ &mut self.mUnits
+ }
+ #[inline]
+ pub unsafe fn get_mValues(&self) -> &[nsStyleUnion; 4usize] {
+ &self.mValues
+ }
+ pub unsafe fn get_mValues_mut(&mut self) -> &mut [nsStyleUnion; 4usize] {
+ &mut self.mValues
+ }
}
#[test]
fn bindgen_test_layout_nsStyleSides() {
@@ -4903,8 +4917,22 @@ fn bindgen_test_layout_nsStyleSides() {
#[repr(C)]
#[derive(Debug)]
pub struct nsStyleCorners {
- pub mUnits: [nsStyleUnit; 8usize],
- pub mValues: [nsStyleUnion; 8usize],
+ mUnits: [nsStyleUnit; 8usize],
+ mValues: [nsStyleUnion; 8usize],
+}
+impl nsStyleCorners {
+ #[inline]
+ pub unsafe fn get_mUnits(&self) -> &[nsStyleUnit; 8usize] { &self.mUnits }
+ pub unsafe fn get_mUnits_mut(&mut self) -> &mut [nsStyleUnit; 8usize] {
+ &mut self.mUnits
+ }
+ #[inline]
+ pub unsafe fn get_mValues(&self) -> &[nsStyleUnion; 8usize] {
+ &self.mValues
+ }
+ pub unsafe fn get_mValues_mut(&mut self) -> &mut [nsStyleUnion; 8usize] {
+ &mut self.mValues
+ }
}
#[test]
fn bindgen_test_layout_nsStyleCorners() {
diff --git a/ports/geckolib/gecko_bindings/tools/regen.py b/ports/geckolib/gecko_bindings/tools/regen.py
index 4c3d4e5..c7fd581 100755
--- a/ports/geckolib/gecko_bindings/tools/regen.py
+++ b/ports/geckolib/gecko_bindings/tools/regen.py
@@ -94,7 +94,8 @@ COMPILATION_TARGETS = {
"imgRequestProxy", "imgRequestProxyStatic", "CounterStyleManager",
"ImageValue", "URLValue", "URLValueData", "nsIPrincipal",
"nsDataHashtable", "imgIRequest"
- ]
+ ],
+ "unsafe_field_types": ["nsStyleUnion", "nsStyleUnit"],
},
# Generation of the ffi bindings.
"bindings": {
@@ -130,7 +131,7 @@ COMPILATION_TARGETS = {
],
"void_types": [
"nsINode", "nsIDocument", "nsIPrincipal", "nsIURI",
- ]
+ ],
}
}
@@ -253,6 +254,11 @@ def build(objdir, target_name, kind_name=None,
flags.append("-match")
flags.append(header.format(objdir))
+ if "unsafe_field_types" in current_target:
+ for ty in current_target["unsafe_field_types"]:
+ flags.append("-unsafe-field-type")
+ flags.append(ty.format(objdir))
+
if "blacklist" in current_target:
for ty in current_target["blacklist"]:
flags.append("-blacklist-type") |
Hmm... I don't feel like there's a clear use case for this. I mean... This only changes those fields to have accesors, but I feel the approach is flaky in some sense (I mean, doing it type-based instead of on individual fields). I... don't like that much this approach, but I'd love to know the opinion of other people... @nox? |
Could do it on the basis of individual fields too. Usually unsafe fields are ones with additional invariants, and that is often a property of the field's type. But not always. Perhaps having it blacklist type,fieldname pairs makes more sense. |
Are these fields actually public on the Gecko's side to begin with? |
Yes. But C++ is very #yolo about safety 😄. And there's no safe way to encapsulate/represent a tagged union without using things like |
fb65a21
to
ee9fec6
Compare
I changed it so that you specify type/field name pairs instead of just types. |
@nox, yes, all fields are public. @Manishearth the intrinsic problem I see with this approach, is that you can still access them the same way (only with a getter instead of a direct field access). Making them definitely private is not feasible, because then you can't build on top of them, but making them private with a public accessor seems like a extra layer of complexity that I'm not sure it's worth it. My point is, the only advantage of doing this is you are forced to write a unsafe block over the getter, but given that the code that has to deal with these structs is also mostly unsafe, I'm not sure it's a win in real benefit vs. maintainability of bindgen and the generated bindings (i.e., it's more code we have to migrate each time rustc breaks libsyntax, plus more flags to keep in sync in the bindgen scripts). I don't really want to make a final decision without knowing the opinion of @nox, and also of @bholley, because my opinion on the utility of this could very well be wrong. |
@nox, and with "all fields are public", I mean those fields are public within the geckolib crate, though it's not exposed anywhere else. |
@emilio The point is that it lets us write completely-safe abstraction layers around these structs. Right now, the whole CoordData layer can be completely bypassed by directly writing to mUnit and mValue. Indeed, we actually do that for z-index (which I will fix). With this approach, the CoordData bindings will use the unsafe methods to get fields, and the rest of the stylo code will never directly touch these fields.
The benefit is that you can't accidentally modify these fields from non-sugar code. |
FWIW, modifying And don't take me wrong, I agree it's a somewhat useful feature, but I think it's would not pull its weight in terms of real benefit vs maintainability of the codebase in general. Anyway, I really wouldn't like to make the final decision on this, because my preference is not that strong. Ideally, I'd like this to be at parse level, so we depend less on libsyntax, and not add a fixed cost to it, but (with my limited knowledge of the visitor API libsyntax exposes) looks good to me otherwise (except I don't see the field identifier check, only the type name). |
Not mUnit though. Nothing stops me from setting mUnit to Calc and watching the segfaults rain down upon us 😄 I removed the visitor thing btw, now it's a simple string comparison. The visitor was necessary because it used to be "all fields containing things which contain this type", now it's simply "tell me which type/field pairs to privatize" |
ee9fec6
to
f6b4c11
Compare
IRC discussions always solve problems: http://logs.glob.uno/?c=mozilla%23servo&s=22+Jul+2016&e=22+Jul+2016#c486112 TLDR:
|
f6b4c11
to
4c2f588
Compare
Made it use a div. Haven't written a test yet. |
I think it would also be useful to be able to specify that certain types and fields don't have getters and/or setters at all. For example, annotating a struct as having private members by default, and then annotating specific methods as having a getter and/or setter. We could do this for nsIAtom, making the only accessor a getter for mHash. This would avoid the need for @SimonSapin to use a somewhat-confusing newtype there. |
57bb6df
to
a3ea659
Compare
Updated. I also add an explicit passthrough of the DYLD_LIBRARY_PATH path in the python file, since make on my machine unsets it. @SimonSapin does this look more in line with what you need? Privacy and accessors are now independent, and you can control both the safety of the accessors and whether or not the setter (mutable accessor) exists. |
@@ -122,6 +122,7 @@ fn decl_name(ctx: &mut ClangParserCtx, cursor: &Cursor) -> Global { | |||
CXCursor_ClassTemplate | | |||
CXCursor_ClassDecl | | |||
CXCursor_StructDecl => { | |||
let anno = Annotations::new(&cursor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... This is for nested structs right? Can you add a test for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is for annotating the entire struct with something. See the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm... I'm pretty sure we parse that at another place. huh. I'll take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's visit_top, which AFAICT is only called for toplevel decls. decl_name otoh is called for all decls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only thing visit_top
shouldn't visit (for a class/struct/union decl), are nested structs, when we directly call visit_composite
IIRC.
So I'm fine with moving it here, but there's no point in parsing it twice, that's what I wanted to say. Nontheless, I think that would bring a bit of churn here right? I'd be satisfied if you file an issue so I can refactor that when I have the time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #26
Seems like you didn't update all the expectations changed (the Can you please add a test for the |
d541afb
to
33fa6ef
Compare
Replace works. The failures seem to be in unrelated tests and happen on master too. |
Huh, seems like the tests results are different locally. |
a3dec17
to
0fcf367
Compare
@@ -42,6 +42,7 @@ script: | |||
- cargo build --verbose --features llvm_stable | |||
- make test | |||
- git add -A | |||
- git diff @ | |||
- git diff-index --quiet HEAD |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do the same removing the --quiet here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, git diff-index only gives an error on empty diffs if run with --quiet.
(It also outputs object ids, not diffs)
r=me if the difference between travis and your local computer isn't huge or related to this. |
@bors-servo r+ It's unrelated. |
📌 Commit 0fcf367 has been approved by |
Add support for 'unsafe fields' Needed for the next step of servo/servo#12521 These are fields which are private but have unsafe accessor functions. Since we generate bindings in a separate module, we can't touch private fields from other parts of the module (an alternative is to inject a footer with these private impls). `pub(restricted)` exists, but is not stable. r? @emilio
@bors-servo r=emilio |
💡 This pull request was already approved, no need to approve it again.
|
📌 Commit 0fcf367 has been approved by |
☀️ Test successful - travis |
Needed for the next step of servo/servo#12521
These are fields which are private but have unsafe accessor functions. Since we generate bindings in a separate module, we can't touch private fields from other parts of the module (an alternative is to inject a footer with these private impls).
pub(restricted)
exists, but is not stable.r? @emilio