Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@joelmartinez
Copy link
Member

@joelmartinez joelmartinez commented Dec 22, 2015

The tests added during the initial development of the -multiassembly argument failed to test the presence of extension methods;
the result was a crash that occurred when there were extension methods present in multiple assemblies.

This patch addresses the issue by putting a guard clause around the code that adds the extension method to the index.
Tests have been modified accordingly to verify this bug and include extension methods

Additionally, the -multiassembly parameter was removed entirely. This behavior is now triggered by the presence of -apistyle, which is where support for "multiple assemblies" was meant to work anyways. To review, this is to support cross-platform development, where you have multiple -- different -- assemblies for each platform. These assemblies will share a certain cross-section of APIs that will be exactly the same in each assembly (as opposed to multiple versions of literally the same assembly).

As a result, every member (when run with apistyle) will now contain an individual AssemblyInfo tag that also contains the name of the assembly.

@joelmartinez
Copy link
Member Author

@jonpryor, once #2333 is accepted, this PR will be much simpler to evaluate ... I figured this was better than just adding the change/fix onto that PR, but please let me know if you'd prefer everything in a single PR :)

@jonpryor
Copy link
Contributor

jonpryor commented Jan 5, 2016

@joelmartinez given that PR #2333 has been accepted (...mostly...), could we rebase this PR w/o the #2333 changes included?

@joelmartinez
Copy link
Member Author

@jonpryor ok, I've made the two small changes, that was a really good point.

I did realize though why I had that other change in there. There was a very small change that affected how the AssemblyInfo nodes are added, and without that change, the extension methods added in some of the tests aren't 100% correct. So I've rebased this branch to master for now, and when #2333 is accepted, I'll rebase to include that one as well. I'll ping you when that's done :)

@joelmartinez
Copy link
Member Author

@jonpryor alrighty, this one has been rebased to include the other change, and I've verified that make check passes locally :)

@jonpryor
Copy link
Contributor

jonpryor commented Jan 7, 2016

the result was a crash that occurred when there were extension methods present in multiple assemblies.

What was the crash? Was this an mdoc update crash or a monodoc.dll crash?

This patch addresses the issue by putting a guard clause around the code that adds the extension method to the index.

...which doesn't answer where the crash lay.

My concern is that you're fixing the crash in the wrong spot.

For example, having two separate extension methods with the same name in different namespaces is perfectly valid:

namespace Example {
    class Value {}
}
namespace Example.Extension1 {
    static class ValueExtensions {
        public static void Foo (this Value value) {}
    }
}
namespace Example.Extension2 {
    static class ValueExtensions {
        public static void Foo (this Value value) {}
    }
}

We have two different Value.Foo() extension methods.

This is fine; the one that gets used is based on the using declarations in-scope.

