Flee
This commit is contained in:
59
ExpressionElements/MemberElements/ArgumentList.cs
Normal file
59
ExpressionElements/MemberElements/ArgumentList.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System.Collections;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Encapsulates an argument list")]
|
||||
internal class ArgumentList
|
||||
{
|
||||
private readonly IList<ExpressionElement> _myElements;
|
||||
public ArgumentList(ICollection elements)
|
||||
{
|
||||
ExpressionElement[] arr = new ExpressionElement[elements.Count];
|
||||
elements.CopyTo(arr, 0);
|
||||
_myElements = arr;
|
||||
}
|
||||
|
||||
private string[] GetArgumentTypeNames()
|
||||
{
|
||||
List<string> l = new List<string>();
|
||||
|
||||
foreach (ExpressionElement e in _myElements)
|
||||
{
|
||||
l.Add(e.ResultType.Name);
|
||||
}
|
||||
|
||||
return l.ToArray();
|
||||
}
|
||||
|
||||
public Type[] GetArgumentTypes()
|
||||
{
|
||||
List<Type> l = new List<Type>();
|
||||
|
||||
foreach (ExpressionElement e in _myElements)
|
||||
{
|
||||
l.Add(e.ResultType);
|
||||
}
|
||||
|
||||
return l.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string[] typeNames = this.GetArgumentTypeNames();
|
||||
return Utility.FormatList(typeNames);
|
||||
}
|
||||
|
||||
public ExpressionElement[] ToArray()
|
||||
{
|
||||
ExpressionElement[] arr = new ExpressionElement[_myElements.Count];
|
||||
_myElements.CopyTo(arr, 0);
|
||||
return arr;
|
||||
}
|
||||
|
||||
public ExpressionElement this[int index] => _myElements[index];
|
||||
|
||||
public int Count => _myElements.Count;
|
||||
}
|
||||
}
|
409
ExpressionElements/MemberElements/FunctionCall.cs
Normal file
409
ExpressionElements/MemberElements/FunctionCall.cs
Normal file
@@ -0,0 +1,409 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Represents a function call")]
|
||||
internal class FunctionCallElement : MemberElement
|
||||
{
|
||||
private readonly ArgumentList _myArguments;
|
||||
private readonly ICollection<MethodInfo> _myMethods;
|
||||
private CustomMethodInfo _myTargetMethodInfo;
|
||||
|
||||
private Type _myOnDemandFunctionReturnType;
|
||||
public FunctionCallElement(string name, ArgumentList arguments)
|
||||
{
|
||||
this.MyName = name;
|
||||
_myArguments = arguments;
|
||||
}
|
||||
|
||||
internal FunctionCallElement(string name, ICollection<MethodInfo> methods, ArgumentList arguments)
|
||||
{
|
||||
MyName = name;
|
||||
_myArguments = arguments;
|
||||
_myMethods = methods;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
// Get the types of our arguments
|
||||
Type[] argTypes = _myArguments.GetArgumentTypes();
|
||||
// Find all methods with our name on the type
|
||||
ICollection<MethodInfo> methods = _myMethods;
|
||||
|
||||
if (methods == null)
|
||||
{
|
||||
// Convert member info to method info
|
||||
MemberInfo[] arr = this.GetMembers(MemberTypes.Method);
|
||||
MethodInfo[] arr2 = new MethodInfo[arr.Length];
|
||||
Array.Copy(arr, arr2, arr.Length);
|
||||
methods = arr2;
|
||||
}
|
||||
|
||||
if (methods.Count > 0)
|
||||
{
|
||||
// More than one method exists with this name
|
||||
this.BindToMethod(methods, MyPrevious, argTypes);
|
||||
return;
|
||||
}
|
||||
|
||||
// No methods with this name exist; try to bind to an on-demand function
|
||||
_myOnDemandFunctionReturnType = MyContext.Variables.ResolveOnDemandFunction(MyName, argTypes);
|
||||
|
||||
if (_myOnDemandFunctionReturnType == null)
|
||||
{
|
||||
// Failed to bind to a function
|
||||
this.ThrowFunctionNotFoundException(MyPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowFunctionNotFoundException(MemberElement previous)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.UndefinedFunction, CompileExceptionReason.UndefinedName, MyName, _myArguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.UndefinedFunctionOnType, CompileExceptionReason.UndefinedName, MyName, _myArguments, previous.TargetType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowNoAccessibleMethodsException(MemberElement previous)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoAccessibleMatches, CompileExceptionReason.AccessDenied, MyName, _myArguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoAccessibleMatchesOnType, CompileExceptionReason.AccessDenied, MyName, _myArguments, previous.TargetType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowAmbiguousMethodCallException()
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.AmbiguousCallOfFunction, CompileExceptionReason.AmbiguousMatch, MyName, _myArguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to find a match from a set of methods
|
||||
/// </summary>
|
||||
/// <param name="methods"></param>
|
||||
/// <param name="previous"></param>
|
||||
/// <param name="argTypes"></param>
|
||||
private void BindToMethod(ICollection<MethodInfo> methods, MemberElement previous, Type[] argTypes)
|
||||
{
|
||||
List<CustomMethodInfo> customInfos = new List<CustomMethodInfo>();
|
||||
|
||||
// Wrap the MethodInfos in our custom class
|
||||
foreach (MethodInfo mi in methods)
|
||||
{
|
||||
CustomMethodInfo cmi = new CustomMethodInfo(mi);
|
||||
customInfos.Add(cmi);
|
||||
}
|
||||
|
||||
// Discard any methods that cannot qualify as overloads
|
||||
CustomMethodInfo[] arr = customInfos.ToArray();
|
||||
customInfos.Clear();
|
||||
|
||||
foreach (CustomMethodInfo cmi in arr)
|
||||
{
|
||||
if (cmi.IsMatch(argTypes, MyPrevious, MyContext) == true)
|
||||
{
|
||||
customInfos.Add(cmi);
|
||||
}
|
||||
}
|
||||
|
||||
if (customInfos.Count == 0)
|
||||
{
|
||||
// We have no methods that can qualify as overloads; throw exception
|
||||
this.ThrowFunctionNotFoundException(previous);
|
||||
}
|
||||
else
|
||||
{
|
||||
// At least one method matches our criteria; do our custom overload resolution
|
||||
this.ResolveOverloads(customInfos.ToArray(), previous, argTypes);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the best match from a set of overloaded methods
|
||||
/// </summary>
|
||||
/// <param name="infos"></param>
|
||||
/// <param name="previous"></param>
|
||||
/// <param name="argTypes"></param>
|
||||
private void ResolveOverloads(CustomMethodInfo[] infos, MemberElement previous, Type[] argTypes)
|
||||
{
|
||||
// Compute a score for each candidate
|
||||
foreach (CustomMethodInfo cmi in infos)
|
||||
{
|
||||
cmi.ComputeScore(argTypes);
|
||||
}
|
||||
|
||||
// Sort array from best to worst matches
|
||||
Array.Sort<CustomMethodInfo>(infos);
|
||||
|
||||
// Discard any matches that aren't accessible
|
||||
infos = this.GetAccessibleInfos(infos);
|
||||
|
||||
// No accessible methods left
|
||||
if (infos.Length == 0)
|
||||
{
|
||||
this.ThrowNoAccessibleMethodsException(previous);
|
||||
}
|
||||
|
||||
// Handle case where we have more than one match with the same score
|
||||
this.DetectAmbiguousMatches(infos);
|
||||
|
||||
// If we get here, then there is only one best match
|
||||
_myTargetMethodInfo = infos[0];
|
||||
}
|
||||
|
||||
private CustomMethodInfo[] GetAccessibleInfos(CustomMethodInfo[] infos)
|
||||
{
|
||||
List<CustomMethodInfo> accessible = new List<CustomMethodInfo>();
|
||||
|
||||
foreach (CustomMethodInfo cmi in infos)
|
||||
{
|
||||
if (cmi.IsAccessible(this) == true)
|
||||
{
|
||||
accessible.Add(cmi);
|
||||
}
|
||||
}
|
||||
|
||||
return accessible.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle case where we have overloads with the same score
|
||||
/// </summary>
|
||||
/// <param name="infos"></param>
|
||||
private void DetectAmbiguousMatches(CustomMethodInfo[] infos)
|
||||
{
|
||||
List<CustomMethodInfo> sameScores = new List<CustomMethodInfo>();
|
||||
CustomMethodInfo first = infos[0];
|
||||
|
||||
// Find all matches with the same score as the best match
|
||||
foreach (CustomMethodInfo cmi in infos)
|
||||
{
|
||||
if (((IEquatable<CustomMethodInfo>)cmi).Equals(first) == true)
|
||||
{
|
||||
sameScores.Add(cmi);
|
||||
}
|
||||
}
|
||||
|
||||
// More than one accessible match with the same score exists
|
||||
if (sameScores.Count > 1)
|
||||
{
|
||||
this.ThrowAmbiguousMethodCallException();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Validate()
|
||||
{
|
||||
base.Validate();
|
||||
|
||||
if ((_myOnDemandFunctionReturnType != null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Any function reference in an expression must return a value
|
||||
if (object.ReferenceEquals(this.Method.ReturnType, typeof(void)))
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.FunctionHasNoReturnValue, CompileExceptionReason.FunctionHasNoReturnValue, MyName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
|
||||
ExpressionElement[] elements = _myArguments.ToArray();
|
||||
|
||||
// If we are an on-demand function, then emit that and exit
|
||||
if ((_myOnDemandFunctionReturnType != null))
|
||||
{
|
||||
this.EmitOnDemandFunction(elements, ilg, services);
|
||||
return;
|
||||
}
|
||||
|
||||
bool isOwnerMember = MyOptions.IsOwnerType(this.Method.ReflectedType);
|
||||
|
||||
// Load the owner if required
|
||||
if (MyPrevious == null && isOwnerMember == true && this.IsStatic == false)
|
||||
{
|
||||
this.EmitLoadOwner(ilg);
|
||||
}
|
||||
|
||||
this.EmitFunctionCall(this.NextRequiresAddress, ilg, services);
|
||||
}
|
||||
|
||||
private void EmitOnDemandFunction(ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Load the variable collection
|
||||
EmitLoadVariables(ilg);
|
||||
// Load the function name
|
||||
ilg.Emit(OpCodes.Ldstr, MyName);
|
||||
// Load the arguments array
|
||||
EmitElementArrayLoad(elements, typeof(object), ilg, services);
|
||||
|
||||
// Call the function to get the result
|
||||
MethodInfo mi = VariableCollection.GetFunctionInvokeMethod(_myOnDemandFunctionReturnType);
|
||||
|
||||
this.EmitMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
// Emit the arguments to a paramArray method call
|
||||
private void EmitParamArrayArguments(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Get the fixed parameters
|
||||
ParameterInfo[] fixedParameters = new ParameterInfo[_myTargetMethodInfo.MyFixedArgTypes.Length];
|
||||
Array.Copy(parameters, fixedParameters, fixedParameters.Length);
|
||||
|
||||
// Get the corresponding fixed parameters
|
||||
ExpressionElement[] fixedElements = new ExpressionElement[_myTargetMethodInfo.MyFixedArgTypes.Length];
|
||||
Array.Copy(elements, fixedElements, fixedElements.Length);
|
||||
|
||||
// Emit the fixed arguments
|
||||
this.EmitRegularFunctionInternal(fixedParameters, fixedElements, ilg, services);
|
||||
|
||||
// Get the paramArray arguments
|
||||
ExpressionElement[] paramArrayElements = new ExpressionElement[elements.Length - fixedElements.Length];
|
||||
Array.Copy(elements, fixedElements.Length, paramArrayElements, 0, paramArrayElements.Length);
|
||||
|
||||
// Emit them into an array
|
||||
EmitElementArrayLoad(paramArrayElements, _myTargetMethodInfo.ParamArrayElementType, ilg, services);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit elements into an array
|
||||
/// </summary>
|
||||
/// <param name="elements"></param>
|
||||
/// <param name="arrayElementType"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitElementArrayLoad(ExpressionElement[] elements, Type arrayElementType, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
// Load the array length
|
||||
LiteralElement.EmitLoad(elements.Length, ilg);
|
||||
|
||||
// Create the array
|
||||
ilg.Emit(OpCodes.Newarr, arrayElementType);
|
||||
|
||||
// Store the new array in a unique local and remember the index
|
||||
LocalBuilder local = ilg.DeclareLocal(arrayElementType.MakeArrayType());
|
||||
int arrayLocalIndex = local.LocalIndex;
|
||||
Utility.EmitStoreLocal(ilg, arrayLocalIndex);
|
||||
|
||||
for (int i = 0; i <= elements.Length - 1; i++)
|
||||
{
|
||||
// Load the array
|
||||
Utility.EmitLoadLocal(ilg, arrayLocalIndex);
|
||||
// Load the index
|
||||
LiteralElement.EmitLoad(i, ilg);
|
||||
// Emit the element (with any required conversions)
|
||||
ExpressionElement element = elements[i];
|
||||
element.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(element.ResultType, arrayElementType, ilg);
|
||||
// Store it into the array
|
||||
Utility.EmitArrayStore(ilg, arrayElementType);
|
||||
}
|
||||
|
||||
// Load the array
|
||||
Utility.EmitLoadLocal(ilg, arrayLocalIndex);
|
||||
}
|
||||
|
||||
public void EmitFunctionCall(bool nextRequiresAddress, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
ParameterInfo[] parameters = this.Method.GetParameters();
|
||||
ExpressionElement[] elements = _myArguments.ToArray();
|
||||
|
||||
// Emit either a regular or paramArray call
|
||||
if (_myTargetMethodInfo.IsParamArray == false)
|
||||
{
|
||||
if (_myTargetMethodInfo.IsExtensionMethod == false)
|
||||
this.EmitRegularFunctionInternal(parameters, elements, ilg, services);
|
||||
else
|
||||
this.EmitExtensionFunctionInternal(parameters, elements, ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitParamArrayArguments(parameters, elements, ilg, services);
|
||||
}
|
||||
|
||||
MemberElement.EmitMethodCall(this.ResultType, nextRequiresAddress, this.Method, ilg);
|
||||
}
|
||||
|
||||
private void EmitExtensionFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Debug.Assert(parameters.Length == elements.Length + 1, "argument count mismatch");
|
||||
if (MyPrevious == null) this.EmitLoadOwner(ilg);
|
||||
//Emit each element and any required conversions to the actual parameter type
|
||||
for (int i = 1; i <= parameters.Length - 1; i++)
|
||||
{
|
||||
ExpressionElement element = elements[i - 1];
|
||||
ParameterInfo pi = parameters[i];
|
||||
element.Emit(ilg, services);
|
||||
bool success = ImplicitConverter.EmitImplicitConvert(element.ResultType, pi.ParameterType, ilg);
|
||||
Debug.Assert(success, "conversion failed");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit the arguments to a regular method call
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <param name="elements"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private void EmitRegularFunctionInternal(ParameterInfo[] parameters, ExpressionElement[] elements, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
Debug.Assert(parameters.Length == elements.Length, "argument count mismatch");
|
||||
|
||||
// Emit each element and any required conversions to the actual parameter type
|
||||
for (int i = 0; i <= parameters.Length - 1; i++)
|
||||
{
|
||||
ExpressionElement element = elements[i];
|
||||
ParameterInfo pi = parameters[i];
|
||||
element.Emit(ilg, services);
|
||||
bool success = ImplicitConverter.EmitImplicitConvert(element.ResultType, pi.ParameterType, ilg);
|
||||
Debug.Assert(success, "conversion failed");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method info we will be calling
|
||||
/// </summary>
|
||||
private MethodInfo Method => _myTargetMethodInfo.Target;
|
||||
|
||||
public override Type ResultType
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myOnDemandFunctionReturnType != null))
|
||||
{
|
||||
return _myOnDemandFunctionReturnType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.Method.ReturnType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool RequiresAddress => !IsGetTypeMethod(this.Method);
|
||||
|
||||
protected override bool IsPublic => this.Method.IsPublic;
|
||||
|
||||
public override bool IsStatic => this.Method.IsStatic;
|
||||
public override bool IsExtensionMethod => this._myTargetMethodInfo.IsExtensionMethod;
|
||||
}
|
||||
}
|
492
ExpressionElements/MemberElements/Identifier.cs
Normal file
492
ExpressionElements/MemberElements/Identifier.cs
Normal file
@@ -0,0 +1,492 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.ComponentModel;
|
||||
using Flee.CalcEngine.PublicTypes;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.ExpressionElements.Base.Literals;
|
||||
using Flee.ExpressionElements.Literals;
|
||||
using Flee.ExpressionElements.Literals.Integral;
|
||||
using Flee.ExpressionElements.Literals.Real;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Represents an identifier")]
|
||||
internal class IdentifierElement : MemberElement
|
||||
{
|
||||
private FieldInfo _myField;
|
||||
private PropertyInfo _myProperty;
|
||||
private PropertyDescriptor _myPropertyDescriptor;
|
||||
private Type _myVariableType;
|
||||
private Type _myCalcEngineReferenceType;
|
||||
public IdentifierElement(string name)
|
||||
{
|
||||
this.MyName = name;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
// Try to bind to a field or property
|
||||
if (this.ResolveFieldProperty(MyPrevious) == true)
|
||||
{
|
||||
this.AddReferencedVariable(MyPrevious);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to find a variable with our name
|
||||
_myVariableType = MyContext.Variables.GetVariableTypeInternal(MyName);
|
||||
|
||||
// Variables are only usable as the first element
|
||||
if (MyPrevious == null && (_myVariableType != null))
|
||||
{
|
||||
this.AddReferencedVariable(MyPrevious);
|
||||
return;
|
||||
}
|
||||
|
||||
CalculationEngine ce = MyContext.CalculationEngine;
|
||||
|
||||
if ((ce != null))
|
||||
{
|
||||
ce.AddDependency(MyName, MyContext);
|
||||
_myCalcEngineReferenceType = ce.ResolveTailType(MyName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MyPrevious == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoIdentifierWithName, CompileExceptionReason.UndefinedName, MyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NoIdentifierWithNameOnType, CompileExceptionReason.UndefinedName, MyName, MyPrevious.TargetType.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ResolveFieldProperty(MemberElement previous)
|
||||
{
|
||||
MemberInfo[] members = this.GetMembers(MemberTypes.Field | MemberTypes.Property);
|
||||
|
||||
// Keep only the ones which are accessible
|
||||
members = this.GetAccessibleMembers(members);
|
||||
|
||||
if (members.Length == 0)
|
||||
{
|
||||
// No accessible members; try to resolve a virtual property
|
||||
return this.ResolveVirtualProperty(previous);
|
||||
}
|
||||
else if (members.Length > 1)
|
||||
{
|
||||
// More than one accessible member
|
||||
if (previous == null)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.IdentifierIsAmbiguous, CompileExceptionReason.AmbiguousMatch, MyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.IdentifierIsAmbiguousOnType, CompileExceptionReason.AmbiguousMatch, MyName, previous.TargetType.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only one member; bind to it
|
||||
_myField = members[0] as FieldInfo;
|
||||
if ((_myField != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assume it must be a property
|
||||
_myProperty = (PropertyInfo)members[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ResolveVirtualProperty(MemberElement previous)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
// We can't use virtual properties if we are the first element
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertyDescriptorCollection coll = TypeDescriptor.GetProperties(previous.ResultType);
|
||||
_myPropertyDescriptor = coll.Find(MyName, true);
|
||||
return (_myPropertyDescriptor != null);
|
||||
}
|
||||
|
||||
private void AddReferencedVariable(MemberElement previous)
|
||||
{
|
||||
if ((previous != null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_myVariableType != null) || MyOptions.IsOwnerType(this.MemberOwnerType) == true)
|
||||
{
|
||||
ExpressionInfo info = (ExpressionInfo)MyServices.GetService(typeof(ExpressionInfo));
|
||||
info.AddReferencedVariable(MyName);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
|
||||
this.EmitFirst(ilg);
|
||||
|
||||
if ((_myCalcEngineReferenceType != null))
|
||||
{
|
||||
this.EmitReferenceLoad(ilg);
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
this.EmitVariableLoad(ilg);
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
this.EmitFieldLoad(_myField, ilg, services);
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
this.EmitVirtualPropertyLoad(ilg);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitPropertyLoad(_myProperty, ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitReferenceLoad(FleeILGenerator ilg)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldarg_1);
|
||||
MyContext.CalculationEngine.EmitLoad(MyName, ilg);
|
||||
}
|
||||
|
||||
private void EmitFirst(FleeILGenerator ilg)
|
||||
{
|
||||
if ((MyPrevious != null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool isVariable = (_myVariableType != null);
|
||||
|
||||
if (isVariable == true)
|
||||
{
|
||||
// Load variables
|
||||
EmitLoadVariables(ilg);
|
||||
}
|
||||
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true & this.IsStatic == false)
|
||||
{
|
||||
this.EmitLoadOwner(ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitVariableLoad(FleeILGenerator ilg)
|
||||
{
|
||||
MethodInfo mi = VariableCollection.GetVariableLoadMethod(_myVariableType);
|
||||
ilg.Emit(OpCodes.Ldstr, MyName);
|
||||
this.EmitMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
private void EmitFieldLoad(System.Reflection.FieldInfo fi, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
if (fi.IsLiteral == true)
|
||||
{
|
||||
EmitLiteral(fi, ilg, services);
|
||||
}
|
||||
else if (this.ResultType.IsValueType == true & this.NextRequiresAddress == true)
|
||||
{
|
||||
EmitLdfld(fi, true, ilg);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitLdfld(fi, false, ilg);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EmitLdfld(System.Reflection.FieldInfo fi, bool indirect, FleeILGenerator ilg)
|
||||
{
|
||||
if (fi.IsStatic == true)
|
||||
{
|
||||
if (indirect == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldsflda, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldsfld, fi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (indirect == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldflda, fi);
|
||||
}
|
||||
else
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldfld, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit the load of a constant field. We can't emit a ldsfld/ldfld of a constant so we have to get its value
|
||||
/// and then emit a ldc.
|
||||
/// </summary>
|
||||
/// <param name="fi"></param>
|
||||
/// <param name="ilg"></param>
|
||||
/// <param name="services"></param>
|
||||
private static void EmitLiteral(System.Reflection.FieldInfo fi, FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
object value = fi.GetValue(null);
|
||||
Type t = value.GetType();
|
||||
TypeCode code = Type.GetTypeCode(t);
|
||||
LiteralElement elem = default(LiteralElement);
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case TypeCode.Char:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.Int32:
|
||||
elem = new Int32LiteralElement(System.Convert.ToInt32(value));
|
||||
break;
|
||||
case TypeCode.UInt32:
|
||||
elem = new UInt32LiteralElement((UInt32)value);
|
||||
break;
|
||||
case TypeCode.Int64:
|
||||
elem = new Int64LiteralElement((Int64)value);
|
||||
break;
|
||||
case TypeCode.UInt64:
|
||||
elem = new UInt64LiteralElement((UInt64)value);
|
||||
break;
|
||||
case TypeCode.Double:
|
||||
elem = new DoubleLiteralElement((double)value);
|
||||
break;
|
||||
case TypeCode.Single:
|
||||
elem = new SingleLiteralElement((float)value);
|
||||
break;
|
||||
case TypeCode.Boolean:
|
||||
elem = new BooleanLiteralElement((bool)value);
|
||||
break;
|
||||
case TypeCode.String:
|
||||
elem = new StringLiteralElement((string)value);
|
||||
break;
|
||||
default:
|
||||
elem = null;
|
||||
Debug.Fail("Unsupported constant type");
|
||||
break;
|
||||
}
|
||||
|
||||
elem.Emit(ilg, services);
|
||||
}
|
||||
|
||||
private void EmitPropertyLoad(System.Reflection.PropertyInfo pi, FleeILGenerator ilg)
|
||||
{
|
||||
System.Reflection.MethodInfo getter = pi.GetGetMethod(true);
|
||||
base.EmitMethodCall(getter, ilg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load a PropertyDescriptor based property
|
||||
/// </summary>
|
||||
/// <param name="ilg"></param>
|
||||
private void EmitVirtualPropertyLoad(FleeILGenerator ilg)
|
||||
{
|
||||
// The previous value is already on the top of the stack but we need it at the bottom
|
||||
|
||||
// Get a temporary local index
|
||||
int index = ilg.GetTempLocalIndex(MyPrevious.ResultType);
|
||||
|
||||
// Store the previous value there
|
||||
Utility.EmitStoreLocal(ilg, index);
|
||||
|
||||
// Load the variable collection
|
||||
EmitLoadVariables(ilg);
|
||||
// Load the property name
|
||||
ilg.Emit(OpCodes.Ldstr, MyName);
|
||||
|
||||
// Load the previous value and convert it to object
|
||||
Utility.EmitLoadLocal(ilg, index);
|
||||
ImplicitConverter.EmitImplicitConvert(MyPrevious.ResultType, typeof(object), ilg);
|
||||
|
||||
// Call the method to get the actual value
|
||||
MethodInfo mi = VariableCollection.GetVirtualPropertyLoadMethod(this.ResultType);
|
||||
this.EmitMethodCall(mi, ilg);
|
||||
}
|
||||
|
||||
private Type MemberOwnerType
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myField != null))
|
||||
{
|
||||
return _myField.ReflectedType;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return _myPropertyDescriptor.ComponentType;
|
||||
}
|
||||
else if ((_myProperty != null))
|
||||
{
|
||||
return _myProperty.ReflectedType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override System.Type ResultType
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myCalcEngineReferenceType != null))
|
||||
{
|
||||
return _myCalcEngineReferenceType;
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
return _myVariableType;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return _myPropertyDescriptor.PropertyType;
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
return _myField.FieldType;
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo mi = _myProperty.GetGetMethod(true);
|
||||
return mi.ReturnType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool RequiresAddress => _myPropertyDescriptor == null;
|
||||
|
||||
protected override bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null) | (_myCalcEngineReferenceType != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
return _myField.IsPublic;
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo mi = _myProperty.GetGetMethod(true);
|
||||
return mi.IsPublic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsStatic
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null))
|
||||
{
|
||||
// Variables never support static
|
||||
return false;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
// Neither do virtual properties
|
||||
return false;
|
||||
}
|
||||
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true && MyPrevious == null)
|
||||
{
|
||||
// Owner members support static if we are the first element
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Support static if we are the first (ie: we are a static import)
|
||||
return MyPrevious == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null))
|
||||
{
|
||||
// Variables always support instance
|
||||
return true;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
// So do virtual properties
|
||||
return true;
|
||||
}
|
||||
else if (MyOptions.IsOwnerType(this.MemberOwnerType) == true && MyPrevious == null)
|
||||
{
|
||||
// Owner members support instance if we are the first element
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We always support instance if we are not the first element
|
||||
return (MyPrevious != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsStatic
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((_myVariableType != null) | (_myCalcEngineReferenceType != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((_myVariableType != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((_myField != null))
|
||||
{
|
||||
return _myField.IsStatic;
|
||||
}
|
||||
else if ((_myPropertyDescriptor != null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodInfo mi = _myProperty.GetGetMethod(true);
|
||||
return mi.IsStatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
public override bool IsExtensionMethod => false;
|
||||
}
|
||||
}
|
181
ExpressionElements/MemberElements/Indexer.cs
Normal file
181
ExpressionElements/MemberElements/Indexer.cs
Normal file
@@ -0,0 +1,181 @@
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
[Obsolete("Element representing an array index")]
|
||||
internal class IndexerElement : MemberElement
|
||||
{
|
||||
private ExpressionElement _myIndexerElement;
|
||||
|
||||
private readonly ArgumentList _myIndexerElements;
|
||||
public IndexerElement(ArgumentList indexer)
|
||||
{
|
||||
_myIndexerElements = indexer;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
// Are we are indexing on an array?
|
||||
Type target = MyPrevious.TargetType;
|
||||
|
||||
// Yes, so setup for an array index
|
||||
if (target.IsArray == true)
|
||||
{
|
||||
this.SetupArrayIndexer();
|
||||
return;
|
||||
}
|
||||
|
||||
// Not an array, so try to find an indexer on the type
|
||||
if (this.FindIndexer(target) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.TypeNotArrayAndHasNoIndexerOfType, CompileExceptionReason.TypeMismatch, target.Name, _myIndexerElements);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupArrayIndexer()
|
||||
{
|
||||
_myIndexerElement = _myIndexerElements[0];
|
||||
|
||||
if (_myIndexerElements.Count > 1)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.MultiArrayIndexNotSupported, CompileExceptionReason.TypeMismatch);
|
||||
}
|
||||
else if (ImplicitConverter.EmitImplicitConvert(_myIndexerElement.ResultType, typeof(Int32), null) == false)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.ArrayIndexersMustBeOfType, CompileExceptionReason.TypeMismatch, typeof(Int32).Name);
|
||||
}
|
||||
}
|
||||
|
||||
private bool FindIndexer(Type targetType)
|
||||
{
|
||||
// Get the default members
|
||||
MemberInfo[] members = targetType.GetDefaultMembers();
|
||||
|
||||
List<MethodInfo> methods = new List<MethodInfo>();
|
||||
|
||||
// Use the first one that's valid for our indexer type
|
||||
foreach (MemberInfo mi in members)
|
||||
{
|
||||
PropertyInfo pi = mi as PropertyInfo;
|
||||
if ((pi != null))
|
||||
{
|
||||
methods.Add(pi.GetGetMethod(true));
|
||||
}
|
||||
}
|
||||
|
||||
FunctionCallElement func = new FunctionCallElement("Indexer", methods.ToArray(), _myIndexerElements);
|
||||
func.Resolve(MyServices);
|
||||
_myIndexerElement = func;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
this.EmitArrayLoad(ilg, services);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitIndexer(ilg, services);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitArrayLoad(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
_myIndexerElement.Emit(ilg, services);
|
||||
ImplicitConverter.EmitImplicitConvert(_myIndexerElement.ResultType, typeof(Int32), ilg);
|
||||
|
||||
Type elementType = this.ResultType;
|
||||
|
||||
if (elementType.IsValueType == false)
|
||||
{
|
||||
// Simple reference load
|
||||
ilg.Emit(OpCodes.Ldelem_Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.EmitValueTypeArrayLoad(ilg, elementType);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitValueTypeArrayLoad(FleeILGenerator ilg, Type elementType)
|
||||
{
|
||||
if (this.NextRequiresAddress == true)
|
||||
{
|
||||
ilg.Emit(OpCodes.Ldelema, elementType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Utility.EmitArrayLoad(ilg, elementType);
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitIndexer(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
FunctionCallElement func = (FunctionCallElement)_myIndexerElement;
|
||||
func.EmitFunctionCall(this.NextRequiresAddress, ilg, services);
|
||||
}
|
||||
|
||||
private Type ArrayType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
return MyPrevious.TargetType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsArray => MyPrevious.TargetType.IsArray;
|
||||
|
||||
protected override bool RequiresAddress => this.IsArray == false;
|
||||
|
||||
public override System.Type ResultType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
return this.ArrayType.GetElementType();
|
||||
}
|
||||
else
|
||||
{
|
||||
return _myIndexerElement.ResultType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool IsPublic
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.IsArray == true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsElementPublic((MemberElement)_myIndexerElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsStatic => false;
|
||||
public override bool IsExtensionMethod => false;
|
||||
}
|
||||
}
|
120
ExpressionElements/MemberElements/InvocationList.cs
Normal file
120
ExpressionElements/MemberElements/InvocationList.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System.Collections;
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
using Flee.PublicTypes;
|
||||
using Flee.Resources;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
internal class InvocationListElement : ExpressionElement
|
||||
{
|
||||
private readonly MemberElement _myTail;
|
||||
public InvocationListElement(IList elements, IServiceProvider services)
|
||||
{
|
||||
this.HandleFirstElement(elements, services);
|
||||
LinkElements(elements);
|
||||
Resolve(elements, services);
|
||||
_myTail = (MemberElement)elements[elements.Count - 1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Arrange elements as a linked list
|
||||
/// </summary>
|
||||
/// <param name="elements"></param>
|
||||
private static void LinkElements(IList elements)
|
||||
{
|
||||
for (int i = 0; i <= elements.Count - 1; i++)
|
||||
{
|
||||
MemberElement current = (MemberElement)elements[i];
|
||||
MemberElement nextElement = null;
|
||||
if (i + 1 < elements.Count)
|
||||
{
|
||||
nextElement = (MemberElement)elements[i + 1];
|
||||
}
|
||||
current.Link(nextElement);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleFirstElement(IList elements, IServiceProvider services)
|
||||
{
|
||||
ExpressionElement first = (ExpressionElement)elements[0];
|
||||
|
||||
// If the first element is not a member element, then we assume it is an expression and replace it with the correct member element
|
||||
if (!(first is MemberElement))
|
||||
{
|
||||
ExpressionMemberElement actualFirst = new ExpressionMemberElement(first);
|
||||
elements[0] = actualFirst;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ResolveNamespaces(elements, services);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResolveNamespaces(IList elements, IServiceProvider services)
|
||||
{
|
||||
ExpressionContext context = (ExpressionContext)services.GetService(typeof(ExpressionContext));
|
||||
ImportBase currentImport = context.Imports.RootImport;
|
||||
|
||||
while (true)
|
||||
{
|
||||
string name = GetName(elements);
|
||||
|
||||
if (name == null)
|
||||
{
|
||||
break; // TODO: might not be correct. Was : Exit While
|
||||
}
|
||||
|
||||
ImportBase import = currentImport.FindImport(name);
|
||||
|
||||
if (import == null)
|
||||
{
|
||||
break; // TODO: might not be correct. Was : Exit While
|
||||
}
|
||||
|
||||
currentImport = import;
|
||||
elements.RemoveAt(0);
|
||||
|
||||
if (elements.Count > 0)
|
||||
{
|
||||
MemberElement newFirst = (MemberElement)elements[0];
|
||||
newFirst.SetImport(currentImport);
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.Count == 0)
|
||||
{
|
||||
base.ThrowCompileException(CompileErrorResourceKeys.NamespaceCannotBeUsedAsType, CompileExceptionReason.TypeMismatch, currentImport.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetName(IList elements)
|
||||
{
|
||||
if (elements.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Is the first member a field/property element?
|
||||
var fpe = elements[0] as IdentifierElement;
|
||||
|
||||
return fpe?.MemberName;
|
||||
}
|
||||
|
||||
private static void Resolve(IList elements, IServiceProvider services)
|
||||
{
|
||||
foreach (MemberElement element in elements)
|
||||
{
|
||||
element.Resolve(services);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
_myTail.Emit(ilg, services);
|
||||
}
|
||||
|
||||
public override System.Type ResultType => _myTail.ResultType;
|
||||
}
|
||||
}
|
38
ExpressionElements/MemberElements/Miscellaneous.cs
Normal file
38
ExpressionElements/MemberElements/Miscellaneous.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Flee.ExpressionElements.Base;
|
||||
using Flee.InternalTypes;
|
||||
|
||||
|
||||
namespace Flee.ExpressionElements.MemberElements
|
||||
{
|
||||
internal class ExpressionMemberElement : MemberElement
|
||||
{
|
||||
private readonly ExpressionElement _myElement;
|
||||
public ExpressionMemberElement(ExpressionElement element)
|
||||
{
|
||||
_myElement = element;
|
||||
}
|
||||
|
||||
protected override void ResolveInternal()
|
||||
{
|
||||
}
|
||||
|
||||
public override void Emit(FleeILGenerator ilg, IServiceProvider services)
|
||||
{
|
||||
base.Emit(ilg, services);
|
||||
_myElement.Emit(ilg, services);
|
||||
if (_myElement.ResultType.IsValueType == true)
|
||||
{
|
||||
EmitValueTypeLoadAddress(ilg, this.ResultType);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool SupportsInstance => true;
|
||||
|
||||
protected override bool IsPublic => true;
|
||||
|
||||
public override bool IsStatic => false;
|
||||
public override bool IsExtensionMethod => false;
|
||||
|
||||
public override System.Type ResultType => _myElement.ResultType;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user