Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@gaaclarke
Copy link
Member

This is setting up the infrastructure to test the iOS AccessibilityBridge.

Related PR: #18164

@auto-assign auto-assign bot requested a review from gw280 May 11, 2020 20:33
@gaaclarke gaaclarke requested a review from goderbauer May 11, 2020 23:07
@gaaclarke
Copy link
Member Author

I threw a few extra tests onto this PR while I was waiting for a review since github doesn't handle dependent PRs very well. I'll refrain from adding more.

Copy link
Member

@chinmaygarde chinmaygarde left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AccessibilityBridge stuff itself is great. Blocking for the ARC stuff for now. Feel free to isolate that out into its own patch and lets go ahead with just the AB.

explicit scoped_nsprotocol(NST object = nil) : object_(object) {}

scoped_nsprotocol(const scoped_nsprotocol<NST>& that) : object_([that.object_ retain]) {}
scoped_nsprotocol(const scoped_nsprotocol<NST>& that)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handling of inclusion from arc and non-arc TUs is unrelated to AccessibilityBridge. Can you separate this out into its own patch and add a unit-test (in the fml_unittests harness) that ensures objects are correctly released. Crucially, I am not able to fully reason about the correctness of what happens when say an ARC TU vends an API to a non-ARC TU using this header and vice-versa.

cc @stuartmorgan who has experience with ARC migrations.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it is not unrelated, it is necessary for this test to function. Without it code that includes accessibility_bridge.h won't compile under ARC and OCMock breaks without ARC.

I could refactor the bridge to use the pimpl pattern but I think the right move is just to fix scoped_nsobject so we fix it everywhere once.

I can add a test that shows this works.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like there isn't any tests for scoped_nsobject. Here is sample code that shows it works: https://gist.github.com/gaaclarke/27aba0a4952f36591d05f164993ba95f

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately it is not unrelated, it is necessary for this test to function.

Can you clarify? I am not sure why the test cannot use manual reference counting in the test and compile the OCMock TUs with ARC. I'd like to understand why that causes an issue because I am afraid of introducing exactly the same kinds of issues here in the engine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. In my experience OCMock has problems in manually reference counted code. I tried to find reference to the problems but couldn't pull anything up. What it comes down to is that ARC has been adopted by the majority of developers and OCMock is doing funky things to work. It just isn't as tested for manual reference counting. I wouldn't consider it a concern for ARC adoption. It is only an issue because of the odd nature of mocking libraries.

This change doesn't introduce ARC to the engine. The engine will execute exactly the same. It just allows us to continue writing our unit tests in ARC. You'll have to take my word that OCMock works better with ARC. This PR just makes our code ARC compatible, which is a positive move.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is still concern I added a unit-test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you looked at Chromium's current scoped_ns* implementations? Getting it right for cross-mode TUs was a nightmare, involving several iterations that turned out to have subtle undefined behavior bugs (once due to a non-obvious ODR violation, and once due to what eventually turned out to be a compiler bug IIRC).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://chromium.googlesource.com/chromium/src/base/+/master/mac/scoped_nsobject.h#49

Thanks Stuart, they use an extra template parameter which allows you to supply retain and release implementations (presumably they pass in noops in ARC code).

This PR is slightly different. I first tried to to make scoped_nsobject work for ARC and non-ARC instances and ran into weirdness. I suspect it was due to ARC's usage of the autorelease pool so that deallocation time isn't deterministic.

This doesn't have that problem because it just wraps objc message calls in c functions. I didn't try to make scoped_nsobject usable with ARC instances. In fact you get a linker error if you try to do that. I'm only interested in letting ARC instances use objects that have scoped_nsobjects. If I'm writing ARC C++ code I won't need scoped_nsobject, I can just rely on ARC. The fact that this works is exercised in the new unit test I added.

As far as concerns about weirdness, this just wraps objc messages in c functions, otherwise behaves the same. The PR passes integration tests and unit tests. Since the only ARC code is unit tests, the worse case scenario is that our tests will flake or crash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm only interested in letting ARC instances use objects that have scoped_nsobjects.

That's what we were doing too, and we hit UB trying.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have any documentation on what didn't work previously so we can see exactly what was tried and what went wrong? Right now we have reason and empirical evidence that this works versus anecdotal evidence that something like it didn't work in a different context.

I appreciate the perspective. The stakes are low here since this only affects testing targets. Let's not hold back progress for a hypothetical, let's try to pin down the actual problem if one exists.

@@ -0,0 +1,130 @@
#import <XCTest/XCTest.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standard Flutter license verbiage.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@gaaclarke gaaclarke requested a review from chinmaygarde May 12, 2020 18:20
@gaaclarke
Copy link
Member Author

This change to scoped_nsobject is breaking scenario_app tests. I'm a bit baffled why. They are using ARC but never including scoped_nsobject directly so I would have expected an identical binary to be produced. I'll go back to the drawing board.

