-
Notifications
You must be signed in to change notification settings - Fork 516
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
[dotnet] preliminary cut for class-redirector #17951
Conversation
|
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.
Most of them are nits except for the choice of net6 over net7, why is that?
var map = new CSToObjCMap (); | ||
map.Add ("Foo", new ObjCNameIndex ("Bar", 2)); | ||
map.Add ("Baz", new ObjCNameIndex ("Zed", 3)); |
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.
Because you are inheriting from the dictionary class, you can use a dict initializer:
var map = new CSToObjCMap (); | |
map.Add ("Foo", new ObjCNameIndex ("Bar", 2)); | |
map.Add ("Baz", new ObjCNameIndex ("Zed", 3)); | |
var map = new CSToObjCMap () { | |
["Foo"] = new ObjCNameIndex ("Bar", 2), | |
["Baz"] = new ObjCNameIndex ("Zed", 3), | |
}; |
</Element> | ||
</CSToObjCMap>"; | ||
|
||
var reader = new StringReader (text); |
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.
var reader = new StringReader (text); | |
using var reader = new StringReader (text); |
if (map.TryGetValue ("Foo", out var nameIndex)) { | ||
Assert.That (nameIndex.ObjCName, Is.EqualTo ("Bar"), "no bar name"); | ||
Assert.That (nameIndex.MapIndex, Is.EqualTo (2), "no bar index"); | ||
} else { | ||
Assert.Fail ("no Foo value"); | ||
} |
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.
Maybe we can make things a little cleaner with the following:
if (map.TryGetValue ("Foo", out var nameIndex)) { | |
Assert.That (nameIndex.ObjCName, Is.EqualTo ("Bar"), "no bar name"); | |
Assert.That (nameIndex.MapIndex, Is.EqualTo (2), "no bar index"); | |
} else { | |
Assert.Fail ("no Foo value"); | |
} | |
Assert.True(map.TryGetValue ("Foo", out var nameIndex)); | |
Assert.That (nameIndex.ObjCName, Is.EqualTo ("Bar"), "no bar name"); | |
Assert.That (nameIndex.MapIndex, Is.EqualTo (2), "no bar index"); |
if (map.TryGetValue ("Baz", out var nameIndex1)) { | ||
Assert.That (nameIndex1.ObjCName, Is.EqualTo ("Zed"), "no bar name"); | ||
Assert.That (nameIndex1.MapIndex, Is.EqualTo (3), "no bar index"); | ||
} else { | ||
Assert.Fail ("no Foo value"); | ||
} |
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.
Same comment:
if (map.TryGetValue ("Baz", out var nameIndex1)) { | |
Assert.That (nameIndex1.ObjCName, Is.EqualTo ("Zed"), "no bar name"); | |
Assert.That (nameIndex1.MapIndex, Is.EqualTo (3), "no bar index"); | |
} else { | |
Assert.Fail ("no Foo value"); | |
} | |
Assert.True(map.TryGetValue ("Baz", out var nameIndex1)); | |
Assert.That (nameIndex1.ObjCName, Is.EqualTo ("Zed"), "no bar name"); | |
Assert.That (nameIndex1.MapIndex, Is.EqualTo (3), "no bar index"); |
static bool DirectoryIsWritable (string path) | ||
{ | ||
var info = new DirectoryInfo (path); | ||
return (info.Attributes & FileAttributes.ReadOnly) == 0; |
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.
Might be simpler to use the Enum.HasFlag method: https://learn.microsoft.com/en-us/dotnet/api/system.enum.hasflag?view=net-7.0
if (classMap.TryGetValue (cl.FullName, out var classPtrField)) { | ||
PatchClassPtrUsage (cl, classPtrField!); | ||
} |
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.
If we use net7 TryGetValue should have the attribute NotNullWhen in the newer versions and the '!' should not be needed. Mentioned before, but why net6?
var i = 0; | ||
Instruction? stsfld = null; | ||
while (i < il.Body.Instructions.Count) { | ||
var instr = il.Body.Instructions [i]; | ||
// look for | ||
// stsfld class_ptr | ||
if (instr.OpCode == OpCodes.Stsfld && instr.Operand == class_ptr) { | ||
stsfld = instr; | ||
break; | ||
} | ||
i++; | ||
} |
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.
We could use a for loop, why the use of a while loop? I am a person that makes mistakes, the for loop should help not to forget the index update:
var i = 0; | |
Instruction? stsfld = null; | |
while (i < il.Body.Instructions.Count) { | |
var instr = il.Body.Instructions [i]; | |
// look for | |
// stsfld class_ptr | |
if (instr.OpCode == OpCodes.Stsfld && instr.Operand == class_ptr) { | |
stsfld = instr; | |
break; | |
} | |
i++; | |
} | |
int i; | |
Instruction? stsfld = null; | |
for (i=0; i < il.Body.Instructions.Count; i++) { | |
var instr = il.Body.Instructions [i]; | |
// look for | |
// stsfld class_ptr | |
if (instr.OpCode == OpCodes.Stsfld && instr.Operand == class_ptr) { | |
stsfld = instr; | |
break; | |
} | |
} |
You can still access the 'i' variable as you do later in the code. We ca even be little more evil (I do not like this option):
var i = 0; | |
Instruction? stsfld = null; | |
while (i < il.Body.Instructions.Count) { | |
var instr = il.Body.Instructions [i]; | |
// look for | |
// stsfld class_ptr | |
if (instr.OpCode == OpCodes.Stsfld && instr.Operand == class_ptr) { | |
stsfld = instr; | |
break; | |
} | |
i++; | |
} | |
int i = 0; | |
Instruction? stsfld = null; | |
for (; i < il.Body.Instructions.Count; i++) { | |
var instr = il.Body.Instructions [i]; | |
// look for | |
// stsfld class_ptr | |
if (instr.OpCode == OpCodes.Stsfld && instr.Operand == class_ptr) { | |
stsfld = instr; | |
break; | |
} | |
} |
var instr = il.Body.Instructions [index]!; | ||
var operand = instr.Operand?.ToString () ?? ""; |
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 need for the '!' here, just fwd the '?' correct?
var instr = il.Body.Instructions [index]!; | |
var operand = instr.Operand?.ToString () ?? ""; | |
var instr = il.Body.Instructions [index]; | |
var operand = instr?.Operand?.ToString () ?? ""; |
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.
I disagree here. When I use the !
I'm trying to say clearly "this cannot be and should never be null and I'm so confident that I will bet the stability of my code on that assertion" whereas the ?.
says "well, if it's not there, empty string is OK, which it, in fact, is not. Every array element should have an instruction. Some instructions may or may not have operands.
var instr = il.Body.Instructions [index]!; | ||
return instr.OpCode == OpCodes.Ldstr; |
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.
Same comment, no need to do the ! which is usually code smell regarding to nullability. We can do:
var instr = il.Body.Instructions [index]!; | |
return instr.OpCode == OpCodes.Ldstr; | |
var instr = il.Body.Instructions [index]; | |
return instr?.OpCode == OpCodes.Ldstr; |
Afaik OpCode is a ref class, so you are no dealing with a Nullable situation.
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.
Unfortunately, Cecil is written with prior to nullable and Instruction
is possible to be null (but should never be null in this situation, but the analyzer doesn't know that). In this case, the bad smell is in the analyzer in working with legacy code.
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.
Oh yes, but you can move the ! to be a ? and the analyser won't complain and the output will be valid.
foreach (var elem in elements) { | ||
if (elem.Key is not null && elem.Value is not null) | ||
map.Add (elem.Key, elem.Value); | ||
} |
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.
Can't we add this filter for the key/value in the LINQ expression with a where clause?
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.
I started out writing a LINQ expression and it was ugly and not as readable as I liked. This was much more readable. I could have done elements.Where (el => el.Key is not null && elem.Value is not null)
but Dictionary
doesn't have AddRange
so I still need a for
loop, or I'd have to do:
elements.Where (el => el.Key is not null && el.Value is not null).ForEach (el => map.Add(el.Key, el.Value));
And now I've reduced it to one line and just made it harder to read (IMHO) and harder to debug.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
✅ API diff for current PR / commitLegacy Xamarin (No breaking changes)
NET (empty diffs)
✅ API diff vs stableLegacy Xamarin (No breaking changes).NET (No breaking changes)✅ Generator diffGenerator diff is empty Pipeline on Agent |
💻 [CI Build] Windows Integration Tests passed 💻✅ All Windows Integration Tests passed. Pipeline on Agent |
💻 [PR Build] Tests on macOS M1 - Mac Ventura (13.0) passed 💻✅ All tests on macOS M1 - Mac Ventura (13.0) passed. Pipeline on Agent |
💻 [PR Build] Tests on macOS M1 - Mac Big Sur (11.5) passed 💻✅ All tests on macOS M1 - Mac Big Sur (11.5) passed. Pipeline on Agent |
🚀 [CI Build] Test results 🚀Test results✅ All tests passed on VSTS: simulator tests. 🎉 All 225 tests passed 🎉 Tests counts✅ bcl: All 69 tests passed. Html Report (VSDrops) Download Pipeline on Agent |
static string [] xamarinDlls = new string [] { | ||
"Microsoft.iOS.dll", | ||
"Microsoft.macOS.dll", | ||
"Microsoft.tvOS.dll", |
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.
Microsoft.MacCatalyst.dll?
if (index < 0) | ||
return false; | ||
var instr = il.Body.Instructions [index]!; | ||
var operand = instr.Operand?.ToString () ?? ""; |
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're calling ToString on every instruction, which is unnecessary. You only need to call ToString on call
instructions, which would be much more performant. This would mean moving the instr.OpCode == OpCodes.Call
check to before the ToString call.
This is the preliminary version of class-redirector addressing issue #16671
What it does:
For each class:
Tests:
right now there are tests that test the XML reading and writing
There will be future tests that test the class rewriting writ small before we release it on our code base
As it is, this will not affect our code if its merged in
To do:
need to modify the static registrar to generate the XML file
need to invoke this after the static registrar has run
make it localizable
What you should consider in this PR: