More WASM fixes.
-Subproject commit 090c95d755f8112db5cf6c30df92a9941d9124a4
+Subproject commit 627333cae84f02a36ee9ca605c96dac4557d9f35
-Subproject commit 9e04cc7f41899c880807d1d055a24c768670784a
+Subproject commit b58ba4282377bcefd48abdc2d62ce6330e079abe
public LinearGradientBrush (Rectangle rect, Color color1, Color color2, LinearGradientMode linearGradientMode)
{
+ if (linearGradientMode < LinearGradientMode.Horizontal || linearGradientMode > LinearGradientMode.BackwardDiagonal) {
+ throw new InvalidEnumArgumentException (nameof (linearGradientMode), unchecked ((int)linearGradientMode), typeof (LinearGradientMode));
+ }
+
+ if (rect.Width == 0 || rect.Height == 0) {
+ throw new ArgumentException( string.Format ("Rectangle '{0}' cannot have a width or height equal to 0.", rect.ToString ()));
+ }
+
IntPtr nativeObject;
Status status = GDIPlus.GdipCreateLineBrushFromRectI (ref rect, color1.ToArgb (), color2.ToArgb (), linearGradientMode, WrapMode.Tile, out nativeObject);
GDIPlus.CheckStatus (status);
public LinearGradientBrush (RectangleF rect, Color color1, Color color2, LinearGradientMode linearGradientMode)
{
+ if (linearGradientMode < LinearGradientMode.Horizontal || linearGradientMode > LinearGradientMode.BackwardDiagonal) {
+ throw new InvalidEnumArgumentException (nameof (linearGradientMode), unchecked ((int)linearGradientMode), typeof (LinearGradientMode));
+ }
+
+ if (rect.Width == 0.0 || rect.Height == 0.0) {
+ throw new ArgumentException (string.Format ("Rectangle '{0}' cannot have a width or height equal to 0.", rect.ToString ()));
+ }
+
IntPtr nativeObject;
Status status = GDIPlus.GdipCreateLineBrushFromRect (ref rect, color1.ToArgb (), color2.ToArgb (), linearGradientMode, WrapMode.Tile, out nativeObject);
GDIPlus.CheckStatus (status);
public LinearGradientBrush (Rectangle rect, Color color1, Color color2, float angle, bool isAngleScaleable)
{
+ if (rect.Width == 0 || rect.Height == 0) {
+ throw new ArgumentException (string.Format ("Rectangle '{0}' cannot have a width or height equal to 0.", rect.ToString ()));
+ }
+
IntPtr nativeObject;
Status status = GDIPlus.GdipCreateLineBrushFromRectWithAngleI (ref rect, color1.ToArgb (), color2.ToArgb (), angle, isAngleScaleable, WrapMode.Tile, out nativeObject);
GDIPlus.CheckStatus (status);
public LinearGradientBrush (RectangleF rect, Color color1, Color color2, float angle, bool isAngleScaleable)
{
+ if (rect.Width == 0 || rect.Height == 0) {
+ throw new ArgumentException (string.Format ("Rectangle '{0}' cannot have a width or height equal to 0.", rect.ToString ()));
+ }
+
IntPtr nativeObject;
Status status = GDIPlus.GdipCreateLineBrushFromRectWithAngle (ref rect, color1.ToArgb (), color2.ToArgb (), angle, isAngleScaleable, WrapMode.Tile, out nativeObject);
GDIPlus.CheckStatus (status);
Assert.AreEqual (32, elements[5], 0.0001, "matrix.5");
}
+ [Test]
+ public void Constructor_Rectangle_InvalidWidthHeight ()
+ {
+ var emptyWidth = new Rectangle (0, 0, 0, 1);
+ var emptyHeight = new Rectangle (0, 0, 0, 1);
+
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyWidth, Color.Empty, Color.Empty, 1));
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyHeight, Color.Empty, Color.Empty, 1));
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyWidth, Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal));
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyHeight, Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal));
+ }
+
+ [Test]
+ public void Constructor_RectangleF_InvalidWidthHeight ()
+ {
+ var emptyWidth = new RectangleF (0, 0, 0, 1);
+ var emptyHeight = new RectangleF (0, 0, 0, 1);
+
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyWidth, Color.Empty, Color.Empty, 1));
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyHeight, Color.Empty, Color.Empty, 1));
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyWidth, Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal));
+ Assert.Throws<ArgumentException>(() => new LinearGradientBrush (emptyHeight, Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal));
+ }
+
+ [Test]
+ public void Constructor_LinearGradientMode_InvalidMode ()
+ {
+ var rect = new Rectangle (0, 0, 1, 1);
+ var rectf = new RectangleF (0, 0, 1, 1);
+
+ Assert.Throws<InvalidEnumArgumentException>(() => new LinearGradientBrush (rect, Color.Empty, Color.Empty, LinearGradientMode.Horizontal - 1));
+ Assert.Throws<InvalidEnumArgumentException>(() => new LinearGradientBrush (rectf, Color.Empty, Color.Empty, LinearGradientMode.Horizontal - 1));
+ Assert.Throws<InvalidEnumArgumentException>(() => new LinearGradientBrush (rect, Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal + 1));
+ Assert.Throws<InvalidEnumArgumentException>(() => new LinearGradientBrush (rectf, Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal + 1));
+ }
+
[Test]
public void InterpolationColors_Colors_InvalidBlend ()
{
Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipSetImagePalette (IntPtr.Zero, palette), "GdipSetImagePalette(null,palette)");
Assert.AreEqual (Status.InvalidParameter, GDIPlus.GdipSetImagePalette (bitmap, IntPtr.Zero), "GdipSetImagePalette(bitmap,null)");
Assert.AreEqual (Status.Ok, GDIPlus.GdipSetImagePalette (bitmap, palette), "GdipSetImagePalette");
-
- // change palette to 0 entries
- int flags = Marshal.ReadInt32 (palette);
- Marshal.WriteInt64 (palette, flags << 32);
- Assert.AreEqual (Status.Ok, GDIPlus.GdipSetImagePalette (bitmap, palette), "GdipSetImagePalette/Empty");
-
- Assert.AreEqual (Status.Ok, GDIPlus.GdipGetImagePaletteSize (bitmap, out size), "GdipGetImagePaletteSize/Empty");
- Assert.AreEqual (8, size, "size");
}
finally {
Marshal.FreeHGlobal (palette);
public void Add (JsonValue item)
{
- if (item == null)
- throw new ArgumentNullException ("item");
-
list.Add (item);
}
Assert.AreEqual (JsonType.Array, j.JsonType, "type");
var str = j.ToString ();
Assert.AreEqual (str, "[1, 2, 3, null]");
+ ((JsonArray) j).Add (null);
+ str = j.ToString ();
+ Assert.AreEqual (str, "[1, 2, 3, null, null]");
}
// Test that we correctly serialize JsonObject with null elements.
return timeout;
}
set {
- if (value != System.Threading.Timeout.InfiniteTimeSpan && value < TimeSpan.Zero)
+ if (value != System.Threading.Timeout.InfiniteTimeSpan && (value <= TimeSpan.Zero || value.Ticks > int.MaxValue))
throw new ArgumentOutOfRangeException ();
timeout = value;
Assert.Fail ("#2");
} catch (ArgumentOutOfRangeException) {
}
+
+ try {
+ client.Timeout = TimeSpan.Zero;
+ Assert.Fail ("#3");
+ } catch (ArgumentOutOfRangeException) {
+ }
+
+ try {
+ client.Timeout = new TimeSpan(int.MaxValue + 1L);
+ Assert.Fail ("#3");
+ } catch (ArgumentOutOfRangeException) {
+ }
}
[Test]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
- public extern static IntPtr BufferToBSTR (Array ptr, int slen);
+ extern static IntPtr BufferToBSTR (Array ptr, int slen);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern static IntPtr UnsafeAddrOfPinnedArrayElement (Array arr, int index);
--- /dev/null
+// CS0131: The left-hand side of an assignment must be a variable, a property or an indexer
+// Line: 8
+
+class X
+{
+ void Test ()
+ {
+ Foo () = 1;
+ }
+
+ static int Foo ()
+ {
+ return 1;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS0199: A static readonly field `X.f' cannot be passed ref or out (except in a static constructor)
+// Line: 10
+
+class X
+{
+ static readonly int f = 0;
+
+ public static void Main ()
+ {
+ ref int j = ref f;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS0206: A property, indexer or dynamic member access may not be passed as `ref' or `out' parameter
+// Line: 10
+
+class X
+{
+ static int P { get; set; }
+
+ static void Main ()
+ {
+ ref int rl = ref P;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS1503: Argument `#1' cannot convert `ref long' expression to type `ref int'
+// Line: 18
+
+using System;
+
+class X
+{
+ long field;
+
+ static void Main ()
+ {
+ var x = new X ();
+ x.Run ();
+ }
+
+ void Run ()
+ {
+ Test (ref Prop);
+ }
+
+ static int Test (ref int y)
+ {
+ return y;
+ }
+
+ ref long Prop {
+ get {
+ return ref field;
+ }
+ }
+}
--- /dev/null
+// CS1547: Keyword `void' cannot be used in this context
+// Line: 6
+
+interface IA
+{
+ ref void Foo ();
+}
--- /dev/null
+// CS1644: Feature `byref locals and returns' cannot be used because it is not part of the C# 6.0 language specification
+// Line: 9
+// Compiler options: -langversion:6
+
+class Text
+{
+ static ref long Foo ()
+ {
+ throw new System.NotImplementedException ();
+ }
+}
--- /dev/null
+// CS1715: `B.Foo': type must be `int' to match overridden member `A.Foo'
+// Line: 11
+
+public abstract class A
+{
+ public abstract ref int Foo { get; }
+}
+
+public class B : A
+{
+ public override ref long Foo {
+ get {
+ throw null;
+ }
+ }
+}
\ No newline at end of file
-// CS1764: Cannot use fixed local `p' inside an anonymous method, lambda expression or query expression
+// CS1764: Cannot use fixed variable `p' inside an anonymous method, lambda expression or query expression
// Line: 10
// Compiler options: -unsafe
--- /dev/null
+// CS8145: Auto-implemented properties cannot return by reference
+// Line: 6
+
+public class X
+{
+ ref string TestProp { get; }
+}
\ No newline at end of file
--- /dev/null
+// CS8146: `X.TestProp': property and indexer which return by reference must have a get accessor
+// Line: 6
+
+public class X
+{
+ ref string TestProp { set; }
+}
\ No newline at end of file
--- /dev/null
+// CS8147: `X.this[int]': property and indexer which return by reference cannot have set accessors
+// Line: 6
+
+public class X
+{
+ ref string this [int arg] {
+ set {
+
+ }
+ get {
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8147: `X.TestProp': property and indexer which return by reference cannot have set accessors
+// Line: 6
+
+public class X
+{
+ ref string TestProp {
+ set {
+
+ }
+ get {
+
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8148: `B.Foo': must return by reference to match overridden member `A.Foo'
+// Line: 11
+
+public abstract class A
+{
+ public abstract ref int Foo { get; }
+}
+
+public class B : A
+{
+ public override long Foo {
+ get {
+ throw null;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8148: `B.Foo()': must not return by reference to match overridden member `A.Foo()'
+// Line: 11
+
+public abstract class A
+{
+ public abstract int Foo ();
+}
+
+public class B : A
+{
+ public override ref int Foo ()
+ {
+
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8149: By-reference returns can only be used in lambda expressions that return by reference
+// Line: 12
+
+using System;
+
+class A
+{
+ int p;
+
+ void Test ()
+ {
+ Action a = () => ref p;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8149: By-reference returns can only be used in methods that return by reference
+// Line: 10
+
+class A
+{
+ int p;
+
+ int Test ()
+ {
+ return ref p;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8150: By-reference return is required when method returns by reference
+// Line: 10
+
+class A
+{
+ int p;
+
+ ref int Test ()
+ {
+ return p;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8151: The return by reference expression must be of type `string' because this method returns by reference
+// Line: 10
+
+public class X
+{
+ int field;
+
+ ref string TestMethod ()
+ {
+ return ref field;
+ }
+}
--- /dev/null
+// CS8152: `C' does not implement interface member `IA.Foo()' and the best implementing candidate `C.Foo()' return type `void' does not return by reference
+// Line: 11
+
+interface IA
+{
+ ref char Foo ();
+}
+
+public class C : IA
+{
+ public void Foo ()
+ {
+ }
+}
--- /dev/null
+// CS8153: An expression tree lambda cannot contain a call to a method, property, or indexer that returns by reference
+// Line: 11
+
+using System;
+using System.Linq.Expressions;
+
+class X
+{
+ void Foo ()
+ {
+ Expression<Func<int>> e = () => Test (ref this[0]);
+ }
+
+ static int Test (ref int y)
+ {
+ return y;
+ }
+
+ ref int this [int y] {
+ get {
+ throw null;
+ }
+ }
+}
--- /dev/null
+// CS8154: The body of `TestClass.TestFunction()' cannot be an iterator block because the method returns by reference
+// Line: 10
+
+class TestClass
+{
+ int x;
+
+ ref int TestFunction()
+ {
+ yield return x;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8155: Lambda expressions that return by reference cannot be converted to expression trees
+// Line: 14
+
+using System.Linq.Expressions;
+
+class TestClass
+{
+ static int x;
+
+ delegate ref int D ();
+
+ static void Main ()
+ {
+ Expression<D> e = () => ref x;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8156: An expression cannot be used in this context because it may not be returned by reference
+// Line: 8
+
+class X
+{
+ int Prop {
+ get {
+ return 1;
+ }
+ }
+
+ ref int Test ()
+ {
+ return ref Prop;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8156: An expression cannot be used in this context because it may not be returned by reference
+// Line: 8
+
+class Test
+{
+ ref int Foo ()
+ {
+ return ref 2;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8157: Cannot return `r' by reference because it was initialized to a value that cannot be returned by reference
+// Line: 11
+
+struct S
+{
+ int i;
+
+ ref int M ()
+ {
+ ref int r = ref i;
+ return ref r;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8160: A readonly field cannot be returned by reference
+// Line: 10
+
+class X
+{
+ readonly int f = 0;
+
+ ref int Test ()
+ {
+ return ref f;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8161: A static readonly field cannot be returned by reference
+// Line: 10
+
+class X
+{
+ static readonly int f;
+
+ static ref int Test ()
+ {
+ return ref f;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8170:
+// Line: 10
+
+public struct S
+{
+ int f;
+
+ public ref S Foo ()
+ {
+ return ref f;
+ }
+}
--- /dev/null
+// CS8170:
+// Line: 8
+
+public struct S
+{
+ public ref S Foo ()
+ {
+ return ref this;
+ }
+}
--- /dev/null
+// CS8171: Cannot initialize a by-value variable `l' with a reference expression
+// Line: 10
+
+class Test
+{
+ int field;
+
+ void Foo ()
+ {
+ int l = ref field;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8172: Cannot initialize a by-reference variable `j' with a value
+// Line: 10
+
+class X
+{
+ static int f;
+
+ public static void Main ()
+ {
+ ref int j = f;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8173: The expression must be of type `long' because it is being assigned by reference
+// Line: 11
+
+public class X
+{
+ int field;
+
+ public static void Main ()
+ {
+ int i = 5;
+ ref long j = ref i;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8174: A declaration of a by-reference variable must have an initializer
+// Line: 8
+
+class X
+{
+ public static void Main ()
+ {
+ ref int j;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8175: Cannot use by-reference variable `v' inside an anonymous method, lambda expression, or query expression
+// Line: 14
+
+using System;
+
+public class Test
+{
+ public static void Main()
+ {
+ var arr = new int [1];
+ ref var v = ref arr [0];
+
+ Action a = delegate {
+ ref var v2 = ref v;
+ };
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8176: Iterators cannot use by-reference variables
+// Line: 12
+
+using System.Collections.Generic;
+
+class X
+{
+ int x;
+
+ IEnumerable<int> Test ()
+ {
+ ref int y = ref x;
+ yield break;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8177: Async methods cannot use by-reference variables
+// Line: 12
+
+using System.Threading.Tasks;
+
+class X
+{
+ int x;
+
+ async Task Test ()
+ {
+ ref int y = ref x;
+ await Task.Yield ();
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8178: `await' cannot be used in an expression containing a call to `X.this[int]' because it returns by reference
+// Line: 12
+
+using System.Threading.Tasks;
+
+class X
+{
+ int x;
+
+ async Task Test ()
+ {
+ Foo (ref this [await Task.FromResult (1)]);
+ }
+
+ ref int this [int arg] => ref x;
+
+ static void Foo (ref int arg)
+ {
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8178: `await' cannot be used in an expression containing a call to `X.Wrap(int)' because it returns by reference
+// Line: 12
+
+using System.Threading.Tasks;
+
+class X
+{
+ int x;
+
+ async Task Test ()
+ {
+ Foo (ref Wrap (await Task.FromResult (1))) = 4;
+ }
+
+ ref int Wrap (int arg)
+ {
+ return ref x;
+ }
+
+ static ref int Foo (ref int arg)
+ {
+ return ref arg;
+ }
+}
\ No newline at end of file
--- /dev/null
+// CS8189: By reference return delegate does not match `C.D()' return type
+// Line: 15
+
+class C
+{
+ delegate ref int D ();
+
+ static int M ()
+ {
+ return 1;
+ }
+
+ static void Main ()
+ {
+ D d = new D (M);
+ }
+}
\ No newline at end of file
cs8141.cs
cs8141-2.cs
cs8144.cs
+cs8157.cs NO ERROR
+cs8160.cs
+cs8161.cs
+cs8170.cs NO ERROR
+cs8170-2.cs
return Clone (Expr.Clone (clonectx));
}
- public virtual Expression CreateExpressionTree (ResolveContext ec)
+ public virtual Expression CreateExpressionTree (ResolveContext rc)
{
+ if (Type.Kind == MemberKind.ByRef) {
+ rc.Report.Error (8153, Expr.Location, "An expression tree lambda cannot contain a call to a method, property, or indexer that returns by reference");
+ return null;
+ }
+
if (ArgType == AType.Default)
- ec.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
+ rc.Report.Error (854, Expr.Location, "An expression tree cannot contain an invocation which uses optional parameter");
- return Expr.CreateExpressionTree (ec);
+ return Expr.CreateExpressionTree (rc);
}
return;
}
+ if (Expr.Type.Kind == MemberKind.ByRef) {
+ Expr.Emit (ec);
+ return;
+ }
+
AddressOp mode = AddressOp.Store;
if (ArgType == AType.Ref)
mode |= AddressOp.Load;
- IMemoryLocation ml = (IMemoryLocation) Expr;
- ml.AddressOf (ec, mode);
+ ((IMemoryLocation)Expr).AddressOf (ec, mode);
}
public Argument EmitToField (EmitContext ec, bool cloneResult)
return all;
}
- public static Arguments CreateForExpressionTree (ResolveContext ec, Arguments args, params Expression[] e)
+ public static Arguments CreateForExpressionTree (ResolveContext rc, Arguments args, params Expression[] e)
{
Arguments all = new Arguments ((args == null ? 0 : args.Count) + e.Length);
for (int i = 0; i < e.Length; ++i) {
- if (e [i] != null)
- all.Add (new Argument (e[i]));
+ var expr = e [i];
+ if (expr != null) {
+ all.Add (new Argument (expr));
+ }
}
if (args != null) {
foreach (Argument a in args.args) {
- Expression tree_arg = a.CreateExpressionTree (ec);
+ Expression tree_arg = a.CreateExpressionTree (rc);
if (tree_arg != null)
all.Add (new Argument (tree_arg));
}
var base_member_type = ((IInterfaceMemberSpec)base_member).MemberType;
if (!TypeSpecComparer.Override.IsEqual (MemberType, base_member_type)) {
Report.SymbolRelatedToPreviousError (base_member);
- if (this is PropertyBasedMember) {
+ if (((base_member_type.Kind ^ MemberType.Kind) & MemberKind.ByRef) != 0) {
+ Report.Error (8148, Location, "`{0}': must {2}return by reference to match overridden member `{1}'",
+ GetSignatureForError (), base_member.GetSignatureForError (), base_member_type.Kind == MemberKind.ByRef ? "" : "not ");
+ } else if (this is PropertyBasedMember) {
Report.Error (1715, Location, "`{0}': type must be `{1}' to match overridden member `{2}'",
GetSignatureForError (), base_member_type.GetSignatureForError (), base_member.GetSignatureForError ());
} else {
return null;
}
+ if (expr is ReferenceExpression) {
+ // Only identify conversion is allowed
+ return null;
+ }
+
e = ImplicitNumericConversion (expr, expr_type, target_type);
if (e != null)
return e;
field_declaration
: opt_attributes
opt_modifiers
- member_type IDENTIFIER
+ ref_member_type IDENTIFIER
{
lexer.parsing_generic_declaration = false;
}
;
+ref_member_type
+ : member_type
+ {
+ $$ = $1;
+ }
+ | REF
+ {
+ lexer.parsing_generic_declaration = true;
+ }
+ type
+ {
+ if (lang_version < LanguageVersion.V_7) {
+ FeatureIsNotAvailable (GetLocation ($1), "byref locals and returns");
+ }
+
+ $$ = new ReferenceTypeExpr ((FullNamedExpression) $3, GetLocation ($1));
+ }
+ ;
+
method_header
: opt_attributes
opt_modifiers
- member_type
+ ref_member_type
method_declaration_name OPEN_PARENS
{
valid_param_mod = ParameterModifierType.All;
}
| opt_attributes
opt_modifiers
- member_type
+ ref_member_type
modifiers method_declaration_name OPEN_PARENS opt_formal_parameter_list CLOSE_PARENS
{
MemberName name = (MemberName) $5;
}
| opt_attributes
opt_modifiers
- member_type
+ ref_member_type
method_declaration_name error
{
Error_SyntaxError (yyToken);
++lexer.parsing_block;
start_block (GetLocation ($1));
}
- expression SEMICOLON
+ lambda_arrow_expression SEMICOLON
{
lexer.parsing_block = 0;
current_block.AddStatement (new ContextualReturn ((Expression) $3));
property_declaration
: opt_attributes
opt_modifiers
- member_type
+ ref_member_type
member_declaration_name
{
lexer.parsing_generic_declaration = false;
if (doc_support)
current_property.DocComment = ConsumeStoredComment ();
+
+ if ($3 is ReferenceTypeExpr) {
+ if (current_property.Get == null) {
+ report.Error (8146, GetLocation ($4), "`{0}': property and indexer which return by reference must have a get accessor", current_property.GetSignatureForError ());
+ }
+
+ if (current_property.Set != null) {
+ report.Error (8147, GetLocation ($4), "`{0}': property and indexer which return by reference cannot have set accessors", current_property.GetSignatureForError ());
+ }
+ }
}
CLOSE_BRACE
{
}
| opt_attributes
opt_modifiers
- member_type
+ ref_member_type
member_declaration_name
{
lexer.parsing_generic_declaration = false;
indexer_declaration
: opt_attributes opt_modifiers
- member_type indexer_declaration_name OPEN_BRACKET
+ ref_member_type indexer_declaration_name OPEN_BRACKET
{
valid_param_mod = ParameterModifierType.Params | ParameterModifierType.DefaultValue;
}
if (doc_support)
current_property.DocComment = ConsumeStoredComment ();
+
+ if ($3 is ReferenceTypeExpr) {
+ if (current_property.Get == null) {
+ report.Error (8146, GetLocation ($4), "`{0}': property and indexer which return by reference must have a get accessor", current_property.GetSignatureForError ());
+ }
+
+ if (current_property.Set != null) {
+ report.Error (8147, GetLocation ($4), "`{0}': property and indexer which return by reference cannot have set accessors", current_property.GetSignatureForError ());
+ }
+ }
current_property = null;
}
: opt_attributes
opt_modifiers
DELEGATE
- member_type type_declaration_name
+ ref_member_type type_declaration_name
OPEN_PARENS
{
valid_param_mod = ParameterModifierType.Ref | ParameterModifierType.Out | ParameterModifierType.Params | ParameterModifierType.DefaultValue;
: {
start_block (Location.Null);
}
- expression // All expressions must handle error or current block won't be restored and breaking ast completely
+ lambda_arrow_expression // All expressions must handle error or current block won't be restored and breaking ast completely
{
Block b = end_block (Location.Null);
b.IsCompilerGenerated = true;
}
;
+lambda_arrow_expression
+ : expression
+ | reference_expression
+ ;
+
expression_or_error
: expression
| error
current_variable = null;
lbag.AddLocation ($$, GetLocation ($1), GetLocation ($7));
}
+ | REF variable_type identifier_inside_body
+ {
+ if (lang_version < LanguageVersion.V_7) {
+ FeatureIsNotAvailable (GetLocation ($1), "byref locals and returns");
+ }
+
+ var lt = (LocatedToken) $3;
+ var li = new LocalVariable (current_block, lt.Value, LocalVariable.Flags.ByRef, lt.Location);
+ current_block.AddLocalName (li);
+ current_variable = new BlockVariable ((FullNamedExpression) $2, li);
+ }
+ opt_local_variable_initializer opt_variable_declarators SEMICOLON
+ {
+ $$ = current_variable;
+ current_variable = null;
+ if ($5 != null) {
+ lbag.AddLocation ($$, PopLocation (), GetLocation ($7));
+ } else {
+ report.Error (8174, GetLocation ($3), "A declaration of a by-reference variable must have an initializer");
+ lbag.AddLocation ($$, GetLocation ($7));
+ }
+ }
;
opt_local_variable_initializer
report.Error (1575, GetLocation ($1), "A stackalloc expression requires [] after type");
$$ = new StackAlloc ((Expression) $2, null, GetLocation ($1));
}
+ | reference_expression
+ ;
+
+reference_expression
+ : REF expression
+ {
+ if (lang_version < LanguageVersion.V_7) {
+ FeatureIsNotAvailable (GetLocation ($1), "byref locals and returns");
+ }
+
+ $$ = new ReferenceExpression ((Expression) $2, GetLocation ($1));
+ }
;
expression_statement
$$ = new Return ((Expression) $2, GetLocation ($1));
lbag.AddStatement ($$, GetLocation ($3));
}
+ | RETURN reference_expression SEMICOLON
+ {
+ $$ = new Return ((Expression) $2, GetLocation ($1));
+ lbag.AddStatement ($$, GetLocation ($3));
+ }
| RETURN expression error
{
Error_SyntaxError (yyToken);
rt = ec.BuiltinTypes.Object;
if (!Delegate.IsTypeCovariant (ec, rt, invoke_method.ReturnType)) {
- Expression ret_expr = new TypeExpression (delegate_method.ReturnType, loc);
- Error_ConversionFailed (ec, delegate_method, ret_expr);
+ Error_ConversionFailed (ec, delegate_method, delegate_method.ReturnType);
}
if (method_group.IsConditionallyExcluded) {
method_group.FlowAnalysis (fc);
}
- void Error_ConversionFailed (ResolveContext ec, MethodSpec method, Expression return_type)
+ void Error_ConversionFailed (ResolveContext ec, MethodSpec method, TypeSpec return_type)
{
var invoke_method = Delegate.GetInvokeMethod (type);
string member_name = method_group.InstanceExpression != null ?
return;
}
+ if (invoke_method.ReturnType.Kind == MemberKind.ByRef) {
+ ec.Report.Error (8189, loc, "By reference return delegate does not match `{0}' return type",
+ Delegate.FullDelegateDesc (invoke_method));
+ return;
+ }
+
ec.Report.Error (407, loc, "A method or delegate `{0} {1}' return type does not match delegate `{2} {3}' return type",
return_type.GetSignatureForError (), member_name,
invoke_method.ReturnType.GetSignatureForError (), Delegate.FullDelegateDesc (invoke_method));
return;
string from_type = type.GetSignatureForError ();
+ if (type.Kind == MemberKind.ByRef)
+ from_type = "ref " + from_type;
string to_type = target.GetSignatureForError ();
+ if (target.Kind == MemberKind.ByRef)
+ to_type = "ref " + to_type;
+
if (from_type == to_type) {
from_type = type.GetSignatureForErrorIncludingAssemblyName ();
to_type = target.GetSignatureForErrorIncludingAssemblyName ();
public Expression ResolveLValue (ResolveContext ec, Expression right_side)
{
int errors = ec.Report.Errors;
- bool out_access = right_side == EmptyExpression.OutAccess;
+ //bool out_access = right_side == EmptyExpression.OutAccess;
Expression e = DoResolveLValue (ec, right_side);
- if (e != null && out_access && !(e is IMemoryLocation)) {
+ //if (e != null && out_access && !(e is IMemoryLocation)) {
// FIXME: There's no problem with correctness, the 'Expr = null' handles that.
// Enabling this 'throw' will "only" result in deleting useless code elsewhere,
//throw new InternalErrorException ("ResolveLValue didn't return an IMemoryLocation: " +
// e.GetType () + " " + e.GetSignatureForError ());
- e = null;
- }
+ // e = null;
+ //}
if (e == null) {
if (errors == ec.Report.Errors) {
if (arg_type == InternalType.VarOutType)
return 0;
+ var ref_arg_type = arg_type as ReferenceContainer;
+ if (ref_arg_type != null) {
+ arg_type = ref_arg_type.Element;
+ }
+
//
// Do full equality check after quick path
//
int arg_count = args == null ? 0 : args.Count;
for (; a_idx < arg_count; a_idx++, ++a_pos) {
- a = args[a_idx];
+ a = args [a_idx];
if (a == null)
continue;
if (p_mod != Parameter.Modifier.PARAMS) {
p_mod = cpd.FixedParameters [a_idx].ModFlags;
- pt = ptypes[a_idx];
+ pt = ptypes [a_idx];
has_unsafe_arg |= pt.IsPointer;
if (p_mod == Parameter.Modifier.PARAMS) {
continue;
}
+ var ref_arg_type = arg_type as ReferenceContainer;
+ if (ref_arg_type != null) {
+ if (ref_arg_type.Element != pt)
+ break;
+
+ return true;
+ }
+
if (!TypeSpecComparer.IsEqual (arg_type, pt))
break;
}
GetSignatureForError ());
}
- return null;
+ return ErrorExpression.Instance;
}
if (right_side == EmptyExpression.LValueMemberAccess) {
// Already reported as CS1648/CS1650
- return null;
+ return ErrorExpression.Instance;
}
if (right_side == EmptyExpression.LValueMemberOutAccess) {
rc.Report.Error (1649, loc, "Members of readonly field `{0}' cannot be passed ref or out (except in a constructor)",
GetSignatureForError ());
}
- return null;
+ return ErrorExpression.Instance;
}
if (IsStatic) {
GetSignatureForError ());
}
- return null;
+ return ErrorExpression.Instance;
}
public override Expression DoResolveLValue (ResolveContext ec, Expression right_side)
Error_NullPropagatingLValue (rc);
if (right_side == EmptyExpression.OutAccess) {
+ if (best_candidate?.MemberType.Kind == MemberKind.ByRef) {
+ if (Arguments?.ContainsEmitWithAwait () == true) {
+ rc.Report.Error (8178, loc, "`await' cannot be used in an expression containing a call to `{0}' because it returns by reference",
+ GetSignatureForError ());
+ }
+
+ return this;
+ }
+
// TODO: best_candidate can be null at this point
INamedBlockVariable variable = null;
if (best_candidate != null && rc.CurrentBlock.ParametersBlock.TopBlock.GetLocalName (best_candidate.Name, rc.CurrentBlock, ref variable) && variable is Linq.RangeVariable) {
return;
}
+ bool dereference = IsRef && !(source is ReferenceExpression);
New n_source = source as New;
if (n_source != null && n_source.CanEmitOptimizedLocalTarget (ec)) {
if (!n_source.Emit (ec, this)) {
if (leave_copy) {
EmitLoad (ec);
- if (IsRef)
+ if (dereference)
ec.EmitLoadFromPtr (type);
}
return;
}
} else {
- if (IsRef)
+ if (dereference)
EmitLoad (ec);
source.Emit (ec);
if (leave_copy) {
ec.Emit (OpCodes.Dup);
- if (IsRef) {
+ if (dereference) {
temp = new LocalTemporary (Type);
temp.Store (ec);
}
}
- if (IsRef)
+ if (dereference)
ec.EmitStoreFromPtr (type);
else
Variable.EmitAssign (ec);
}
public override bool IsRef {
- get { return false; }
+ get { return local_info.IsByRef; }
}
public override string Name {
AnonymousMethodExpression.Error_AddressOfCapturedVar (ec, this, loc);
} else if (local_info.IsFixed) {
ec.Report.Error (1764, loc,
- "Cannot use fixed local `{0}' inside an anonymous method, lambda expression or query expression",
+ "Cannot use fixed variable `{0}' inside an anonymous method, lambda expression or query expression",
+ GetSignatureForError ());
+ } else if (local_info.IsByRef) {
+ ec.Report.Error (8175, loc,
+ "Cannot use by-reference variable `{0}' inside an anonymous method, lambda expression, or query expression",
GetSignatureForError ());
}
protected override Expression DoResolve (ResolveContext rc)
{
ResolveConditionalAccessReceiver (rc);
- return DoResolveInvocation (rc);
+ return DoResolveInvocation (rc, null);
}
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
return res.Resolve (rc);
}
+ if (right_side != null) {
+ if (eclass != ExprClass.Unresolved)
+ return this;
+
+ var res = DoResolveInvocation (rc, right_side);
+ if (res == null)
+ return null;
+
+ return res;
+ }
+
return base.DoResolveLValue (rc, right_side);
}
- Expression DoResolveInvocation (ResolveContext ec)
+ Expression DoResolveInvocation (ResolveContext ec, Expression rhs)
{
Expression member_expr;
var atn = expr as ATypeNameExpression;
IsSpecialMethodInvocation (ec, method, loc);
eclass = ExprClass.Value;
+
+ if (type.Kind == MemberKind.ByRef) {
+ if (rhs == null && arguments?.ContainsEmitWithAwait () == true) {
+ ec.Report.Error (8178, loc, "`await' cannot be used in an expression containing a call to `{0}' because it returns by reference",
+ GetSignatureForError ());
+ }
+
+ if (rhs != EmptyExpression.OutAccess)
+ return ByRefDereference.Create (this).Resolve (ec);
+ }
+
return this;
}
}
}
+ class ReferenceTypeExpr : TypeExpr
+ {
+ FullNamedExpression element;
+
+ public ReferenceTypeExpr (FullNamedExpression element, Location loc)
+ {
+ this.element = element;
+ this.loc = loc;
+ }
+
+ public override TypeSpec ResolveAsType (IMemberContext mc, bool allowUnboundTypeArguments = false)
+ {
+ type = element.ResolveAsType (mc);
+ if (type == null)
+ return null;
+
+ eclass = ExprClass.Type;
+ type = ReferenceContainer.MakeType (mc.Module, type);
+
+ return type;
+ }
+
+ public override string GetSignatureForError ()
+ {
+ return "ref " + element.GetSignatureForError ();
+ }
+
+ public override object Accept (StructuralVisitor visitor)
+ {
+ return visitor.Visit (this);
+ }
+ }
+
class FixedBufferPtr : Expression
{
readonly Expression array;
return Reachability.CreateUnreachable ();
}
}
+
+ class ReferenceExpression : CompositeExpression
+ {
+ public ReferenceExpression (Expression expr, Location loc)
+ : base (expr)
+ {
+ this.loc = loc;
+ }
+
+ static bool CanBeByRef (Expression expr)
+ {
+ if (expr is IAssignMethod)
+ return true;
+
+ var invocation = expr as Invocation;
+ if (invocation?.Type.Kind == MemberKind.ByRef)
+ return true;
+
+ return false;
+ }
+
+ public override Expression CreateExpressionTree (ResolveContext rc)
+ {
+ throw new NotSupportedException ("ET");
+ }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ var res = expr.DoResolveLValue (rc, EmptyExpression.OutAccess);
+ if (res == null || !CanBeByRef (res)) {
+ if (res?.Type != InternalType.ErrorType)
+ rc.Report.Error (8156, expr.Location, "An expression cannot be used in this context because it may not be returned by reference");
+ return ErrorExpression.Instance;
+ }
+
+ type = res.Type;
+ var type_container = type as ReferenceContainer;
+ if (type_container != null)
+ type = type_container.Element;
+
+ expr = res;
+ eclass = ExprClass.Value;
+ return this;
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ var ml = expr as IMemoryLocation;
+ if (ml != null)
+ ml.AddressOf (ec, AddressOp.LoadStore);
+ else
+ expr.Emit (ec);
+ }
+
+ public override void Error_ValueCannotBeConverted (ResolveContext rc, TypeSpec target, bool expl)
+ {
+ rc.Report.Error (8173, loc, "The expression must be of type `{0}' because it is being assigned by reference", target.GetSignatureForError ());
+ }
+ }
+
+ class ByRefDereference : CompositeExpression, IMemoryLocation, IAssignMethod
+ {
+ bool prepared;
+ LocalTemporary temporary;
+
+ private ByRefDereference (Expression expr)
+ : base (expr)
+ {
+ }
+
+ public static Expression Create (Expression expr)
+ {
+ var rc = expr.Type as ReferenceContainer;
+ if (rc == null)
+ return expr;
+
+ return new ByRefDereference (expr) {
+ type = rc.Element
+ };
+ }
+
+ public void AddressOf (EmitContext ec, AddressOp mode)
+ {
+ expr.Emit (ec);
+ }
+
+ public void Emit (EmitContext ec, bool leave_copy)
+ {
+ Emit (ec);
+ if (leave_copy) {
+ ec.Emit (OpCodes.Dup);
+ temporary = new LocalTemporary (type);
+ temporary.Store (ec);
+ }
+ }
+
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool isCompound)
+ {
+ prepared = isCompound;
+
+ expr.Emit (ec);
+
+ if (isCompound)
+ ec.Emit (OpCodes.Dup);
+
+ source.Emit (ec);
+ if (leave_copy) {
+ throw new NotImplementedException ("leave_copy");
+ }
+
+ ec.EmitStoreFromPtr (type);
+
+ if (temporary != null) {
+ temporary.Emit (ec);
+ temporary.Release (ec);
+ }
+ }
+
+ protected override Expression DoResolve (ResolveContext rc)
+ {
+ eclass = ExprClass.Variable;
+ return this;
+ }
+
+ public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
+ {
+ return DoResolve (rc);
+ }
+
+ public override void Emit (EmitContext ec)
+ {
+ if (!prepared)
+ base.Emit(ec);
+
+ ec.EmitLoadFromPtr (type);
+ }
+
+ public override object Accept (StructuralVisitor visitor)
+ {
+ return visitor.Visit (this);
+ }
+ }
}
if (ec is PointerContainer)
return PointerContainer.MakeType (context.Module, et);
+ if (ec is ReferenceContainer)
+ return ReferenceContainer.MakeType (context.Module, et);
+
throw new NotImplementedException ();
}
return;
if (!CheckType (ret, parent, out iterator_type, out is_enumerable)) {
- parent.Compiler.Report.Error (1624, method.Location,
- "The body of `{0}' cannot be an iterator block " +
- "because `{1}' is not an iterator interface type",
- method.GetSignatureForError (),
- ret.GetSignatureForError ());
+ if (ret.Kind == MemberKind.ByRef) {
+ parent.Compiler.Report.Error (8154, method.Location,
+ "The body of `{0}' cannot be an iterator block because the method returns by reference",
+ method.GetSignatureForError ());
+ } else {
+ parent.Compiler.Report.Error (1624, method.Location,
+ "The body of `{0}' cannot be an iterator block because `{1}' is not an iterator interface type",
+ method.GetSignatureForError (), ret.GetSignatureForError ());
+ }
return;
}
public override Expression CreateExpressionTree (ResolveContext ec)
{
+ if (Expr is ReferenceExpression) {
+ ec.Report.Error (8155, Expr.Location, "Lambda expressions that return by reference cannot be converted to expression trees");
+ return null;
+ }
+
return Expr.CreateExpressionTree (ec);
}
if (Expr == null)
return false;
+ if (Expr is ReferenceExpression) {
+ // CSC: should be different error code
+ ec.Report.Error (8149, loc, "By-reference returns can only be used in lambda expressions that return by reference");
+ return false;
+ }
+
statement = Expr as ExpressionStatement;
if (statement == null) {
var reduced = Expr as IReducedExpressionStatement;
Enum = 1 << 14,
Interface = 1 << 15,
TypeParameter = 1 << 16,
+ ByRef = 1 << 17,
ArrayType = 1 << 19,
PointerType = 1 << 20,
Report.Error (737, container.Location,
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' is not public",
container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError ());
+ } else if (mi.ReturnType.Kind == MemberKind.ByRef) {
+ Report.Error (8152, container.Location,
+ "`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not return by reference",
+ container.GetSignatureForError (), mi.GetSignatureForError (), candidate.GetSignatureForError (),
+ candidate.ReturnType.GetSignatureForError ());
} else {
Report.Error (738, container.Location,
"`{0}' does not implement interface member `{1}' and the best implementing candidate `{2}' return type `{3}' does not match interface member return type `{4}'",
return false;
}
+ if (MemberType.Kind == MemberKind.ByRef) {
+ Report.Error (8145, Location, "Auto-implemented properties cannot return by reference");
+ return false;
+ }
+
if (Compiler.Settings.Version < LanguageVersion.V_3 && Initializer == null)
Report.FeatureIsNotAvailable (Compiler, Location, "auto-implemented properties");
if (expr == null)
return false;
+ if (expr is ReferenceExpression && block_return_type.Kind != MemberKind.ByRef) {
+ ec.Report.Error (8149, loc, "By-reference returns can only be used in methods that return by reference");
+ return false;
+ }
+
if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
- expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
+ if (block_return_type.Kind == MemberKind.ByRef) {
+ var ref_expr = Expr as ReferenceExpression;
+ if (ref_expr == null) {
+ ec.Report.Error (8150, loc, "By-reference return is required when method returns by reference");
+ return false;
+ }
- if (expr == null) {
- if (am != null && block_return_type == ec.ReturnType) {
- ec.Report.Error (1662, loc,
- "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
- am.ContainerType, am.GetSignatureForError ());
+ var byref_return = (ReferenceContainer)block_return_type;
+
+ if (expr.Type != byref_return.Element) {
+ ec.Report.Error (8151, loc, "The return by reference expression must be of type `{0}' because this method returns by reference",
+ byref_return.GetSignatureForError ());
+ return false;
+ }
+ } else {
+
+ expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
+
+ if (expr == null) {
+ if (am != null && block_return_type == ec.ReturnType) {
+ ec.Report.Error (1662, loc,
+ "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
+ am.ContainerType, am.GetSignatureForError ());
+ }
+ return false;
}
- return false;
}
}
}
if (type == null) {
- type = type_expr.ResolveAsType (bc);
+ type = ResolveTypeExpression (bc);
if (type == null)
return false;
}
if (initializer != null) {
+ if (li.IsByRef) {
+ if (!(initializer is ReferenceExpression)) {
+ bc.Report.Error (8172, loc, "Cannot initialize a by-reference variable `{0}' with a value", li.Name);
+ return false;
+ }
+
+ if (bc.CurrentAnonymousMethod is AsyncInitializer) {
+ bc.Report.Error (8177, loc, "Async methods cannot use by-reference variables");
+ } else if (bc.CurrentIterator != null) {
+ bc.Report.Error (8176, loc, "Iterators cannot use by-reference variables");
+ }
+
+ } else {
+ if (initializer is ReferenceExpression) {
+ bc.Report.Error (8171, loc, "Cannot initialize a by-value variable `{0}' with a reference expression", li.Name);
+ return false;
+ }
+ }
+
initializer = ResolveInitializer (bc, li, initializer);
// li.Variable.DefinitelyAssigned
}
return a.ResolveStatement (bc);
}
+ protected virtual TypeSpec ResolveTypeExpression (BlockContext bc)
+ {
+ return type_expr.ResolveAsType (bc);
+ }
+
protected override void DoEmit (EmitContext ec)
{
li.CreateBuilder (ec);
UsingVariable = 1 << 7,
IsLocked = 1 << 8,
SymbolFileHidden = 1 << 9,
+ ByRef = 1 << 10,
ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
}
}
}
+ public bool IsByRef => (flags & Flags.ByRef) != 0;
+
public bool IsCompilerGenerated {
get {
return (flags & Flags.CompilerGenerated) != 0;
throw new InternalErrorException ("Already created variable `{0}'", name);
}
- //
- // All fixed variabled are pinned, a slot has to be alocated
- //
- builder = ec.DeclareLocal (Type, IsFixed);
+ if (IsByRef) {
+ builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, Type), IsFixed);
+ } else {
+ //
+ // All fixed variabled are pinned, a slot has to be alocated
+ //
+ builder = ec.DeclareLocal(Type, IsFixed);
+ }
+
if ((flags & Flags.SymbolFileHidden) == 0)
ec.DefineLocalVariable (name, builder);
}
if ((flags & Flags.CompilerGenerated) != 0)
CreateBuilder (ec);
- ec.Emit (OpCodes.Ldloca, builder);
+ if (IsByRef)
+ ec.Emit (OpCodes.Ldloc, builder);
+ else
+ ec.Emit (OpCodes.Ldloca, builder);
}
public static string GetCompilerGeneratedName (Block block)
}
}
+ [System.Diagnostics.DebuggerDisplay("{DisplayDebugInfo()}")]
class ReferenceContainer : ElementTypeSpec
{
ReferenceContainer (TypeSpec element)
- : base (MemberKind.Class, element, null) // TODO: Kind.Class is most likely wrong
+ : base (MemberKind.ByRef, element, null)
{
}
}
}
+ string DisplayDebugInfo()
+ {
+ return "ref " + GetSignatureForError();
+ }
+
public override MetaType GetMetaInfo ()
{
if (info == null) {
return info;
}
+ public override string GetSignatureForError ()
+ {
+ return Element.GetSignatureForError ();
+ }
+
public static ReferenceContainer MakeType (ModuleContainer module, TypeSpec element)
{
+ if (element.Kind == MemberKind.ByRef)
+ throw new ArgumentException ();
+
ReferenceContainer pc;
if (!module.ReferenceTypesCache.TryGetValue (element, out pc)) {
pc = new ReferenceContainer (element);
--- /dev/null
+// Compiler options: -unsafe
+
+public unsafe class X
+{
+ int field;
+ int* ufield;
+
+ public static void Main ()
+ {
+ int i = 5;
+ ref int j = ref i;
+
+ var x = new X ();
+ ref var v = ref x.TestMethod ();
+ }
+
+ ref int TestMethod ()
+ {
+ return ref field;
+ }
+
+ ref int TestProperty {
+ get {
+ return ref field;
+ }
+ }
+
+ ref int this [long arg] {
+ get {
+ return ref field;
+ }
+ }
+
+ unsafe ref int* Foo ()
+ {
+ return ref ufield;
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+
+class X
+{
+ int field;
+
+ static void Main ()
+ {
+ var x = new X ();
+ x.Run ();
+ }
+
+ void Run ()
+ {
+ Test (ref this[0]);
+ Test (ref Prop);
+ }
+
+ static int Test (ref int y)
+ {
+ return y;
+ }
+
+ ref int this [int y] {
+ get {
+ return ref field;
+ }
+ }
+
+ ref int Prop {
+ get {
+ return ref field;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+class X
+{
+ int x;
+
+ static void Main ()
+ {
+ var x = new X ();
+ Foo (ref x.Wrap (1));
+ Foo (ref x.Prop);
+ Foo (ref x[""]);
+ }
+
+ ref int Wrap (int arg)
+ {
+ return ref x;
+ }
+
+ ref int Prop {
+ get {
+ return ref x;
+ }
+ }
+
+ ref int this [string arg] {
+ get {
+ return ref x;
+ }
+ }
+
+ static void Foo (ref int arg)
+ {
+ }
+}
\ No newline at end of file
--- /dev/null
+class X
+{
+ int field;
+
+ public static int Main ()
+ {
+ var x = new X ();
+
+ x.field = 5;
+ if (!x.Test1 ())
+ return 1;
+
+ x.Test2 ();
+
+ if (x.Test3 ()++ != 6)
+ return 2;
+
+ if (x.field != 7)
+ return 3;
+
+ return 0;
+ }
+
+ bool Test1 ()
+ {
+ ref var x = ref field;
+ int v = x;
+ ++x;
+
+ return x == 6;
+ }
+
+ void Test2 ()
+ {
+ ref int x = ref field;
+ x.ToString ();
+ }
+
+ ref int Test3 ()
+ {
+ ref int l = ref field;
+ ref int v = ref l;
+ return ref l;
+ }
+}
\ No newline at end of file
--- /dev/null
+class X
+{
+ static int field;
+
+ public static int Main ()
+ {
+ Test () = 3;
+
+ if (field != (byte) 3)
+ return 1;
+
+ G<string>.Test (ref field) = 6;
+ if (field != 6)
+ return 2;
+
+ --Test ();
+ if (field != 5)
+ return 3;
+
+ Test (ref Test (), ref Test ());
+
+ return 0;
+ }
+
+ static ref int Test ()
+ {
+ return ref field;
+ }
+
+ static void Test<T> (ref T a, ref int b)
+ {
+ }
+
+ static void Test2<T> (ref T arg)
+ {
+ Test (ref arg, ref Test ());
+ }
+}
+
+class G<U>
+{
+ public static ref T Test<T> (ref T arg)
+ {
+ return ref arg;
+ }
+}
\ No newline at end of file
</method>
</type>
</test>
+ <test name="test-ref-01.cs">
+ <type name="X">
+ <method name="Void Main()" attrs="150">
+ <size>20</size>
+ </method>
+ <method name="Int32& TestMethod()" attrs="129">
+ <size>15</size>
+ </method>
+ <method name="Int32& get_TestProperty()" attrs="2177">
+ <size>15</size>
+ </method>
+ <method name="Int32& get_Item(Int64)" attrs="2177">
+ <size>15</size>
+ </method>
+ <method name="Int32*& Foo()" attrs="129">
+ <size>15</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
+ <test name="test-ref-02.cs">
+ <type name="X">
+ <method name="Void Main()" attrs="145">
+ <size>14</size>
+ </method>
+ <method name="Void Run()" attrs="129">
+ <size>27</size>
+ </method>
+ <method name="Int32 Test(Int32 ByRef)" attrs="145">
+ <size>11</size>
+ </method>
+ <method name="Int32& get_Item(Int32)" attrs="2177">
+ <size>15</size>
+ </method>
+ <method name="Int32& get_Prop()" attrs="2177">
+ <size>15</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
+ <test name="test-ref-03.cs">
+ <type name="X">
+ <method name="Void Main()" attrs="145">
+ <size>47</size>
+ </method>
+ <method name="Int32& Wrap(Int32)" attrs="129">
+ <size>15</size>
+ </method>
+ <method name="Int32& get_Prop()" attrs="2177">
+ <size>15</size>
+ </method>
+ <method name="Int32& get_Item(System.String)" attrs="2177">
+ <size>15</size>
+ </method>
+ <method name="Void Foo(Int32 ByRef)" attrs="145">
+ <size>2</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
+ <test name="test-ref-04.cs">
+ <type name="X">
+ <method name="Int32 Main()" attrs="150">
+ <size>93</size>
+ </method>
+ <method name="Boolean Test1()" attrs="129">
+ <size>30</size>
+ </method>
+ <method name="Void Test2()" attrs="129">
+ <size>22</size>
+ </method>
+ <method name="Int32& Test3()" attrs="129">
+ <size>19</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
+ <test name="test-ref-05.cs">
+ <type name="X">
+ <method name="Int32 Main()" attrs="150">
+ <size>108</size>
+ </method>
+ <method name="Int32& Test()" attrs="145">
+ <size>14</size>
+ </method>
+ <method name="Void Test[T](T ByRef, Int32 ByRef)" attrs="145">
+ <size>2</size>
+ </method>
+ <method name="Void Test2[T](T ByRef)" attrs="145">
+ <size>13</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ <type name="G`1[U]">
+ <method name="T& Test[T](T ByRef)" attrs="150">
+ <size>10</size>
+ </method>
+ <method name="Void .ctor()" attrs="6278">
+ <size>7</size>
+ </method>
+ </type>
+ </test>
<test name="test-static-using-01.cs">
<type name="A.B.X">
<method name="Int32 Test()" attrs="150">
EXTRA_DIST = $(MONO_BTLS_SOURCES_FILES)
CMAKE_VERBOSE=$(if $(V),VERBOSE=1,)
+NINJA_VERBOSE=$(if ($V),-v,)
if NINJA
NINJA_ARGS = -G Ninja
if NINJA
build-shared/libmono-btls-shared$(libsuffix): build-shared/$(BUILDFILE) $(MONO_BTLS_SOURCES_FILES)
- ninja -C build-shared $(CMAKE_VERBOSE)
+ ninja -C build-shared $(NINJA_VERBOSE)
else
build-shared/libmono-btls-shared$(libsuffix): build-shared/$(BUILDFILE) $(MONO_BTLS_SOURCES_FILES)
$(MAKE) -C build-shared $(CMAKE_VERBOSE)
sre-save.c \
custom-attrs.c \
fdhandle.h \
- fdhandle.c
+ fdhandle.c \
+ callspec.h \
+ callspec.c
# These source files have compile time dependencies on GC code
gc_dependent_sources = \
--- /dev/null
+/**
+ * \file
+ * Call specification facilities for the Mono Runtime.
+ *
+ * Author:
+ * Paolo Molaro (lupus@ximian.com)
+ * Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full
+ * license information.
+ */
+#include "metadata.h"
+#include "callspec.h"
+#include "assembly.h"
+#include "class-internals.h"
+#include "debug-helpers.h"
+
+static MonoAssembly *prog_assembly;
+
+gboolean
+mono_callspec_eval_exception (MonoClass *klass, MonoCallSpec *spec)
+{
+ int include = 0;
+ int i;
+
+ if (!klass)
+ return FALSE;
+
+ for (i = 0; i < spec->len; i++) {
+ MonoTraceOperation *op = &spec->ops [i];
+ int inc = 0;
+
+ switch (op->op) {
+ case MONO_TRACEOP_EXCEPTION:
+ if (strcmp ("", op->data) == 0 &&
+ strcmp ("all", op->data2) == 0)
+ inc = 1;
+ else if (strcmp ("", op->data) == 0 ||
+ strcmp (klass->name_space, op->data) == 0)
+ if (strcmp (klass->name, op->data2) == 0)
+ inc = 1;
+ break;
+ default:
+ break;
+ }
+ if (op->exclude) {
+ if (inc)
+ include = 0;
+ } else if (inc)
+ include = 1;
+ }
+
+ return include;
+}
+
+gboolean mono_callspec_eval (MonoMethod *method, const MonoCallSpec *spec)
+{
+ int include = 0;
+ int i;
+
+ for (i = 0; i < spec->len; i++) {
+ MonoTraceOperation *op = &spec->ops[i];
+ int inc = 0;
+
+ switch (op->op) {
+ case MONO_TRACEOP_ALL:
+ inc = 1;
+ break;
+ case MONO_TRACEOP_PROGRAM:
+ if (prog_assembly &&
+ (method->klass->image ==
+ mono_assembly_get_image (prog_assembly)))
+ inc = 1;
+ break;
+ case MONO_TRACEOP_WRAPPER:
+ if ((method->wrapper_type ==
+ MONO_WRAPPER_NATIVE_TO_MANAGED) ||
+ (method->wrapper_type ==
+ MONO_WRAPPER_MANAGED_TO_NATIVE))
+ inc = 1;
+ break;
+ case MONO_TRACEOP_METHOD:
+ if (mono_method_desc_full_match (
+ (MonoMethodDesc *)op->data, method))
+ inc = 1;
+ break;
+ case MONO_TRACEOP_CLASS:
+ if (strcmp (method->klass->name_space, op->data) == 0)
+ if (strcmp (method->klass->name, op->data2) ==
+ 0)
+ inc = 1;
+ break;
+ case MONO_TRACEOP_ASSEMBLY:
+ if (strcmp (mono_image_get_name (method->klass->image),
+ op->data) == 0)
+ inc = 1;
+ break;
+ case MONO_TRACEOP_NAMESPACE:
+ if (strcmp (method->klass->name_space, op->data) == 0)
+ inc = 1;
+ break;
+ case MONO_TRACEOP_EXCEPTION:
+ break;
+ }
+ if (op->exclude) {
+ if (inc)
+ include = 0;
+ } else if (inc) {
+ include = 1;
+ }
+ }
+ return include;
+}
+
+static int is_filenamechar (char p)
+{
+ if (p >= 'A' && p <= 'Z')
+ return TRUE;
+ if (p >= 'a' && p <= 'z')
+ return TRUE;
+ if (p >= '0' && p <= '9')
+ return TRUE;
+ if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
+ return TRUE;
+ return FALSE;
+}
+
+static char *get_string (char **in)
+{
+ char *start = *in;
+ char *p = *in;
+ while (is_filenamechar (*p)) {
+ p++;
+ }
+ size_t len = p - start;
+ char *ret = (char *)g_malloc (len + 1);
+ memcpy (ret, start, len);
+ ret [len] = 0;
+ *in = p;
+ return ret;
+}
+
+enum Token {
+ TOKEN_METHOD,
+ TOKEN_CLASS,
+ TOKEN_ALL,
+ TOKEN_PROGRAM,
+ TOKEN_EXCEPTION,
+ TOKEN_NAMESPACE,
+ TOKEN_WRAPPER,
+ TOKEN_STRING,
+ TOKEN_EXCLUDE,
+ TOKEN_DISABLED,
+ TOKEN_SEPARATOR,
+ TOKEN_END,
+ TOKEN_ERROR
+};
+
+static int get_token (char **in, char **extra, char **errstr)
+{
+ char *p = *in;
+ while (p[0] == '+')
+ p++;
+
+ *extra = NULL;
+
+ if (p[0] == '\0') {
+ *in = p;
+ return TOKEN_END;
+ }
+ if (p[0] == 'M' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_METHOD;
+ }
+ if (p[0] == 'N' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_NAMESPACE;
+ }
+ if (p[0] == 'T' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_CLASS;
+ }
+ if (p[0] == 'E' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_EXCEPTION;
+ }
+ if (*p == '-') {
+ p++;
+ *in = p;
+ return TOKEN_EXCLUDE;
+ }
+ if (is_filenamechar (*p)) {
+ *extra = get_string (&p);
+ *in = p;
+ if (strcmp (*extra, "all") == 0)
+ return TOKEN_ALL;
+ if (strcmp (*extra, "program") == 0)
+ return TOKEN_PROGRAM;
+ if (strcmp (*extra, "wrapper") == 0)
+ return TOKEN_WRAPPER;
+ if (strcmp (*extra, "disabled") == 0)
+ return TOKEN_DISABLED;
+ return TOKEN_STRING;
+ }
+ if (*p == ',') {
+ p++;
+ *in = p;
+ return TOKEN_SEPARATOR;
+ }
+
+ *errstr = g_strdup_printf ("Syntax error at or around '%s'", p);
+ return TOKEN_ERROR;
+}
+
+static int get_spec (char **in, MonoCallSpec *spec, char **errstr)
+{
+ int n = spec->len;
+ char *extra = NULL;
+
+ int token = get_token (in, &extra, errstr);
+ gboolean exclude = FALSE;
+ if (token == TOKEN_EXCLUDE) {
+ exclude = TRUE;
+ token = get_token (in, &extra, errstr);
+ if (token == TOKEN_EXCLUDE || token == TOKEN_DISABLED) {
+ *errstr = g_strdup_printf ("Expecting an expression");
+ token = TOKEN_ERROR;
+ goto out;
+ }
+ }
+ if (token == TOKEN_END || token == TOKEN_SEPARATOR ||
+ token == TOKEN_ERROR)
+ goto out;
+
+ if (token == TOKEN_DISABLED) {
+ spec->enabled = FALSE;
+ goto out;
+ }
+
+ if (token == TOKEN_METHOD) {
+ MonoMethodDesc *desc = mono_method_desc_new (extra, TRUE);
+ if (desc == NULL) {
+ *errstr =
+ g_strdup_printf ("Invalid method name: %s", extra);
+ token = TOKEN_ERROR;
+ goto out;
+ }
+ spec->ops[n].op = MONO_TRACEOP_METHOD;
+ spec->ops[n].data = desc;
+ } else if (token == TOKEN_ALL)
+ spec->ops[n].op = MONO_TRACEOP_ALL;
+ else if (token == TOKEN_PROGRAM)
+ spec->ops[n].op = MONO_TRACEOP_PROGRAM;
+ else if (token == TOKEN_WRAPPER)
+ spec->ops[n].op = MONO_TRACEOP_WRAPPER;
+ else if (token == TOKEN_NAMESPACE) {
+ spec->ops[n].op = MONO_TRACEOP_NAMESPACE;
+ spec->ops[n].data = g_strdup (extra);
+ } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION) {
+ char *p = strrchr (extra, '.');
+ if (p) {
+ *p++ = 0;
+ spec->ops[n].data = g_strdup (extra);
+ spec->ops[n].data2 = g_strdup (p);
+ } else {
+ spec->ops[n].data = g_strdup ("");
+ spec->ops[n].data2 = g_strdup (extra);
+ }
+ spec->ops[n].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS
+ : MONO_TRACEOP_EXCEPTION;
+ } else if (token == TOKEN_STRING) {
+ spec->ops[n].op = MONO_TRACEOP_ASSEMBLY;
+ spec->ops[n].data = g_strdup (extra);
+ } else {
+ *errstr =
+ g_strdup_printf ("Syntax error in method specification");
+ token = TOKEN_ERROR;
+ goto out;
+ }
+
+ if (exclude)
+ spec->ops[n].exclude = 1;
+
+ spec->len = n + 1;
+ token = TOKEN_SEPARATOR;
+out:
+ if (extra != NULL) {
+ g_free (extra);
+ }
+ return token;
+}
+
+gboolean
+mono_callspec_parse (const char *options, MonoCallSpec *spec, char **errstr)
+{
+ char *p = (char *)options;
+ int size = 1;
+ int token;
+
+ memset (spec, 0, sizeof (*spec));
+ *errstr = NULL;
+
+ spec->enabled = TRUE;
+ if (*p == 0) {
+ spec->len = 1;
+ spec->ops = g_new0 (MonoTraceOperation, 1);
+ spec->ops[0].op = MONO_TRACEOP_ALL;
+ return TRUE;
+ }
+
+ for (p = (char *)options; *p != 0; p++)
+ if (*p == ',')
+ size++;
+
+ spec->ops = g_new0 (MonoTraceOperation, size);
+
+ p = (char *)options;
+
+ while ((token = (get_spec (&p, spec, errstr))) != TOKEN_END) {
+ if (token == TOKEN_ERROR)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void mono_callspec_cleanup (MonoCallSpec *spec)
+{
+ if (spec->ops != NULL) {
+ g_free (spec->ops);
+ }
+ memset (spec, 0, sizeof (*spec));
+}
+
+void
+mono_callspec_set_assembly (MonoAssembly *assembly)
+{
+ prog_assembly = assembly;
+}
--- /dev/null
+/**
+ * \file
+ */
+
+#ifndef __MONO_CALLSPEC_H__
+#define __MONO_CALLSPEC_H__
+#include <glib.h>
+#include <mono/utils/mono-compiler.h>
+
+typedef enum {
+ MONO_TRACEOP_ALL,
+ MONO_TRACEOP_PROGRAM,
+ MONO_TRACEOP_METHOD,
+ MONO_TRACEOP_ASSEMBLY,
+ MONO_TRACEOP_CLASS,
+ MONO_TRACEOP_NAMESPACE,
+ MONO_TRACEOP_EXCEPTION,
+ MONO_TRACEOP_WRAPPER,
+} MonoTraceOpcode;
+
+typedef struct {
+ MonoTraceOpcode op;
+ int exclude;
+ void *data, *data2;
+} MonoTraceOperation;
+
+typedef struct {
+ int len;
+ gboolean enabled;
+ MonoTraceOperation *ops;
+} MonoCallSpec;
+
+G_BEGIN_DECLS
+
+MONO_PROFILER_API gboolean mono_callspec_parse (const char *options,
+ MonoCallSpec *spec,
+ char **errstr);
+MONO_PROFILER_API void mono_callspec_cleanup (MonoCallSpec *spec);
+MONO_PROFILER_API gboolean mono_callspec_eval_exception (MonoClass *klass,
+ MonoCallSpec *spec);
+MONO_PROFILER_API gboolean mono_callspec_eval (MonoMethod *method,
+ const MonoCallSpec *spec);
+void mono_callspec_set_assembly (MonoAssembly *assembly);
+
+G_END_DECLS
+
+#endif /* __MONO_CALLSPEC_H__ */
ghcimpl = cached_info.ghcimpl;
has_cctor = cached_info.has_cctor;
} else if (klass->rank == 1 && klass->byval_arg.type == MONO_TYPE_SZARRAY) {
- /* SZARRAY can have 2 vtable layouts, with and without the stelemref method.
+ /* SZARRAY can have 3 vtable layouts, with and without the stelemref method and enum element type
* The first slot if for array with.
*/
- static int szarray_vtable_size[2] = { 0 };
+ static int szarray_vtable_size[3] = { 0 };
- int slot = MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg) ? 0 : 1;
+ int slot;
+
+ if (MONO_TYPE_IS_REFERENCE (&klass->element_class->byval_arg))
+ slot = 0;
+ else if (klass->element_class->enumtype)
+ slot = 1;
+ else
+ slot = 2;
/* SZARRAY case */
if (!szarray_vtable_size [slot]) {
if (klass->rank == 1 && klass->byval_arg.type != MONO_TYPE_ARRAY) {
MonoType *args [1];
- /* generic IList, ICollection, IEnumerable */
- interface_count = 2;
+ /* IList and IReadOnlyList -> 2x if enum*/
+ interface_count = klass->element_class->enumtype ? 4 : 2;
interfaces = (MonoClass **)mono_image_alloc0 (klass->image, sizeof (MonoClass*) * interface_count);
args [0] = &klass->element_class->byval_arg;
mono_defaults.generic_ilist_class, 1, args, FALSE);
interfaces [1] = mono_class_bind_generic_parameters (
mono_defaults.generic_ireadonlylist_class, 1, args, FALSE);
+ if (klass->element_class->enumtype) {
+ args [0] = mono_class_enum_basetype (klass->element_class);
+ interfaces [2] = mono_class_bind_generic_parameters (
+ mono_defaults.generic_ilist_class, 1, args, FALSE);
+ interfaces [3] = mono_class_bind_generic_parameters (
+ mono_defaults.generic_ireadonlylist_class, 1, args, FALSE);
+ }
} else if (mono_class_is_ginst (klass)) {
MonoClass *gklass = mono_class_get_generic_class (klass)->container_class;
#include "mono/utils/mono-hwcap.h"
#include "mono/utils/mono-logger-internals.h"
#include "mono/metadata/w32handle.h"
+#include "mono/metadata/callspec.h"
#include "mini.h"
#include "jit.h"
* Need to call this before mini_init () so we can trace methods
* compiled there too.
*/
- mono_jit_trace_calls = mono_trace_parse_options (trace_options);
+ mono_jit_trace_calls = mono_trace_set_options (trace_options);
if (mono_jit_trace_calls == NULL)
exit (1);
}
* Need to call this before mini_init () so we can trace methods
* compiled there too.
*/
- mono_jit_trace_calls = mono_trace_parse_options (trace_options);
+ mono_jit_trace_calls = mono_trace_set_options (trace_options);
if (mono_jit_trace_calls == NULL)
exit (1);
}
return 2;
}
- if (trace_options != NULL)
- mono_trace_set_assembly (assembly);
+ mono_callspec_set_assembly (assembly);
if (mono_compile_aot || action == DO_EXEC) {
const char *error;
gboolean
mono_jit_set_trace_options (const char* options)
{
- MonoTraceSpec *trace_opt = mono_trace_parse_options (options);
+ MonoCallSpec *trace_opt = mono_trace_set_options (options);
if (trace_opt == NULL)
return FALSE;
mono_jit_trace_calls = trace_opt;
valuetype [mscorlib]System.Decimal)
IL_0028: ret
}
+
+ .method private hidebysig static void fail_inline() cil managed
+ {
+ // Code size 9 (0x9)
+ .maxstack 8
+ IL_0000: ldc.i4.s 16
+ IL_0002: conv.u
+ IL_0003: ldc.i4.1
+ IL_0004: mul.ovf.un
+ IL_0005: localloc
+ IL_0007: pop
+ IL_0008: ret
+ }
+
+ .method private hidebysig static int32
+ always_inline(int32 op) cil managed aggressiveinlining
+ {
+ // Code size 24 (0x18)
+ .maxstack 8
+ IL_0000: ldarg.0
+ IL_0001: brfalse.s IL_0009
+
+ IL_0003: ldarg.0
+ IL_0004: ldc.i4.2
+ IL_0005: beq.s IL_000b
+
+ IL_0007: br.s IL_000d
+
+ IL_0009: ldc.i4.0
+ IL_000a: ret
+
+ IL_000b: ldc.i4.3
+ IL_000c: ret
+
+ IL_000d: call void Tests::fail_inline()
+ IL_0012: newobj instance void [mscorlib]System.Exception::.ctor()
+ IL_0017: throw
+ }
+
+ .method public hidebysig static int32 test_3_regress_59608() cil managed
+ {
+ .maxstack 8
+ IL_0000: ldc.i4.2
+ IL_0001: call int32 Tests::always_inline(int32)
+ IL_000c: ret
+ }
+
}
prev_args = cfg->args;
prev_arg_types = cfg->arg_types;
prev_inlined_method = cfg->inlined_method;
- cfg->inlined_method = cmethod;
- cfg->ret_var_set = FALSE;
- cfg->inline_depth ++;
+ prev_ret_var_set = cfg->ret_var_set;
prev_real_offset = cfg->real_offset;
prev_cbb_hash = cfg->cbb_hash;
prev_cil_offset_to_bb = cfg->cil_offset_to_bb;
prev_cbb = cfg->cbb;
prev_current_method = cfg->current_method;
prev_generic_context = cfg->generic_context;
- prev_ret_var_set = cfg->ret_var_set;
prev_disable_inline = cfg->disable_inline;
+ cfg->inlined_method = cmethod;
+ cfg->ret_var_set = FALSE;
+ cfg->inline_depth ++;
+
if (ip && *ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
virtual_ = TRUE;
#include "mini-llvm.h"
#include "lldb.h"
-MonoTraceSpec *mono_jit_trace_calls;
+MonoCallSpec *mono_jit_trace_calls;
MonoMethodDesc *mono_inject_async_exc_method;
int mono_inject_async_exc_pos;
MonoMethodDesc *mono_break_at_bb_method;
#include "mono/metadata/marshal.h"
#include "mono/metadata/security-manager.h"
#include "mono/metadata/exception.h"
+#include "mono/metadata/callspec.h"
/*
* The mini code should not have any compile time dependencies on the GC being used, so the same object file from mini/
typedef struct MonoBasicBlock MonoBasicBlock;
typedef struct MonoLMF MonoLMF;
typedef struct MonoSpillInfo MonoSpillInfo;
-typedef struct MonoTraceSpec MonoTraceSpec;
-extern MonoTraceSpec *mono_jit_trace_calls;
+extern MonoCallSpec *mono_jit_trace_calls;
extern gboolean mono_break_on_exc;
extern int mono_exc_esp_offset;
extern gboolean mono_compile_aot;
MONO_API gboolean mono_breakpoint_clean_code (guint8 *method_start, guint8 *code, int offset, guint8 *buf, int size);
/* Tracing */
-MonoTraceSpec *mono_trace_parse_options (const char *options);
-void mono_trace_set_assembly (MonoAssembly *assembly);
+MonoCallSpec *mono_trace_set_options (const char *options);
gboolean mono_trace_eval (MonoMethod *method);
extern void
#include <string.h>
#include "mini.h"
#include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/assembly.h>
#include <mono/utils/mono-time.h>
#include <mono/utils/mono-memory-model.h>
#include "trace.h"
+#include <mono/metadata/callspec.h>
#if defined (HOST_ANDROID) || (defined (TARGET_IOS) && defined (TARGET_IOS))
# undef printf
# define fprintf(__ignore, ...) g_log ("mono-gc", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
#endif
-static MonoTraceSpec trace_spec;
+static MonoCallSpec trace_spec;
static volatile gint32 output_lock = 0;
-gboolean
-mono_trace_eval_exception (MonoClass *klass)
-{
- int include = 0;
- int i;
-
- if (!klass)
- return FALSE;
-
- for (i = 0; i < trace_spec.len; i++) {
- MonoTraceOperation *op = &trace_spec.ops [i];
- int inc = 0;
-
- switch (op->op){
- case MONO_TRACEOP_EXCEPTION:
- if (strcmp ("", op->data) == 0 && strcmp ("all", op->data2) == 0)
- inc = 1;
- else if (strcmp ("", op->data) == 0 || strcmp (klass->name_space, op->data) == 0)
- if (strcmp (klass->name, op->data2) == 0)
- inc = 1;
- break;
- default:
- break;
- }
- if (op->exclude){
- if (inc)
- include = 0;
- } else if (inc)
- include = 1;
- }
-
- return include;
-}
-
-gboolean
-mono_trace_eval (MonoMethod *method)
-{
- int include = 0;
- int i;
-
- for (i = 0; i < trace_spec.len; i++){
- MonoTraceOperation *op = &trace_spec.ops [i];
- int inc = 0;
-
- switch (op->op){
- case MONO_TRACEOP_ALL:
- inc = 1;
- break;
- case MONO_TRACEOP_PROGRAM:
- if (trace_spec.assembly && (method->klass->image == mono_assembly_get_image (trace_spec.assembly)))
- inc = 1;
- break;
- case MONO_TRACEOP_WRAPPER:
- if ((method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) ||
- (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE))
- inc = 1;
- break;
- case MONO_TRACEOP_METHOD:
- if (mono_method_desc_full_match ((MonoMethodDesc *) op->data, method))
- inc = 1;
- break;
- case MONO_TRACEOP_CLASS:
- if (strcmp (method->klass->name_space, op->data) == 0)
- if (strcmp (method->klass->name, op->data2) == 0)
- inc = 1;
- break;
- case MONO_TRACEOP_ASSEMBLY:
- if (strcmp (mono_image_get_name (method->klass->image), op->data) == 0)
- inc = 1;
- break;
- case MONO_TRACEOP_NAMESPACE:
- if (strcmp (method->klass->name_space, op->data) == 0)
- inc = 1;
- break;
- case MONO_TRACEOP_EXCEPTION:
- break;
- }
- if (op->exclude) {
- if (inc)
- include = 0;
- } else if (inc) {
- include = 1;
- }
- }
- return include;
-}
-
-static int is_filenamechar (char p)
-{
- if (p >= 'A' && p <= 'Z')
- return TRUE;
- if (p >= 'a' && p <= 'z')
- return TRUE;
- if (p >= '0' && p <= '9')
- return TRUE;
- if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
- return TRUE;
- return FALSE;
-}
-
-static char *input;
-static char *value;
-
-static void get_string (void)
+gboolean mono_trace_eval_exception (MonoClass *klass)
{
- char *start = input;
- while (is_filenamechar (*input)){
- input++;
- }
- if (value != NULL)
- g_free (value);
- size_t len = input - start;
- value = (char *)g_malloc (len + 1);
- memcpy (value, start, len);
- value [len] = 0;
+ return mono_callspec_eval_exception (klass, &trace_spec);
}
-enum Token {
- TOKEN_METHOD,
- TOKEN_CLASS,
- TOKEN_ALL,
- TOKEN_PROGRAM,
- TOKEN_EXCEPTION,
- TOKEN_NAMESPACE,
- TOKEN_WRAPPER,
- TOKEN_STRING,
- TOKEN_EXCLUDE,
- TOKEN_DISABLED,
- TOKEN_SEPARATOR,
- TOKEN_END,
- TOKEN_ERROR
-};
-
-static int
-get_token (void)
+gboolean mono_trace_eval (MonoMethod *method)
{
- while (input [0] == '+')
- input++;
-
- if (input [0] == '\0') {
- return TOKEN_END;
- }
- if (input [0] == 'M' && input [1] == ':'){
- input += 2;
- get_string ();
- return TOKEN_METHOD;
- }
- if (input [0] == 'N' && input [1] == ':'){
- input += 2;
- get_string ();
- return TOKEN_NAMESPACE;
- }
- if (input [0] == 'T' && input [1] == ':'){
- input += 2;
- get_string ();
- return TOKEN_CLASS;
- }
- if (input [0] == 'E' && input [1] == ':'){
- input += 2;
- get_string ();
- return TOKEN_EXCEPTION;
- }
- if (*input == '-'){
- input++;
- return TOKEN_EXCLUDE;
- }
- if (is_filenamechar (*input)){
- get_string ();
- if (strcmp (value, "all") == 0)
- return TOKEN_ALL;
- if (strcmp (value, "program") == 0)
- return TOKEN_PROGRAM;
- if (strcmp (value, "wrapper") == 0)
- return TOKEN_WRAPPER;
- if (strcmp (value, "disabled") == 0)
- return TOKEN_DISABLED;
- return TOKEN_STRING;
- }
- if (*input == ','){
- input++;
- return TOKEN_SEPARATOR;
- }
-
- fprintf (stderr, "Syntax error at or around '%s'\n", input);
- return TOKEN_ERROR;
+ return mono_callspec_eval (method, &trace_spec);
}
-static void
-cleanup (void)
+MonoCallSpec *mono_trace_set_options (const char *options)
{
- if (value != NULL)
- g_free (value);
-}
-
-static int
-get_spec (int *last)
-{
- int token = get_token ();
- if (token == TOKEN_EXCLUDE){
- token = get_spec (last);
- if (token == TOKEN_EXCLUDE){
- fprintf (stderr, "Expecting an expression");
- return TOKEN_ERROR;
- }
- if (token == TOKEN_ERROR)
- return token;
- trace_spec.ops [(*last)-1].exclude = 1;
- return TOKEN_SEPARATOR;
- }
- if (token == TOKEN_END || token == TOKEN_SEPARATOR || token == TOKEN_ERROR)
- return token;
-
- if (token == TOKEN_METHOD){
- MonoMethodDesc *desc = mono_method_desc_new (value, TRUE);
- if (desc == NULL){
- fprintf (stderr, "Invalid method name: %s\n", value);
- return TOKEN_ERROR;
- }
- trace_spec.ops [*last].op = MONO_TRACEOP_METHOD;
- trace_spec.ops [*last].data = desc;
- } else if (token == TOKEN_ALL)
- trace_spec.ops [*last].op = MONO_TRACEOP_ALL;
- else if (token == TOKEN_PROGRAM)
- trace_spec.ops [*last].op = MONO_TRACEOP_PROGRAM;
- else if (token == TOKEN_WRAPPER)
- trace_spec.ops [*last].op = MONO_TRACEOP_WRAPPER;
- else if (token == TOKEN_NAMESPACE){
- trace_spec.ops [*last].op = MONO_TRACEOP_NAMESPACE;
- trace_spec.ops [*last].data = g_strdup (value);
- } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION){
- char *p = strrchr (value, '.');
- if (p) {
- *p++ = 0;
- trace_spec.ops [*last].data = g_strdup (value);
- trace_spec.ops [*last].data2 = g_strdup (p);
- }
- else {
- trace_spec.ops [*last].data = g_strdup ("");
- trace_spec.ops [*last].data2 = g_strdup (value);
- }
- trace_spec.ops [*last].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS : MONO_TRACEOP_EXCEPTION;
- } else if (token == TOKEN_STRING){
- trace_spec.ops [*last].op = MONO_TRACEOP_ASSEMBLY;
- trace_spec.ops [*last].data = g_strdup (value);
- } else if (token == TOKEN_DISABLED) {
- trace_spec.enabled = FALSE;
- } else {
- fprintf (stderr, "Syntax error in trace option specification\n");
- return TOKEN_ERROR;
+ char *errstr;
+ if (!mono_callspec_parse (options, &trace_spec, &errstr)) {
+ fprintf (stderr, "%s\n", errstr);
+ g_free (errstr);
+ return NULL;
}
- (*last)++;
- return TOKEN_SEPARATOR;
-}
-MonoTraceSpec *
-mono_trace_parse_options (const char *options)
-{
- char *p = (char*)options;
- int size = 1;
- int last_used;
- int token;
-
- trace_spec.enabled = TRUE;
- if (*p == 0){
- trace_spec.len = 1;
- trace_spec.ops = g_new0 (MonoTraceOperation, 1);
- trace_spec.ops [0].op = MONO_TRACEOP_ALL;
- return &trace_spec;
- }
-
- for (p = (char*)options; *p != 0; p++)
- if (*p == ',')
- size++;
-
- trace_spec.ops = g_new0 (MonoTraceOperation, size);
-
- input = (char*)options;
- last_used = 0;
-
- while ((token = (get_spec (&last_used))) != TOKEN_END){
- if (token == TOKEN_ERROR)
- return NULL;
- if (token == TOKEN_SEPARATOR)
- continue;
- }
- trace_spec.len = last_used;
- cleanup ();
return &trace_spec;
}
-void
-mono_trace_set_assembly (MonoAssembly *assembly)
-{
- trace_spec.assembly = assembly;
-}
-
static
#ifdef HAVE_KW_THREAD
__thread
#include <glib.h>
#include "mono/utils/mono-compiler.h"
-typedef enum {
- MONO_TRACEOP_ALL,
- MONO_TRACEOP_PROGRAM,
- MONO_TRACEOP_METHOD,
- MONO_TRACEOP_ASSEMBLY,
- MONO_TRACEOP_CLASS,
- MONO_TRACEOP_NAMESPACE,
- MONO_TRACEOP_EXCEPTION,
- MONO_TRACEOP_WRAPPER,
-} MonoTraceOpcode;
-
-typedef struct {
- MonoTraceOpcode op;
- int exclude;
- void *data, *data2;
-} MonoTraceOperation;
-
-struct MonoTraceSpec {
- int len;
- gboolean enabled;
- MonoTraceOperation *ops;
-
- MonoAssembly *assembly;
-};
-
G_BEGIN_DECLS
void
} else if (match_option (arg, "calldepth", &val)) {
char *end;
config->max_call_depth = strtoul (val, &end, 10);
+ } else if (match_option (arg, "callspec", &val)) {
+ if (!val)
+ val = "";
+ if (val[0] == '\"')
+ ++val;
+ char *spec = g_strdup (val);
+ size_t speclen = strlen (val);
+ if (speclen > 0 && spec[speclen - 1] == '\"')
+ spec[speclen - 1] = '\0';
+ char *errstr;
+ if (!mono_callspec_parse (spec, &config->callspec, &errstr)) {
+ mono_profiler_printf_err (
+ "Could not parse callspec: '%s': %s", spec,
+ errstr);
+ g_free (errstr);
+ mono_callspec_cleanup (&config->callspec);
+ }
+ g_free (spec);
} else if (match_option (arg, "covfilter-file", &val)) {
if (config->cov_filter_files == NULL)
config->cov_filter_files = g_ptr_array_new ();
static MonoProfilerCallInstrumentationFlags
method_filter (MonoProfiler *prof, MonoMethod *method)
{
+ if (log_config.callspec.len > 0 &&
+ !mono_callspec_eval (method, &log_config.callspec))
+ return MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
+
return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER |
MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE |
MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL |
#include <glib.h>
#define MONO_PROFILER_UNSTABLE_GC_ROOTS
#include <mono/metadata/profiler.h>
+#include <mono/metadata/callspec.h>
#define BUF_ID 0x4D504C01
#define LOG_HEADER_ID 0x4D505A01
// Sample mode. Only used at startup.
MonoProfilerSampleMode sampling_mode;
+
+ // Callspec config - which methods are to be instrumented
+ MonoCallSpec callspec;
} ProfilerConfig;
void proflog_parse_args (ProfilerConfig *config, const char *desc);
using System;
+using System.Collections.Generic;
using System.Runtime.InteropServices;
public class Test {
- static void puts (string s)
- {
- Console.WriteLine (s);
- }
-
- public static int jagged ()
+ public static int test_0_jagged ()
{
int[][] j2 = new int [3][];
return 0;
}
- public static int stest ()
+ public static int test_0_stest ()
{
string[] sa = new string[32];
for (int i = 0; i < sa.Length; i++){
if (sa [i] != null)
- puts (sa [i]);
+ Console.WriteLine (sa [i]);
}
return 0;
}
- public static int atest2 ()
+ public static int test_0_atest2 ()
{
int[,] ia = new int[32,32];
return 0;
}
- public static int atest ()
+ public static int test_0_atest ()
{
int[] ia = new int[32];
for (int i = 0; i <ia.Length; i++)
if ((int)ia.GetValue (i) != i*i*i){
- puts ("Crap: " + i + " " + (int) ia.GetValue (i) );
+ Console.WriteLine ("Crap: " + i + " " + (int) ia.GetValue (i) );
return 4;
}
return 0;
}
-
- public static int Main () {
- puts ("a");
- if (atest () != 0)
- return atest ();
- puts ("b");
- if (atest2 () != 0)
- return 1;
- puts ("c");
- if (atest2 () != 0)
- return 1;
- puts ("d");
- if (stest () != 0)
+ enum Foo { a, b };
+ public static int test_0_enum_array_casting () {
+ var x = new Foo[10];
+ try {
+ var y = (IReadOnlyList<int>)(object)x;
+ } catch (Exception e) {
return 1;
- puts ("e");
- if (jagged () != 0)
- return 1;
-
-
+ }
+ try {
+ var y = (IList<int>)(object)x;
+ } catch (Exception e) {
+ return 2;
+ }
+
+ try {
+ var y = (ICollection<int>)(object)x;
+ } catch (Exception e) {
+ return 3;
+ }
+
+ try {
+ var y = (IEnumerable<int>)(object)x;
+ } catch (Exception e) {
+ return 4;
+ }
+
+ try {
+ var y = (IReadOnlyCollection<int>)(object)x;
+ } catch (Exception e) {
+ return 5;
+ }
return 0;
}
+
+ public static int Main (string[] args) {
+ return TestDriver.RunTests (typeof (Test), args);
+ }
}
<ClCompile Include="..\mono\metadata\assembly.c" />\r
<ClCompile Include="..\mono\metadata\attach.c" />\r
<ClCompile Include="..\mono\metadata\boehm-gc.c" />\r
+ <ClCompile Include="..\mono\metadata\callspec.c" />\r
<ClCompile Include="..\mono\metadata\class-accessors.c" />\r
<ClCompile Include="..\mono\metadata\class.c" />\r
<ClCompile Include="..\mono\metadata\cominterop.c" />\r
<ClInclude Include="..\mono\metadata\appdomain-icalls.h" />\r
<ClInclude Include="..\mono\metadata\assembly.h" />\r
<ClInclude Include="..\mono\metadata\attach.h" />\r
+ <ClInclude Include="..\mono\metadata\callspec.h" />\r
<ClInclude Include="..\mono\metadata\cil-coff.h" />\r
<ClInclude Include="..\mono\metadata\class-internals.h" />\r
<ClInclude Include="..\mono\metadata\class.h" />\r
self.env.set('PANGO_SYSCONFDIR', '%{staged_prefix}/etc')
self.env.set('PANGO_LIBDIR', '%{staged_prefix}/lib')
# self.env.set ('MONO_PATH', '%{staged_prefix}/lib/mono/4.0')
- self.debug_info = ['gtk+', 'cairo',
+ self.debug_info = ['gtk+', 'cairo', 'glib',
'pango', 'mono', 'llvm', 'libgdiplus']
self.cache_host = None
subprocess.call(['bash', '-c', path])
-MonoReleaseProfile()
\ No newline at end of file
+MonoReleaseProfile()
def __init__(self):
Package.__init__(self, 'mono-extensions', None,
sources=['git@github.com:xamarin/mono-extensions.git'],
- revision='07ad37d63e0e9dcf7c879a72bc14c5d6c794f7b6'
+ revision='3cc5e2e1870b35f15b1540f835a370d2b011bacd'
)
self.source_dir_name = 'mono-extensions'
# This script is meant to be executed on all "slave" machines that run coverage collection.
COV_DIR=coverage
-COV_INFO="$COV_DIR/$JOB_NAME.info"
+COV_NAME="$(echo $JOB_NAME | sed 's#/#-#g').info"
+COV_INFO="$COV_DIR/$COV_NAME"
# Build Mono and collect coverage on the test suite.
-CI_TAGS=collect-coverage,monolite scripts/ci/run-jenkins.sh
+CI_TAGS="collect-coverage,monolite,$CI_TAGS" scripts/ci/run-jenkins.sh
# Place the coverage info file into the coverage directory.
# Multiple such files can be assembled to create a unified coverage report that spans multiple architectures and operating systems.
scripts/ci/run-step.sh --label=coverage-lcov --timeout=20m lcov --no-external -c -d mono -d support -d tools -o "$COV_INFO"
# Generate HTML coverage report in the lcov directory at the root of the project.
-scripts/ci/run-step.sh --label=coverage-genhtml --timeout=20m genhtml "$COV_INFO" -o lcov
+scripts/ci/run-step.sh --label=coverage-genhtml --timeout=20m genhtml -f -s "$COV_INFO" -o lcov
# Make the paths relative so that they could be assembled from different Jenkins workspaces.
sed -Eie "s#^SF:$WORKSPACE/?#SF:#" "$COV_INFO"
if [[ $CI_TAGS == *'collect-coverage'* ]]; then
# Collect coverage for further use by lcov and similar tools.
# Coverage must be collected with -O0 and debug information.
- export CFLAGS="-ggdb3 --coverage -O0"
- # Collect coverage on all optimizations
- export MONO_ENV_OPTIONS="$MONO_ENV_OPTIONS -O=all"
-elif [[ ${CI_TAGS} == *'clang-sanitizer'* ]]; then
+ export CFLAGS="$CFLAGS -ggdb3 --coverage -O0"
+fi
+
+if [[ ${CI_TAGS} == *'clang-sanitizer'* ]]; then
export CC="clang"
export CXX="clang++"
- export CFLAGS="-g -O1 -fsanitize=thread -fsanitize-blacklist=${MONO_REPO_ROOT}/scripts/ci/clang-thread-sanitizer-blacklist -mllvm -tsan-instrument-atomics=false"
+ export CFLAGS="$CFLAGS -g -O1 -fsanitize=thread -fsanitize-blacklist=${MONO_REPO_ROOT}/scripts/ci/clang-thread-sanitizer-blacklist -mllvm -tsan-instrument-atomics=false"
export LDFLAGS="-fsanitize=thread"
# TSAN_OPTIONS are used by programs that were compiled with Clang's ThreadSanitizer
# see https://github.com/google/sanitizers/wiki/ThreadSanitizerFlags for more details
export TSAN_OPTIONS="history_size=7:exitcode=0:force_seq_cst_atomics=1"
make_timeout=30m
-elif [[ ${label} == w* ]]; then
+fi
+
+if [[ ${label} == w* ]]; then
# Passing -ggdb3 on Cygwin breaks linking against libmonosgen-x.y.dll
- export CFLAGS="-g -O2"
+ export CFLAGS="$CFLAGS -g -O2"
else
- export CFLAGS="-ggdb3 -O2"
+ export CFLAGS="$CFLAGS -ggdb3 -O2"
fi
if [[ $CI_TAGS == *'retry-flaky-tests'* ]]; then
if [[ ${CI_TAGS} == *'checked-coop'* ]]; then export MONO_CHECK_MODE=gc,thread; fi
if [[ ${CI_TAGS} == *'checked-all'* ]]; then export MONO_CHECK_MODE=all; fi
+export MONO_ENV_OPTIONS="$MONO_ENV_OPTIONS $MONO_TEST_ENV_OPTIONS"
+
if [[ ${CI_TAGS} == *'acceptance-tests'* ]];
then
$(dirname "${BASH_SOURCE[0]}")/run-test-acceptance-tests.sh