@gaaclarke
Copy link
Member Author

@chinmaygarde Okay, I rewrote it and I think you'll find this more agreeable. Now scoped_nsobject can be see by ARC code, but it can't be used by ARC maintained objects (that will result in a linking error). The change is very straightforward and the scenario_app and unit tests pass locally.

@chinmaygarde
Copy link
Member

Taking a closer look. I think you will agree that the scope of this patch expands well beyond just the iOS accessibility bridge. Unless technically unfeasible, please use MRC in the unit-test and lets land this ASAP.

@gaaclarke
Copy link
Member Author

@chinmaygarde Thanks. I think you'll be OK with this change.

It's not that MRC is technically impossible (it might be depending on how OCMock is being used). Transitioning to MRC is more work and we end up in an inferior position with respect to testing stability, consistency, convenience and compatibility. All of our iOS unit tests are ARC. It will be confusing for contributors to have some tests written one way and some tests another. Even if OCMock works with the subset of usages I'm using today, someone may have to rewrite it in ARC when they want to use a particular feature of OCMock.

@gaaclarke gaaclarke requested a review from jmagman May 12, 2020 23:54
@gaaclarke
Copy link
Member Author

@jmagman Hey Jenn, can you take a look at this when you get a chance. @chinmaygarde and I would like you to double check this, thanks!

* NO for isAccessibilityElement).
*/
@interface SemanticsObjectContainer : UIAccessibilityElement
- (instancetype)init __attribute__((unavailable("Use initWithSemanticsObject instead")));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NS_UNAVAILABLE

Also need to mark initWithAccessibilityContainer and +new unavailable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 40 to 44
id ObjcRetain(id object);

id ObjcAutorelease(id object);

void ObjcRelease(id object);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why these exist?

Copy link
Member Author

@gaaclarke gaaclarke May 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. It's a bit of an oddity, a collision between how C++ macros templates work and ARC. Get ready for a long explanation.

The goal of this PR is to test the C++ class flutter::AccessibilityBridge, that class has instance variables of type fml::scoped_nsobject. The way C++ works is that it must declare its instance variables in the header so that the size of the object is known. fml::scoped_nsobject is a template class which will compile for each compilation unit the instances that it finds are actually used, when it is used. They aren't precompiled and may actually result in no code getting generated if they are never used.

So, fml::scoped_nsobject is just a RAII wrapper around objective-c manual reference counting. The problem is that when we compile accessibility_bridge_test.mm (which uses ARC like all our tests), it includes accessibility_bridge.h, which includes scoped_nsobject.h, which uses manual reference counting methods. This creates an error when compiling accessibility_bridge_test.mm because it is compiled under ARC and can't deal with manual reference counting methods.

So, this PR wraps up the manual reference counting methods in C functions whose declaration is known to clients and users of the fml::scoped_nsobject, but whose actual definition is only known to the fml library, effectively hiding the forbidden methods and guaranteeing that the fml::scoped_nsobject will behave the same in an ARC or non-ARC compilation unit.

Long story short, If you remove these functions the test won't compile.

If you try to use an ARC managed Objective-C class with fml::scoped_nsobject you get a linker error because it won't be able to find the retain and release methods on that class. This is how my second approach is different than my first, I tried to make fml::scoped_nsobject work for both type of objects (ARC and non-ARC) which didn't work out.

@gaaclarke gaaclarke requested a review from jmagman May 13, 2020 20:36
@gaaclarke
Copy link
Member Author

At @cbracken 's request I split out #18362.

@gaaclarke gaaclarke force-pushed the accessibility_bridge_test branch from 5fa81c3 to 34a3b39 Compare May 14, 2020 20:50
Copy link
Member

@jmagman jmagman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have great readability on Obj-C++ code, so I'm going to take your word on wrapping the memory management code. The OCMock and Objective-C stuff looks fine though.

* there for structure and they don't provide any semantic information to VoiceOver (they return
* NO for isAccessibilityElement).
*/
@interface SemanticsObjectContainer : UIAccessibilityElement
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way we can start a policy of prefixing all classes with the FLT prefix (since this one has never been in a header before it's eligible even though it's not public)? It's a poor namespacing substitute but it's all Obj-C's got. We have random plugins using that prefix too, which is also not the right thing to do across frameworks... The other classes at least start with Flutter which is still not good, but is at least some kind of prefix.
http://google.github.io/styleguide/objcguide.html#prefixes
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html#//apple_ref/doc/uid/TP40011210-CH10-SW4

@gaaclarke
Copy link
Member Author

@chinmaygarde I underestimated the time it would take to get ARC support approval. I've disentangled it for now. As you can see this confuses the testing build target a bit but we can remove that once we land ARC support. Please give this a glance.

wandyers pushed a commit to wandyers/engine that referenced this pull request May 23, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants