-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[runtime] Compare custom modifiers in type equality #7012
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
Conversation
mono/metadata/metadata.h
Outdated
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 you define it here, it's not internal. It's part of the public API.
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 shouldn't store then on each cmod, but change MonoType to have an optional cmod container.
IE, change MonoType to this:
struct _MonoType {
union {
MonoClass *klass; /* for VALUETYPE and CLASS */
MonoType *type; /* for PTR */
MonoArrayType *array; /* for ARRAY */
MonoMethodSignature *method;
MonoGenericParam *generic_param; /* for VAR and MVAR */
MonoGenericClass *generic_class; /* for GENERICINST */
} data;
unsigned int attrs : 16; /* param attributes or field flags */
MonoTypeEnum type : 8;
unsigned int has_cmods : 1; /* has a cmod appended at the end */
unsigned int byref : 1;
unsigned int pinned : 1; /* valid when included in a local var signature */
};Then have this:
typedef struct CustomModContainer {
int count;
MonoImage *image;
MonoCustomMod modifiers [1]; /*the actual size of this array is count*/
}You'd be able to fetch it using something like this:
CustomModContainer*
mono_type_get_cmods (MonoType *t) {
if (!t->has_cmods)
return null;
return (CustomModContainer*)(((char*)t) + sizeof (MonoType));
}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 a bit nicer:
typedef struct _MonoTypeWithModifiers {
MonoType type;
CustomModContainer cmods;
} MonoTypeWithModifiers;So you can just do &((MonoTypeWithModifiers*)type)->cmods in mono_type_get_cmods
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 looks good @lambdageek, that works just like our other prefixed struct types
mono/metadata/metadata.c
Outdated
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.
Shouldn't this only be comparing required modifiers, not optional ones?
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.
Are two integers, one with an optional modifier, one without any modifiers, are they equal?
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.
It looks like the main use of optional modifiers are to differentiate between functions taking a long and functions taking an int (or other bitness) on C++/CLI. Since they use the same underlying CLR type for both, they use modopts to prevent one function from being used where the other is expected. If we treat them as equal, that defeats that use.
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.
From 335, 7.1.1:
The CLI itself shall treat required and optional modifiers in the same manner. Two signatures that differ only by the addition of a custom modifier (required or optional) shall not be considered to match.
mono/metadata/metadata.c
Outdated
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.
What if the two signatures have the modifiers in different orders? (Is there an ordering rule in ECMA?)
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 worrying, let me look into that.
24db715 to
70ae9f7
Compare
kumpera
left a comment
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.
It makes no sense to store the same data on every cmod, since they all will have the same image.
mono/metadata/metadata.h
Outdated
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 shouldn't store then on each cmod, but change MonoType to have an optional cmod container.
IE, change MonoType to this:
struct _MonoType {
union {
MonoClass *klass; /* for VALUETYPE and CLASS */
MonoType *type; /* for PTR */
MonoArrayType *array; /* for ARRAY */
MonoMethodSignature *method;
MonoGenericParam *generic_param; /* for VAR and MVAR */
MonoGenericClass *generic_class; /* for GENERICINST */
} data;
unsigned int attrs : 16; /* param attributes or field flags */
MonoTypeEnum type : 8;
unsigned int has_cmods : 1; /* has a cmod appended at the end */
unsigned int byref : 1;
unsigned int pinned : 1; /* valid when included in a local var signature */
};Then have this:
typedef struct CustomModContainer {
int count;
MonoImage *image;
MonoCustomMod modifiers [1]; /*the actual size of this array is count*/
}You'd be able to fetch it using something like this:
CustomModContainer*
mono_type_get_cmods (MonoType *t) {
if (!t->has_cmods)
return null;
return (CustomModContainer*)(((char*)t) + sizeof (MonoType));
}70ae9f7 to
66fcd13
Compare
|
@alexanderkyte could you rebase it and address @kumpera review |
|
@alexanderkyte what's the status of that? |
|
It was temporarily put on hold because I was asked to prioritize more urgent things. The current status is that I am making the requested modifications while investigating worries we have about memory unsafety around MonoType copying. This bug brought to our attention that they are variably sized types, and that we have been placing them on the stack / copying them in a number of places. Thankfully they are few in number. |
840eac8 to
5e8eedc
Compare
lambdageek
left a comment
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.
Mostly looks good.
Just a couple of concerns about type equality.
- Does custom modifier order matter?
- Should
modreq(mod1)andmodopt(mod1)compare unequal?
Also I'm not sure about making MonoCustomModContainer public right away.
mono/metadata/metadata.h
Outdated
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.
this can be MonoImage*, I think.
mono/mini/aot-runtime.c
Outdated
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.
Stray $
mono/metadata/metadata.c
Outdated
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 you add a comment here referencing ECMA - I keep stumbling over this line.
(From the last review)
From 335, 7.1.1:
The CLI itself shall treat required and optional modifiers in the same manner. Two signatures that differ only by the addition of a custom modifier (required or optional) shall not be considered to match.
mono/metadata/metadata.c
Outdated
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 should probably have a FIXME here to propagate the error out to the caller.
mono/metadata/metadata.c
Outdated
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.
Does custom modifier order matter? if I have t modreq(mod1) modreq(mod2) t vs t modreq(mod2) modreq(mod1) are those the same type or different?
mono/metadata/metadata.h
Outdated
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.
Also does MonoCustomModContainer really need to be in the public API already?
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.
Custom modifiers are used by monodis. I'm not sure how the public API consumers would be able to interact with the mods without the image, count, and mods provided here.
mono/metadata/metadata.c
Outdated
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 think you need to also compare cm1->modifiers[i].required != cm2->modifiers[i].required
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.
This was added by a line earlier in the loop body
|
If ECMA is underspecified or ambiguous about some cases, we should do what .NET Framework does. We should add tests to lock down the behavior we believe we're implementing. |
|
Any suggestions as to the tests? We use types in a great many places, and this significantly changes only inequalities with modreqs. I think that the fact that signatures treat modifiers as inequalities significantly narrows down the behaviors they can have. The typeof for two arguments that differ in the modifiers has to return false (otherwise C++/CLI would see typeof(int) and typeof(long) to be the same). That means that since these types can be reflected, we'd have to compare for the inequality in the most general place. This is in our type inequality here. One test would be the actual bug filed with roslyn, the inheritance with a single modreq. We could add one test to see if the order matters, using a 3-modreq type. Lastly, we want one that checks if modreqs and modopts with the same parameters are considered equivalent. |
df5250e to
dea3e5f
Compare
|
@kumpera could you review this again |
|
@monojenkins build Linux x64 Checked Private Types Build |
dea3e5f to
0e08b6d
Compare
Custom modifiers are an odd feature of .net which allow types to be tagged. These tags are semantically relevant. A method with a modifier should not override a method without a modifier, for instance (github bug mono#6936). We don't put enough information into the modifier to uniquely find the Image associated with the token we have in the type. Modifiers from different referring assemblies will have different tokens to refer to the same modifier class, making it important to resolve the tokens to MonoClass pointers at runtime. Pointer equality of these classes is the way to determine two custom modifiers the same, or unique. We cannot resolve the image just from the MonoType because it is possible to put modifiers on primitive, builtin types. These builtin types lack any filled-out pointers in the data block, meaning we have zero metadata associated with our floating token. Other places in the codebase fixed this by passing around MonoImages, but adding a MonoImage argument to our type comparison function for this single bug would be a very poor decision with much code churn all throughout the runtime. The bug above resolves to two I4 types, one of which has a modifier, one of which does not. In order to pass this MonoImage around, we need to put a reference to it into something reachable from the MonoType. We can't put it in the custom module type because that is exported. It is part of the public API. MonoTypes do not adhere to pointer equality, and custom modules are structs baked directly into the MonoType. We need therefore to make MonoType point to a MonoCustomModInternal, a new type. This type embeds the public type as a struct and carries around the image as another element in the struct. All public interfaces still see the same metadata endpoints for parsing the modules, including the icalls. It is only our type comparison code that accesses our new field.
0e08b6d to
9d505af
Compare
|
@lambdageek I'm not very confident in my understanding of what I did in sre.c. We have a lot of functions that all state that they do the same general thing, but some "wrap" others. Could you check that I'm not using anything too-unwrapped here? |
lambdageek
left a comment
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.
SRE stuff looks alright.
Fixup the static function's name. And add some comments to 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.
Please add a comment at the top of test that summarizes what is being tested.
For SRE stuff, I also try to put a comment with C#-ish pseudocode of what's being constructed and a comment of which bit is expected to fail or do a tricky thing, etc. It'll save someone time in the future from having to mentally execute SRE.
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 added a number of blocks of comments
mono/metadata/sre.c
Outdated
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.
How about add_custom_modifiers_to_type?
c543fb6 to
48c33f1
Compare
48c33f1 to
7efbdf1
Compare
|
@monojenkins build failed |
|
@kumpera I think this addresses all your concerns now - the image is stored once per |
kumpera
left a comment
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.
LGTM
Custom modifiers are an odd feature of .net which allow types to
be tagged. These tags are semantically relevant. A method with a
modifier should not override a method without a modifier, for
instance (github bug #6936).
We don't put enough information into the modifier to uniquely find the
Image associated with the token we have in the type. Modifiers from
different referring assemblies will have different tokens to refer to
the same modifier class, making it important to resolve the tokens to
MonoClass pointers at runtime. Pointer equality of these classes is the
way to determine two custom modifiers the same, or unique.
We cannot resolve the image just from the MonoType because it is
possible to put modifiers on primitive, builtin types. These builtin
types lack any filled-out pointers in the data block, meaning we have
zero metadata associated with our floating token.
Other places in the codebase fixed this by passing around MonoImages,
but adding a MonoImage argument to our type comparison function for
this single bug would be a very poor decision with much code churn all
throughout the runtime.
The bug above resolves to two I4 types, one of which has a modifier, one
of which does not.
In order to pass this MonoImage around, we need to put a reference to it
into something reachable from the MonoType. We can't put it in the
custom module type because that is exported. It is part of the public
API.
MonoTypes do not adhere to pointer equality, and custom modules are structs
baked directly into the MonoType. We need therefore to make MonoType
point to a MonoCustomModInternal, a new type. This type embeds the
public type as a struct and carries around the image as another element
in the struct.
All public interfaces still see the same metadata endpoints for parsing the
modules, including the icalls. It is only our type comparison code that
accesses our new field.