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

Skip to content

Conversation

@alexanderkyte
Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor

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));

}

Copy link
Member

@lambdageek lambdageek Feb 14, 2018

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

Copy link
Contributor Author

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

Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Contributor Author

@alexanderkyte alexanderkyte Feb 13, 2018

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.

Copy link
Contributor Author

@alexanderkyte alexanderkyte Feb 13, 2018

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.

Copy link
Member

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?)

Copy link
Contributor Author

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.

@alexanderkyte alexanderkyte force-pushed the custommod_type_equality branch from 24db715 to 70ae9f7 Compare February 13, 2018 22:32
Copy link
Contributor

@kumpera kumpera left a 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.

Copy link
Contributor

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));

}

@marek-safar
Copy link
Member

@alexanderkyte could you rebase it and address @kumpera review

@luhenry
Copy link
Contributor

luhenry commented Mar 9, 2018

@alexanderkyte what's the status of that?

@alexanderkyte
Copy link
Contributor Author

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.

Copy link
Member

@lambdageek lambdageek left a 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.

  1. Does custom modifier order matter?
  2. Should modreq(mod1) and modopt(mod1) compare unequal?

Also I'm not sure about making MonoCustomModContainer public right away.

Copy link
Member

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.

Copy link
Member

Choose a reason for hiding this comment

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

Stray $

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 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.

Copy link
Member

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.

Copy link
Member

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?

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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

Copy link
Contributor Author

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

@lambdageek
Copy link
Member

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.

@alexanderkyte
Copy link
Contributor Author

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.

@alexanderkyte alexanderkyte force-pushed the custommod_type_equality branch 2 times, most recently from df5250e to dea3e5f Compare March 24, 2018 02:42
@marek-safar
Copy link
Member

@kumpera could you review this again

@lambdageek
Copy link
Member

@monojenkins build Linux x64 Checked Private Types Build

@luhenry luhenry assigned alexanderkyte and unassigned luhenry Apr 2, 2018
@alexanderkyte alexanderkyte force-pushed the custommod_type_equality branch from dea3e5f to 0e08b6d Compare April 6, 2018 17:35
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.
@alexanderkyte alexanderkyte force-pushed the custommod_type_equality branch from 0e08b6d to 9d505af Compare April 6, 2018 17:35
@alexanderkyte
Copy link
Contributor Author

alexanderkyte commented Apr 6, 2018

@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?

Copy link
Member

@lambdageek lambdageek left a 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.

Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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?

@alexanderkyte alexanderkyte force-pushed the custommod_type_equality branch 4 times, most recently from c543fb6 to 48c33f1 Compare April 7, 2018 01:39
@alexanderkyte alexanderkyte force-pushed the custommod_type_equality branch from 48c33f1 to 7efbdf1 Compare April 7, 2018 07:15
@marek-safar
Copy link
Member

@monojenkins build failed

@lambdageek
Copy link
Member

@kumpera I think this addresses all your concerns now - the image is stored once per MonoType when there are custom modifiers present. Could you review again or dismiss your old review.

Copy link
Contributor

@kumpera kumpera left a comment

Choose a reason for hiding this comment

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

LGTM

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.

5 participants