|
|
@@ -46,13 +46,13 @@ namespace Mono.CSharp {
|
|
|
public override void Emit (EmitContext ec)
|
|
|
{
|
|
|
if (args != null)
|
|
|
- Invocation.EmitArguments (ec, mi, args);
|
|
|
+ Invocation.EmitArguments (ec, mi, args, false, null);
|
|
|
|
|
|
ec.ig.Emit (OpCodes.Call, mi);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- static public Expression MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
|
|
|
+ static public StaticCallExpr MakeSimpleCall (EmitContext ec, MethodGroupExpr mg,
|
|
|
Expression e, Location loc)
|
|
|
{
|
|
|
ArrayList args;
|
|
|
@@ -80,6 +80,10 @@ namespace Mono.CSharp {
|
|
|
if (TypeManager.TypeToCoreType (type) != TypeManager.void_type)
|
|
|
ec.ig.Emit (OpCodes.Pop);
|
|
|
}
|
|
|
+
|
|
|
+ public MethodInfo Method {
|
|
|
+ get { return mi; }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public class ParenthesizedExpression : Expression
|
|
|
@@ -643,7 +647,7 @@ namespace Mono.CSharp {
|
|
|
public class Indirection : Expression, IMemoryLocation, IAssignMethod {
|
|
|
Expression expr;
|
|
|
LocalTemporary temporary;
|
|
|
- bool have_temporary;
|
|
|
+ bool prepared;
|
|
|
|
|
|
public Indirection (Expression expr, Location l)
|
|
|
{
|
|
|
@@ -659,54 +663,47 @@ namespace Mono.CSharp {
|
|
|
|
|
|
public override void Emit (EmitContext ec)
|
|
|
{
|
|
|
- ILGenerator ig = ec.ig;
|
|
|
-
|
|
|
- if (temporary != null){
|
|
|
- if (have_temporary) {
|
|
|
- temporary.Emit (ec);
|
|
|
- } else {
|
|
|
- expr.Emit (ec);
|
|
|
- ec.ig.Emit (OpCodes.Dup);
|
|
|
- temporary.Store (ec);
|
|
|
- have_temporary = true;
|
|
|
- }
|
|
|
- } else
|
|
|
+ if (!prepared)
|
|
|
expr.Emit (ec);
|
|
|
|
|
|
- LoadFromPtr (ig, Type);
|
|
|
+ LoadFromPtr (ec.ig, Type);
|
|
|
}
|
|
|
|
|
|
- public void EmitAssign (EmitContext ec, Expression source)
|
|
|
+ public void Emit (EmitContext ec, bool leave_copy)
|
|
|
{
|
|
|
- if (temporary != null){
|
|
|
- if (have_temporary)
|
|
|
- temporary.Emit (ec);
|
|
|
- else {
|
|
|
- expr.Emit (ec);
|
|
|
- ec.ig.Emit (OpCodes.Dup);
|
|
|
- temporary.Store (ec);
|
|
|
- have_temporary = true;
|
|
|
- }
|
|
|
- } else
|
|
|
- expr.Emit (ec);
|
|
|
+ Emit (ec);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temporary = new LocalTemporary (ec, expr.Type);
|
|
|
+ temporary.Store (ec);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
|
|
|
+ {
|
|
|
+ prepared = prepare_for_load;
|
|
|
+
|
|
|
+ expr.Emit (ec);
|
|
|
|
|
|
+ if (prepare_for_load)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+
|
|
|
source.Emit (ec);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temporary = new LocalTemporary (ec, expr.Type);
|
|
|
+ temporary.Store (ec);
|
|
|
+ }
|
|
|
+
|
|
|
StoreFromPtr (ec.ig, type);
|
|
|
+
|
|
|
+ if (temporary != null)
|
|
|
+ temporary.Emit (ec);
|
|
|
}
|
|
|
|
|
|
public void AddressOf (EmitContext ec, AddressOp Mode)
|
|
|
{
|
|
|
- if (temporary != null){
|
|
|
- if (have_temporary){
|
|
|
- temporary.Emit (ec);
|
|
|
- return;
|
|
|
- }
|
|
|
- expr.Emit (ec);
|
|
|
- ec.ig.Emit (OpCodes.Dup);
|
|
|
- temporary.Store (ec);
|
|
|
- have_temporary = true;
|
|
|
- } else
|
|
|
- expr.Emit (ec);
|
|
|
+ expr.Emit (ec);
|
|
|
}
|
|
|
|
|
|
public override Expression DoResolve (EmitContext ec)
|
|
|
@@ -716,12 +713,7 @@ namespace Mono.CSharp {
|
|
|
//
|
|
|
return this;
|
|
|
}
|
|
|
-
|
|
|
- public new void CacheTemporaries (EmitContext ec)
|
|
|
- {
|
|
|
- temporary = new LocalTemporary (ec, expr.Type);
|
|
|
- }
|
|
|
-
|
|
|
+
|
|
|
public override string ToString ()
|
|
|
{
|
|
|
return "*(" + expr + ")";
|
|
|
@@ -757,13 +749,15 @@ namespace Mono.CSharp {
|
|
|
}
|
|
|
|
|
|
Mode mode;
|
|
|
+ bool is_expr = false;
|
|
|
+ bool recurse = false;
|
|
|
+
|
|
|
Expression expr;
|
|
|
- LocalTemporary temp_storage;
|
|
|
|
|
|
//
|
|
|
// This is expensive for the simplest case.
|
|
|
//
|
|
|
- Expression method;
|
|
|
+ StaticCallExpr method;
|
|
|
|
|
|
public UnaryMutator (Mode m, Expression e, Location l)
|
|
|
{
|
|
|
@@ -855,9 +849,7 @@ namespace Mono.CSharp {
|
|
|
} else if (expr.eclass == ExprClass.IndexerAccess){
|
|
|
IndexerAccess ia = (IndexerAccess) expr;
|
|
|
|
|
|
- temp_storage = new LocalTemporary (ec, expr.Type);
|
|
|
-
|
|
|
- expr = ia.ResolveLValue (ec, temp_storage);
|
|
|
+ expr = ia.ResolveLValue (ec, this);
|
|
|
if (expr == null)
|
|
|
return null;
|
|
|
|
|
|
@@ -975,114 +967,34 @@ namespace Mono.CSharp {
|
|
|
}
|
|
|
|
|
|
}
|
|
|
-
|
|
|
- static EmptyExpression empty_expr;
|
|
|
|
|
|
void EmitCode (EmitContext ec, bool is_expr)
|
|
|
{
|
|
|
- ILGenerator ig = ec.ig;
|
|
|
- IAssignMethod ia = (IAssignMethod) expr;
|
|
|
- Type expr_type = expr.Type;
|
|
|
-
|
|
|
- ia.CacheTemporaries (ec);
|
|
|
-
|
|
|
- //
|
|
|
- // NOTE: We should probably handle three cases:
|
|
|
- //
|
|
|
- // * method invocation required.
|
|
|
- // * direct stack manipulation possible
|
|
|
- // * the object requires an "instance" field
|
|
|
- //
|
|
|
- if (temp_storage == null){
|
|
|
- //
|
|
|
- // Temporary improvement: if we are dealing with something that does
|
|
|
- // not require complicated instance setup, avoid using a temporary
|
|
|
- //
|
|
|
- // For now: only localvariables when not remapped
|
|
|
- //
|
|
|
-
|
|
|
- if (method == null &&
|
|
|
- ((expr is LocalVariableReference) ||(expr is FieldExpr && ((FieldExpr) expr).FieldInfo.IsStatic))){
|
|
|
- if (empty_expr == null)
|
|
|
- empty_expr = new EmptyExpression ();
|
|
|
-
|
|
|
- switch (mode){
|
|
|
- case Mode.PreIncrement:
|
|
|
- case Mode.PreDecrement:
|
|
|
- expr.Emit (ec);
|
|
|
-
|
|
|
- LoadOneAndEmitOp (ec, expr_type);
|
|
|
- if (is_expr)
|
|
|
- ig.Emit (OpCodes.Dup);
|
|
|
- ia.EmitAssign (ec, empty_expr);
|
|
|
- break;
|
|
|
-
|
|
|
- case Mode.PostIncrement:
|
|
|
- case Mode.PostDecrement:
|
|
|
- expr.Emit (ec);
|
|
|
- if (is_expr)
|
|
|
- ig.Emit (OpCodes.Dup);
|
|
|
-
|
|
|
- LoadOneAndEmitOp (ec, expr_type);
|
|
|
- ia.EmitAssign (ec, empty_expr);
|
|
|
- break;
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- temp_storage = new LocalTemporary (ec, expr_type);
|
|
|
- }
|
|
|
-
|
|
|
- switch (mode){
|
|
|
- case Mode.PreIncrement:
|
|
|
- case Mode.PreDecrement:
|
|
|
- if (method == null){
|
|
|
- expr.Emit (ec);
|
|
|
-
|
|
|
- LoadOneAndEmitOp (ec, expr_type);
|
|
|
- } else
|
|
|
- method.Emit (ec);
|
|
|
-
|
|
|
- temp_storage.Store (ec);
|
|
|
- ia.EmitAssign (ec, temp_storage);
|
|
|
- if (is_expr)
|
|
|
- temp_storage.Emit (ec);
|
|
|
- break;
|
|
|
-
|
|
|
- case Mode.PostIncrement:
|
|
|
- case Mode.PostDecrement:
|
|
|
- if (is_expr)
|
|
|
- expr.Emit (ec);
|
|
|
-
|
|
|
- if (method == null){
|
|
|
- if (!is_expr)
|
|
|
- expr.Emit (ec);
|
|
|
- else
|
|
|
- ig.Emit (OpCodes.Dup);
|
|
|
-
|
|
|
- LoadOneAndEmitOp (ec, expr_type);
|
|
|
- } else {
|
|
|
- method.Emit (ec);
|
|
|
- }
|
|
|
-
|
|
|
- temp_storage.Store (ec);
|
|
|
- ia.EmitAssign (ec, temp_storage);
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- temp_storage.Release (ec);
|
|
|
+ recurse = true;
|
|
|
+ this.is_expr = is_expr;
|
|
|
+ ((IAssignMethod) expr).EmitAssign (ec, this, is_expr && (mode == Mode.PreIncrement || mode == Mode.PreDecrement), true);
|
|
|
}
|
|
|
+
|
|
|
|
|
|
public override void Emit (EmitContext ec)
|
|
|
{
|
|
|
- EmitCode (ec, true);
|
|
|
+ if (recurse) {
|
|
|
+ ((IAssignMethod) expr).Emit (ec, is_expr && (mode == Mode.PostIncrement || mode == Mode.PostDecrement));
|
|
|
+ if (method == null)
|
|
|
+ LoadOneAndEmitOp (ec, expr.Type);
|
|
|
+ else
|
|
|
+ ec.ig.Emit (OpCodes.Call, method.Method);
|
|
|
+ recurse = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
+ EmitCode (ec, true);
|
|
|
}
|
|
|
|
|
|
public override void EmitStatement (EmitContext ec)
|
|
|
{
|
|
|
EmitCode (ec, false);
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -3142,7 +3054,7 @@ namespace Mono.CSharp {
|
|
|
ILGenerator ig = ec.ig;
|
|
|
|
|
|
if (Arguments != null)
|
|
|
- Invocation.EmitArguments (ec, method, Arguments);
|
|
|
+ Invocation.EmitArguments (ec, method, Arguments, false, null);
|
|
|
|
|
|
if (method is MethodInfo)
|
|
|
ig.Emit (OpCodes.Call, (MethodInfo) method);
|
|
|
@@ -3277,7 +3189,7 @@ namespace Mono.CSharp {
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- Invocation.EmitArguments (ec, concat_method, operands);
|
|
|
+ Invocation.EmitArguments (ec, concat_method, operands, false, null);
|
|
|
ec.ig.Emit (OpCodes.Call, concat_method);
|
|
|
}
|
|
|
}
|
|
|
@@ -3306,7 +3218,7 @@ namespace Mono.CSharp {
|
|
|
{
|
|
|
ILGenerator ig = ec.ig;
|
|
|
|
|
|
- Invocation.EmitArguments (ec, method, args);
|
|
|
+ Invocation.EmitArguments (ec, method, args, false, null);
|
|
|
|
|
|
ig.Emit (OpCodes.Call, (MethodInfo) method);
|
|
|
ig.Emit (OpCodes.Castclass, type);
|
|
|
@@ -3733,12 +3645,19 @@ namespace Mono.CSharp {
|
|
|
ig.Emit (OpCodes.Ldloc, local_info.LocalBuilder);
|
|
|
}
|
|
|
|
|
|
- public void EmitAssign (EmitContext ec, Expression source)
|
|
|
+ public void Emit (EmitContext ec, bool leave_copy)
|
|
|
+ {
|
|
|
+ Emit (ec);
|
|
|
+ if (leave_copy)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
|
|
|
{
|
|
|
- ILGenerator ig = ec.ig;
|
|
|
-
|
|
|
source.Emit (ec);
|
|
|
- ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
|
|
|
+ if (leave_copy)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ ec.ig.Emit (OpCodes.Stloc, local_info.LocalBuilder);
|
|
|
}
|
|
|
|
|
|
public void AddressOf (EmitContext ec, AddressOp mode)
|
|
|
@@ -3765,7 +3684,8 @@ namespace Mono.CSharp {
|
|
|
Block block;
|
|
|
VariableInfo vi;
|
|
|
public Parameter.Modifier mod;
|
|
|
- public bool is_ref, is_out;
|
|
|
+ public bool is_ref, is_out, prepared;
|
|
|
+ LocalTemporary temp;
|
|
|
|
|
|
public ParameterReference (Parameters pars, Block block, int idx, string name, Location loc)
|
|
|
{
|
|
|
@@ -3900,6 +3820,11 @@ namespace Mono.CSharp {
|
|
|
}
|
|
|
|
|
|
public override void Emit (EmitContext ec)
|
|
|
+ {
|
|
|
+ Emit (ec, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Emit (EmitContext ec, bool leave_copy)
|
|
|
{
|
|
|
ILGenerator ig = ec.ig;
|
|
|
|
|
|
@@ -3910,33 +3835,56 @@ namespace Mono.CSharp {
|
|
|
|
|
|
EmitLdArg (ig, arg_idx);
|
|
|
|
|
|
- if (!is_ref)
|
|
|
- return;
|
|
|
-
|
|
|
- //
|
|
|
- // If we are a reference, we loaded on the stack a pointer
|
|
|
- // Now lets load the real value
|
|
|
- //
|
|
|
- LoadFromPtr (ig, type);
|
|
|
+ if (is_ref) {
|
|
|
+ if (prepared)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+
|
|
|
+ //
|
|
|
+ // If we are a reference, we loaded on the stack a pointer
|
|
|
+ // Now lets load the real value
|
|
|
+ //
|
|
|
+ LoadFromPtr (ig, type);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+
|
|
|
+ if (is_ref) {
|
|
|
+ temp = new LocalTemporary (ec, type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
- public void EmitAssign (EmitContext ec, Expression source)
|
|
|
+
|
|
|
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
|
|
|
{
|
|
|
ILGenerator ig = ec.ig;
|
|
|
-
|
|
|
int arg_idx = idx;
|
|
|
-
|
|
|
+
|
|
|
+ prepared = prepare_for_load;
|
|
|
+
|
|
|
if (!ec.IsStatic)
|
|
|
arg_idx++;
|
|
|
|
|
|
- if (is_ref)
|
|
|
+ if (is_ref && !prepared)
|
|
|
EmitLdArg (ig, arg_idx);
|
|
|
|
|
|
source.Emit (ec);
|
|
|
|
|
|
- if (is_ref)
|
|
|
+ if (leave_copy)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+
|
|
|
+ if (is_ref) {
|
|
|
+ if (leave_copy) {
|
|
|
+ temp = new LocalTemporary (ec, type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
+
|
|
|
StoreFromPtr (ig, type);
|
|
|
- else {
|
|
|
+
|
|
|
+ if (temp != null)
|
|
|
+ temp.Emit (ec);
|
|
|
+ } else {
|
|
|
if (arg_idx <= 255)
|
|
|
ig.Emit (OpCodes.Starg_S, (byte) arg_idx);
|
|
|
else
|
|
|
@@ -5082,14 +5030,24 @@ namespace Mono.CSharp {
|
|
|
/// emission of the arguments is known not to contain
|
|
|
/// a `params' field (for example in constructors or other routines
|
|
|
/// that keep their arguments in this structure)
|
|
|
+ ///
|
|
|
+ /// if `dup_args' is true, a copy of the arguments will be left
|
|
|
+ /// on the stack. If `dup_args' is true, you can specify `this_arg'
|
|
|
+ /// which will be duplicated before any other args. Only EmitCall
|
|
|
+ /// should be using this interface.
|
|
|
/// </summary>
|
|
|
- public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments)
|
|
|
+ public static void EmitArguments (EmitContext ec, MethodBase mb, ArrayList arguments, bool dup_args, LocalTemporary this_arg)
|
|
|
{
|
|
|
ParameterData pd;
|
|
|
if (mb != null)
|
|
|
pd = GetParameterData (mb);
|
|
|
else
|
|
|
pd = null;
|
|
|
+
|
|
|
+ LocalTemporary [] temps = null;
|
|
|
+
|
|
|
+ if (dup_args)
|
|
|
+ temps = new LocalTemporary [arguments.Count];
|
|
|
|
|
|
//
|
|
|
// If we are calling a params method with no arguments, special case it
|
|
|
@@ -5126,6 +5084,18 @@ namespace Mono.CSharp {
|
|
|
}
|
|
|
|
|
|
a.Emit (ec);
|
|
|
+ if (dup_args) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ (temps [i] = new LocalTemporary (ec, a.Type)).Store (ec);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dup_args) {
|
|
|
+ if (this_arg != null)
|
|
|
+ this_arg.Emit (ec);
|
|
|
+
|
|
|
+ for (int i = 0; i < top; i ++)
|
|
|
+ temps [i].Emit (ec);
|
|
|
}
|
|
|
|
|
|
if (pd != null && pd.Count > top &&
|
|
|
@@ -5189,10 +5159,25 @@ namespace Mono.CSharp {
|
|
|
public static void EmitCall (EmitContext ec, bool is_base,
|
|
|
bool is_static, Expression instance_expr,
|
|
|
MethodBase method, ArrayList Arguments, Location loc)
|
|
|
+ {
|
|
|
+ EmitCall (ec, is_base, is_static, instance_expr, method, Arguments, loc, false, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // `dup_args' leaves an extra copy of the arguments on the stack
|
|
|
+ // `omit_args' does not leave any arguments at all.
|
|
|
+ // So, basically, you could make one call with `dup_args' set to true,
|
|
|
+ // and then another with `omit_args' set to true, and the two calls
|
|
|
+ // would have the same set of arguments. However, each argument would
|
|
|
+ // only have been evaluated once.
|
|
|
+ public static void EmitCall (EmitContext ec, bool is_base,
|
|
|
+ bool is_static, Expression instance_expr,
|
|
|
+ MethodBase method, ArrayList Arguments, Location loc,
|
|
|
+ bool dup_args, bool omit_args)
|
|
|
{
|
|
|
ILGenerator ig = ec.ig;
|
|
|
bool struct_call = false;
|
|
|
bool this_call = false;
|
|
|
+ LocalTemporary this_arg = null;
|
|
|
|
|
|
Type decl_type = method.DeclaringType;
|
|
|
|
|
|
@@ -5232,26 +5217,28 @@ namespace Mono.CSharp {
|
|
|
return;
|
|
|
|
|
|
if (!is_static){
|
|
|
- if (decl_type.IsValueType)
|
|
|
+ this_call = instance_expr == null;
|
|
|
+ if (decl_type.IsValueType || (!this_call && instance_expr.Type.IsValueType))
|
|
|
struct_call = true;
|
|
|
+
|
|
|
//
|
|
|
// If this is ourselves, push "this"
|
|
|
//
|
|
|
- if (instance_expr == null) {
|
|
|
- this_call = true;
|
|
|
+ if (!omit_args) {
|
|
|
+ Type t = null;
|
|
|
+ if (this_call) {
|
|
|
ig.Emit (OpCodes.Ldarg_0);
|
|
|
+ t = decl_type;
|
|
|
} else {
|
|
|
//
|
|
|
// Push the instance expression
|
|
|
//
|
|
|
- if (instance_expr.Type.IsValueType){
|
|
|
+ if (instance_expr.Type.IsValueType) {
|
|
|
//
|
|
|
// Special case: calls to a function declared in a
|
|
|
// reference-type with a value-type argument need
|
|
|
- // to have their value boxed.
|
|
|
-
|
|
|
- struct_call = true;
|
|
|
- if (decl_type.IsValueType){
|
|
|
+ // to have their value boxed.
|
|
|
+ if (decl_type.IsValueType) {
|
|
|
//
|
|
|
// If the expression implements IMemoryLocation, then
|
|
|
// we can optimize and use AddressOf on the
|
|
|
@@ -5259,28 +5246,40 @@ namespace Mono.CSharp {
|
|
|
//
|
|
|
// If not we have to use some temporary storage for
|
|
|
// it.
|
|
|
- if (instance_expr is IMemoryLocation){
|
|
|
+ if (instance_expr is IMemoryLocation) {
|
|
|
((IMemoryLocation)instance_expr).
|
|
|
AddressOf (ec, AddressOp.LoadStore);
|
|
|
- }
|
|
|
- else {
|
|
|
- Type t = instance_expr.Type;
|
|
|
-
|
|
|
+ } else {
|
|
|
+ LocalTemporary temp = new LocalTemporary (ec, instance_expr.Type);
|
|
|
instance_expr.Emit (ec);
|
|
|
- LocalBuilder temp = ig.DeclareLocal (t);
|
|
|
- ig.Emit (OpCodes.Stloc, temp);
|
|
|
- ig.Emit (OpCodes.Ldloca, temp);
|
|
|
+ temp.Store (ec);
|
|
|
+ temp.AddressOf (ec, AddressOp.Load);
|
|
|
}
|
|
|
+
|
|
|
+ // avoid the overhead of doing this all the time.
|
|
|
+ if (dup_args)
|
|
|
+ t = TypeManager.GetReferenceType (instance_expr.Type);
|
|
|
} else {
|
|
|
instance_expr.Emit (ec);
|
|
|
ig.Emit (OpCodes.Box, instance_expr.Type);
|
|
|
+ t = TypeManager.object_type;
|
|
|
}
|
|
|
- } else
|
|
|
+ } else {
|
|
|
instance_expr.Emit (ec);
|
|
|
+ t = instance_expr.Type;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dup_args) {
|
|
|
+ this_arg = new LocalTemporary (ec, t);
|
|
|
+ ig.Emit (OpCodes.Dup);
|
|
|
+ this_arg.Store (ec);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- EmitArguments (ec, method, Arguments);
|
|
|
+ if (!omit_args)
|
|
|
+ EmitArguments (ec, method, Arguments, dup_args, this_arg);
|
|
|
|
|
|
OpCode call_op;
|
|
|
if (is_static || struct_call || is_base || (this_call && !method.IsVirtual))
|
|
|
@@ -5642,7 +5641,7 @@ namespace Mono.CSharp {
|
|
|
}
|
|
|
|
|
|
if (method != null)
|
|
|
- Invocation.EmitArguments (ec, method, Arguments);
|
|
|
+ Invocation.EmitArguments (ec, method, Arguments, false, null);
|
|
|
|
|
|
if (is_value_type){
|
|
|
if (method == null)
|
|
|
@@ -5689,7 +5688,7 @@ namespace Mono.CSharp {
|
|
|
IMemoryLocation ml = (IMemoryLocation) value_target;
|
|
|
ml.AddressOf (ec, AddressOp.Store);
|
|
|
if (method != null)
|
|
|
- Invocation.EmitArguments (ec, method, Arguments);
|
|
|
+ Invocation.EmitArguments (ec, method, Arguments, false, null);
|
|
|
|
|
|
if (method == null)
|
|
|
ec.ig.Emit (OpCodes.Initobj, type);
|
|
|
@@ -6372,9 +6371,14 @@ namespace Mono.CSharp {
|
|
|
|
|
|
e.Emit (ec);
|
|
|
|
|
|
- if (dims == 1)
|
|
|
- ArrayAccess.EmitStoreOpcode (ig, array_element_type);
|
|
|
- else
|
|
|
+ if (dims == 1) {
|
|
|
+ bool is_stobj;
|
|
|
+ OpCode op = ArrayAccess.GetStoreOpcode (etype, out is_stobj);
|
|
|
+ if (is_stobj)
|
|
|
+ ig.Emit (OpCodes.Stobj, etype);
|
|
|
+ else
|
|
|
+ ig.Emit (op);
|
|
|
+ } else
|
|
|
ig.Emit (OpCodes.Call, set);
|
|
|
|
|
|
}
|
|
|
@@ -6561,28 +6565,36 @@ namespace Mono.CSharp {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- public override void Emit (EmitContext ec)
|
|
|
+ public void Emit (EmitContext ec, bool leave_copy)
|
|
|
{
|
|
|
- ILGenerator ig = ec.ig;
|
|
|
-
|
|
|
- ec.EmitThis ();
|
|
|
- if (ec.TypeContainer is Struct)
|
|
|
- ig.Emit (OpCodes.Ldobj, type);
|
|
|
+ Emit (ec);
|
|
|
+ if (leave_copy)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
}
|
|
|
-
|
|
|
- public void EmitAssign (EmitContext ec, Expression source)
|
|
|
+
|
|
|
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
|
|
|
{
|
|
|
ILGenerator ig = ec.ig;
|
|
|
|
|
|
if (ec.TypeContainer is Struct){
|
|
|
ec.EmitThis ();
|
|
|
source.Emit (ec);
|
|
|
+ if (leave_copy)
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
ig.Emit (OpCodes.Stobj, type);
|
|
|
} else {
|
|
|
- source.Emit (ec);
|
|
|
- ig.Emit (OpCodes.Starg, 0);
|
|
|
+ throw new Exception ("how did you get here");
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public override void Emit (EmitContext ec)
|
|
|
+ {
|
|
|
+ ILGenerator ig = ec.ig;
|
|
|
+
|
|
|
+ ec.EmitThis ();
|
|
|
+ if (ec.TypeContainer is Struct)
|
|
|
+ ig.Emit (OpCodes.Ldobj, type);
|
|
|
+ }
|
|
|
|
|
|
public void AddressOf (EmitContext ec, AddressOp mode)
|
|
|
{
|
|
|
@@ -7434,7 +7446,8 @@ namespace Mono.CSharp {
|
|
|
//
|
|
|
ElementAccess ea;
|
|
|
|
|
|
- LocalTemporary [] cached_locations;
|
|
|
+ LocalTemporary temp;
|
|
|
+ bool prepared;
|
|
|
|
|
|
public ArrayAccess (ElementAccess ea_data, Location l)
|
|
|
{
|
|
|
@@ -7533,20 +7546,6 @@ namespace Mono.CSharp {
|
|
|
ig.Emit (OpCodes.Ldelem_Ref);
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Emits the right opcode to store an object of Type `t'
|
|
|
- /// from an array of T.
|
|
|
- /// </summary>
|
|
|
- static public void EmitStoreOpcode (ILGenerator ig, Type t)
|
|
|
- {
|
|
|
- bool is_stobj;
|
|
|
- OpCode op = GetStoreOpcode (t, out is_stobj);
|
|
|
- if (is_stobj)
|
|
|
- ig.Emit (OpCodes.Stobj, t);
|
|
|
- else
|
|
|
- ig.Emit (op);
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Returns the right opcode to store an object of Type `t'
|
|
|
/// from an array of T.
|
|
|
@@ -7638,101 +7637,111 @@ namespace Mono.CSharp {
|
|
|
{
|
|
|
ILGenerator ig = ec.ig;
|
|
|
|
|
|
- if (cached_locations == null){
|
|
|
- ea.Expr.Emit (ec);
|
|
|
- foreach (Argument a in ea.Arguments){
|
|
|
- Type argtype = a.Expr.Type;
|
|
|
-
|
|
|
- a.Expr.Emit (ec);
|
|
|
-
|
|
|
- if (argtype == TypeManager.int64_type)
|
|
|
- ig.Emit (OpCodes.Conv_Ovf_I);
|
|
|
- else if (argtype == TypeManager.uint64_type)
|
|
|
- ig.Emit (OpCodes.Conv_Ovf_I_Un);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (cached_locations [0] == null){
|
|
|
- cached_locations [0] = new LocalTemporary (ec, ea.Expr.Type);
|
|
|
- ea.Expr.Emit (ec);
|
|
|
- ig.Emit (OpCodes.Dup);
|
|
|
- cached_locations [0].Store (ec);
|
|
|
+ ea.Expr.Emit (ec);
|
|
|
+ foreach (Argument a in ea.Arguments){
|
|
|
+ Type argtype = a.Expr.Type;
|
|
|
|
|
|
- int j = 1;
|
|
|
+ a.Expr.Emit (ec);
|
|
|
|
|
|
- foreach (Argument a in ea.Arguments){
|
|
|
- Type argtype = a.Expr.Type;
|
|
|
-
|
|
|
- cached_locations [j] = new LocalTemporary (ec, TypeManager.intptr_type /* a.Expr.Type */);
|
|
|
- a.Expr.Emit (ec);
|
|
|
- if (argtype == TypeManager.int64_type)
|
|
|
- ig.Emit (OpCodes.Conv_Ovf_I);
|
|
|
- else if (argtype == TypeManager.uint64_type)
|
|
|
- ig.Emit (OpCodes.Conv_Ovf_I_Un);
|
|
|
-
|
|
|
- ig.Emit (OpCodes.Dup);
|
|
|
- cached_locations [j].Store (ec);
|
|
|
- j++;
|
|
|
- }
|
|
|
- return;
|
|
|
+ if (argtype == TypeManager.int64_type)
|
|
|
+ ig.Emit (OpCodes.Conv_Ovf_I);
|
|
|
+ else if (argtype == TypeManager.uint64_type)
|
|
|
+ ig.Emit (OpCodes.Conv_Ovf_I_Un);
|
|
|
}
|
|
|
-
|
|
|
- foreach (LocalTemporary lt in cached_locations)
|
|
|
- lt.Emit (ec);
|
|
|
}
|
|
|
|
|
|
- public new void CacheTemporaries (EmitContext ec)
|
|
|
- {
|
|
|
- cached_locations = new LocalTemporary [ea.Arguments.Count + 1];
|
|
|
- }
|
|
|
-
|
|
|
- public override void Emit (EmitContext ec)
|
|
|
+ public void Emit (EmitContext ec, bool leave_copy)
|
|
|
{
|
|
|
int rank = ea.Expr.Type.GetArrayRank ();
|
|
|
ILGenerator ig = ec.ig;
|
|
|
|
|
|
- LoadArrayAndArguments (ec);
|
|
|
-
|
|
|
- if (rank == 1)
|
|
|
- EmitLoadOpcode (ig, type);
|
|
|
- else {
|
|
|
- MethodInfo method;
|
|
|
+ if (!prepared) {
|
|
|
+ LoadArrayAndArguments (ec);
|
|
|
|
|
|
- method = FetchGetMethod ();
|
|
|
- ig.Emit (OpCodes.Call, method);
|
|
|
+ if (rank == 1)
|
|
|
+ EmitLoadOpcode (ig, type);
|
|
|
+ else {
|
|
|
+ MethodInfo method;
|
|
|
+
|
|
|
+ method = FetchGetMethod ();
|
|
|
+ ig.Emit (OpCodes.Call, method);
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ LoadFromPtr (ec.ig, this.type);
|
|
|
+
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temp = new LocalTemporary (ec, this.type);
|
|
|
+ temp.Store (ec);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ public override void Emit (EmitContext ec)
|
|
|
+ {
|
|
|
+ Emit (ec, false);
|
|
|
+ }
|
|
|
|
|
|
- public void EmitAssign (EmitContext ec, Expression source)
|
|
|
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
|
|
|
{
|
|
|
int rank = ea.Expr.Type.GetArrayRank ();
|
|
|
ILGenerator ig = ec.ig;
|
|
|
Type t = source.Type;
|
|
|
+ prepared = prepare_for_load;
|
|
|
|
|
|
- LoadArrayAndArguments (ec);
|
|
|
-
|
|
|
- //
|
|
|
- // The stobj opcode used by value types will need
|
|
|
- // an address on the stack, not really an array/array
|
|
|
- // pair
|
|
|
- //
|
|
|
- if (rank == 1){
|
|
|
- if (t == TypeManager.enum_type || t == TypeManager.decimal_type ||
|
|
|
- (t.IsSubclassOf (TypeManager.value_type) && !TypeManager.IsEnumType (t) && !TypeManager.IsBuiltinType (t)))
|
|
|
- ig.Emit (OpCodes.Ldelema, t);
|
|
|
+ if (prepare_for_load) {
|
|
|
+ AddressOf (ec, AddressOp.LoadStore);
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ source.Emit (ec);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temp = new LocalTemporary (ec, this.type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
+ StoreFromPtr (ec.ig, t);
|
|
|
+
|
|
|
+ if (temp != null)
|
|
|
+ temp.Emit (ec);
|
|
|
+
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- source.Emit (ec);
|
|
|
+ LoadArrayAndArguments (ec);
|
|
|
|
|
|
- if (rank == 1)
|
|
|
- EmitStoreOpcode (ig, t);
|
|
|
- else {
|
|
|
+ if (rank == 1) {
|
|
|
+ bool is_stobj;
|
|
|
+ OpCode op = GetStoreOpcode (t, out is_stobj);
|
|
|
+ //
|
|
|
+ // The stobj opcode used by value types will need
|
|
|
+ // an address on the stack, not really an array/array
|
|
|
+ // pair
|
|
|
+ //
|
|
|
+ if (is_stobj)
|
|
|
+ ig.Emit (OpCodes.Ldelema, t);
|
|
|
+
|
|
|
+ source.Emit (ec);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temp = new LocalTemporary (ec, this.type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_stobj)
|
|
|
+ ig.Emit (OpCodes.Stobj, t);
|
|
|
+ else
|
|
|
+ ig.Emit (op);
|
|
|
+ } else {
|
|
|
ModuleBuilder mb = CodeGen.Module.Builder;
|
|
|
int arg_count = ea.Arguments.Count;
|
|
|
Type [] args = new Type [arg_count + 1];
|
|
|
MethodInfo set;
|
|
|
|
|
|
+ source.Emit (ec);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temp = new LocalTemporary (ec, this.type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
+
|
|
|
for (int i = 0; i < arg_count; i++){
|
|
|
//args [i++] = a.Type;
|
|
|
args [i] = TypeManager.int32_type;
|
|
|
@@ -7748,6 +7757,9 @@ namespace Mono.CSharp {
|
|
|
|
|
|
ig.Emit (OpCodes.Call, set);
|
|
|
}
|
|
|
+
|
|
|
+ if (temp != null)
|
|
|
+ temp.Emit (ec);
|
|
|
}
|
|
|
|
|
|
public void AddressOf (EmitContext ec, AddressOp mode)
|
|
|
@@ -8039,19 +8051,53 @@ namespace Mono.CSharp {
|
|
|
return this;
|
|
|
}
|
|
|
|
|
|
- public override void Emit (EmitContext ec)
|
|
|
+ bool prepared = false;
|
|
|
+ LocalTemporary temp;
|
|
|
+
|
|
|
+ public void Emit (EmitContext ec, bool leave_copy)
|
|
|
{
|
|
|
- Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc);
|
|
|
+ Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, get, arguments, loc, prepared, false);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temp = new LocalTemporary (ec, Type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
//
|
|
|
// source is ignored, because we already have a copy of it from the
|
|
|
// LValue resolution and we have already constructed a pre-cached
|
|
|
// version of the arguments (ea.set_arguments);
|
|
|
//
|
|
|
- public void EmitAssign (EmitContext ec, Expression source)
|
|
|
+ public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
|
|
|
+ {
|
|
|
+ prepared = prepare_for_load;
|
|
|
+ Argument a = (Argument) set_arguments [set_arguments.Count - 1];
|
|
|
+
|
|
|
+ if (prepared) {
|
|
|
+ source.Emit (ec);
|
|
|
+ if (leave_copy) {
|
|
|
+ ec.ig.Emit (OpCodes.Dup);
|
|
|
+ temp = new LocalTemporary (ec, Type);
|
|
|
+ temp.Store (ec);
|
|
|
+ }
|
|
|
+ } else if (leave_copy) {
|
|
|
+ temp = new LocalTemporary (ec, Type);
|
|
|
+ source.Emit (ec);
|
|
|
+ temp.Store (ec);
|
|
|
+ a.Expr = temp;
|
|
|
+ }
|
|
|
+
|
|
|
+ Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc, false, prepared);
|
|
|
+
|
|
|
+ if (temp != null)
|
|
|
+ temp.Emit (ec);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public override void Emit (EmitContext ec)
|
|
|
{
|
|
|
- Invocation.EmitCall (ec, is_base_indexer, false, instance_expr, set, set_arguments, loc);
|
|
|
+ Emit (ec, false);
|
|
|
}
|
|
|
}
|
|
|
|