Except in index.xml /Overview/ExtensionMethods/ExtensionMethod. (Aside: what was I thinking when I came up with that format?! //Targets/Target?! There can only ever be one target type!)

What should happen is that both of the Foo() extension methods should be listed in index.xml.

If mdoc update -multiassembly is used, I don't think that would actually happen, as it uses //Member/MemberSignature[@Language='C#']/@Value to determine equality, and in the above example both of the .Foo() extension methods will have the same MemberSignature value.

Question: Why does -multiassembly exist? Shouldn't all invocations "Allow types to be in multiple assemblies"? (IL certainly does!)

@joelmartinez
Copy link
Member Author

@jonpryor

What was the crash? Was this an mdoc update crash or a monodoc.dll crash?

it was an mdoc update crash ... when it went to add the extension methods to the index, it would find duplicates and crash hard.

For example, having two separate extension methods with the same name in different namespaces is perfectly valid:

The issue is when there are literally in the same namespace (but in different assemblies).

Question: Why does -multiassembly exist? Shouldn't all invocations "Allow types to be in multiple assemblies"? (IL certainly does!)

Yes, the same type, but in a different namespace is/was certainly allowed and supported, because as you suggested, the signature would be different ... but in this case, -multiassembly was developed to support the case of ios, watchos, and tvos all having the same types (ie. stuff in CoreFoundation, etc) to enable code reuse across platforms. Because there are some minor differences between the platforms, all of those need to be run through mdoc update. When run with -multiassembly, the assembly a member was found in is added to every member node, so that you can easily see what assembly you can find this particular member in (including, potentially, all of them). You can see an example of the extension method with multiple AssemblyInfo nodes here: https://github.com/mono/mono/pull/2377/files#diff-f22c49e10f4bd6e290bf8848bf8021f5R4

@jonpryor
Copy link
Contributor

jonpryor commented Jan 8, 2016

The issue is when there are literally in the same namespace (but in different assemblies).

Then it sounds like this is what should be fixed. This is a perfectly valid (if confusing) thing to do. Perhaps <ExtensionMethod/> needs to specify the assembly the extension method is located in?

When run with -multiassembly, the assembly a member was found in is added to every member node, so that you can easily see what assembly you can find this particular member in (including, potentially, all of them)

So why not do that for everything? :-)

Glib answer: because it bloats the XML. That's not a great excuse; glib response: use a more compact representation to reduce bloat. :-)

Which likely dovetails nicely with your intention of redoing the mdoc file format...and doesn't solve what to do now. Fair enough.

-multiassembly still feels like a kludge, in which the user needs to know "something" -- something undocumented in the man page! -- and worse, needs to know said "something" for perfectly normal behavior!

        { "multiassembly",
            "Allow types to be in multiple assemblies.",
            v => multiassembly = true },

How does this differ from the years-old behavior of processing the same assembly with different version numbers, e.g. System.dll v1.0 vs. v2.0? It's still conceptually two assemblies, i.e. "multiple" assemblies:

mdoc update -o Documentation/en ../lib/net_2_0/System.dll
mdoc update -o Documentation/en ../lib/net_4_0/System.dll

That doesn't entirely make sense anymore, as we no longer build net_2_0/etc., but we used to, and that's why there's a //Member/AssemblyInfo element:

<Member MemberName="AssertUiEnabled">
  <MemberSignature Language="C#" Value="public bool AssertUiEnabled { get; set; }" />
  <MemberSignature Language="ILAsm" Value=".property instance bool AssertUiEnabled" />
  <MemberType>Property</MemberType>
  <AssemblyInfo>
    <AssemblyVersion>1.0.5000.0</AssemblyVersion>
    <AssemblyVersion>2.0.0.0</AssemblyVersion>
    <AssemblyVersion>4.0.0.0</AssemblyVersion>
  </AssemblyInfo>
...

I'm sorry I didn't notice -multiassembly before, but I don't like it. It shouldn't be necessary, because the behavior it enables should be enabled by default.

@joelmartinez
Copy link
Member Author

something undocumented in the man page!

Actually, on that point, where is that man page documentation located? I'd be happy to update it as features are added changed, along with deploying new versions when appropriate to the doc sites.

ok ... so I have some stuff to think about. I suppose you're right, that it would be better for an extra parameter to be avoided, and the functionality simply supported. Some notes/questions

  • As far as the guard clause I added, I believe that either way this is still necessary, because the difference between the scenario you showed with same-named assembly/different version, is that these are completely different assemblies, done in the same run (ie. mdoc update -o en some.dll another.dll) ... so the different reflected members will both match to the same XML node (as expected), and attempt to add the extension method twice.
  • Ok, I can easily add the AssemblyInfo to that ExtensionMethod ... it's being explicitly removed currently when the node gets cloned at the moment.
  • Would it be a good enough solution if:
    1. I removed the multiassembly option
    2. made it so that multiple //Member/AssemblyInfo elements are added only if necessary?

@jonpryor
Copy link
Contributor

jonpryor commented Jan 8, 2016

where is that man page documentation located

mono/man/mdoc.1.

@jonpryor
Copy link
Contributor

jonpryor commented Jan 8, 2016

the difference between the scenario you showed with same-named assembly/different version, is that these are completely different assemblies, done in the same run...

They could have been specified on the same run; Cecil don't care.

$ time mdoc update -o ~/tmp/en /Library/Frameworks/Mono.framework/Libraries/mono/2.0/System.dll /Library/Frameworks/Mono.framework/Libraries/mono/4.0/System.dll
...
Members Added: 8149, Members Deleted: 0

real    0m6.851s

Same assembly, two different versions, Just Works™. (Or so I hope... No errors reported, anyway.)

Would it be a good enough solution if:

  1. I removed the multiassembly option
  2. Made it so that multiple //Member/AssemblyInfo elements are added only if necessary?

That sounds reasonable. (At least, I can't think of any immediate problems with that...)

@lewurm
Copy link
Contributor

lewurm commented Mar 29, 2016

is there still interest to merge this or should it be closed?

@joelmartinez
Copy link
Member Author

Hi @lewurm ... yes, although this particular task has taken a temporary back seat to some other items. I am still working on it and plan on making additional changes based on the feedback in the PR thread :)

@joelmartinez joelmartinez changed the title [mdoc] Fixes issue with -multiassembly and extension methods [mdoc] Extension method crash fix, removes -multiassembly May 6, 2016
@joelmartinez
Copy link
Member Author

joelmartinez commented May 6, 2016

Hi @jonpryor ... returning to this, finally :) So if you'll remember, originally we said the changes would be:

Would it be a good enough solution if:

  1. I removed the multiassembly option
  2. Made it so that multiple //Member/AssemblyInfo elements are added only if necessary?

I've implemented #1, and a slightly modified #2 from the list above. The behavior that was previously triggered with multiassembly is now triggered with apistyle only. So regular mdoc update will continue to behave as before, while the unified/classic runs will now contain the assembly information at the member level; this is not much more bloat than is already being added anyways due to the unified/classic information, it just adds the name to AssemblyInfo nodes that are already there.

In the future, the ios assemblies will eventually deprecate the "classic" APIs, and when that happens, it will be a lot easier to support this without having to add -multiassembly back in, because then we won't be doing multiple apistyle runs independently of each other (the reason that the internal flag is still there, being triggered by apistyle).

I've updated the PR description to reflect these new changes ... please let me know if you have any questions :)

@joelmartinez
Copy link
Member Author

@jonpryor ping :)

Copy link
Contributor

Choose a reason for hiding this comment

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

No need to set to = false, as that's the default value.

The tests added during the initial development of the -multiassembly argument failed to test the presence of extension methods;
the result was a crash that occurred when there were extension methods present in multiple assemblies.

This patch addresses the issue by putting a guard clause around the code that adds the extension method to the index.
Tests have been modified accordingly to verify this bug and include extension methods

Additionally, the -multiassembly parameter was removed entirely. This behavior is now triggered by the presence of -apistyle,
which is where support for "multiple assemblies" was meant to work anyways. To review, this is to support cross-platform
development, where you have multiple -- *different* -- assemblies for each platform. These assemblies will share a certain
cross-section of APIs that will be *exactly* the same in each assembly (as opposed to multiple versions of literally the same assembly).
As a result, every member will now contain an individual `AssemblyInfo` tag that also contains the name of the assembly.
@joelmartinez
Copy link
Member Author

@jonpryor alright, I've converted the field to a property. You're right, that does make it a bit clearer as you can see exactly the triggering condition is when you view source.

@jonpryor jonpryor merged commit 8c32f9c into mono:master May 31, 2016
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
…y-extension-fix

[mdoc] Extension method crash fix, removes -multiassembly

Commit migrated from mono/mono@8c32f9c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants