Files
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

2115 lines
95 KiB
C#

// ==++==
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
namespace Microsoft.JScript
{
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
public abstract class Binding : AST{
private IReflect[] argIRs;
protected MemberInfo defaultMember;
private IReflect defaultMemberReturnIR;
private bool isArrayElementAccess;
private bool isArrayConstructor;
protected bool isAssignmentToDefaultIndexedProperty;
protected bool isFullyResolved;
protected bool isNonVirtual;
internal MemberInfo[] members;
internal MemberInfo member;
protected String name;
private bool giveErrors;
static internal ConstantWrapper ReflectionMissingCW = new ConstantWrapper(System.Reflection.Missing.Value, null);
static private ConstantWrapper JScriptMissingCW = new ConstantWrapper(Missing.Value, null);
internal Binding(Context context, String name)
: base(context) {
this.argIRs = null;
this.defaultMember = null;
this.defaultMemberReturnIR = null;
this.isArrayElementAccess = false;
this.isArrayConstructor = false;
this.isAssignmentToDefaultIndexedProperty = false;
this.isFullyResolved = true;
this.isNonVirtual = false;
this.members = null;
this.member = null;
this.name = name;
this.giveErrors = true;
}
private bool Accessible(bool checkSetter){
if (this.member == null) return false;
switch(this.member.MemberType){
case MemberTypes.Constructor: return AccessibleConstructor();
case MemberTypes.Event: return false;
case MemberTypes.Field: return AccessibleField(checkSetter);
case MemberTypes.Method: return AccessibleMethod();
case MemberTypes.NestedType:
if (!((Type)this.member).IsNestedPublic){
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}
return true;
case MemberTypes.TypeInfo:
if (!((Type)this.member).IsPublic){
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}
return true;
case MemberTypes.Property: return AccessibleProperty(checkSetter);
}
return false;
}
private bool AccessibleConstructor(){
ConstructorInfo cons = (ConstructorInfo)this.member;
if ((cons is JSConstructor && ((JSConstructor)member).GetClassScope().owner.isAbstract) ||
(!(cons is JSConstructor) && cons.DeclaringType.IsAbstract)){
this.context.HandleError(JSError.CannotInstantiateAbstractClass);
return false;
}
if (cons.IsPublic) return true;
if (cons is JSConstructor){
if (((JSConstructor)cons).IsAccessibleFrom(Globals.ScopeStack.Peek())) return true;
}
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}
private bool AccessibleField(bool checkWritable){
FieldInfo field = (FieldInfo)this.member;
if (checkWritable && (field.IsInitOnly || field.IsLiteral)) return false;
if (!field.IsPublic){
JSWrappedField wfield = field as JSWrappedField;
if (wfield != null)
this.member = field = wfield.wrappedField;
JSClosureField cfield = field as JSClosureField;
JSMemberField mfield = (cfield != null ? cfield.field : field) as JSMemberField;
if (mfield == null){
if ((!field.IsFamily && !field.IsFamilyOrAssembly) || !Binding.InsideClassThatExtends(Globals.ScopeStack.Peek(), field.ReflectedType)){
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}
}else if (!mfield.IsAccessibleFrom(Globals.ScopeStack.Peek())){
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}
}
if (field.IsLiteral && field is JSVariableField){
ClassScope csc = ((JSVariableField)field).value as ClassScope;
if (csc != null && !csc.owner.IsStatic){
Lookup lookup = this as Lookup;
if (lookup == null || !lookup.InStaticCode() || lookup.InFunctionNestedInsideInstanceMethod()) return true;
if (this.giveErrors)
this.context.HandleError(JSError.InstanceNotAccessibleFromStatic, this.isFullyResolved);
return true;
}
}
if (field.IsStatic || field.IsLiteral || this.defaultMember != null || !(this is Lookup) || !((Lookup)this).InStaticCode()) return true;
if (field is JSWrappedField && field.DeclaringType == Typeob.LenientGlobalObject) return true;
if (this.giveErrors) {
if (!field.IsStatic && this is Lookup && ((Lookup)this).InStaticCode())
this.context.HandleError(JSError.InstanceNotAccessibleFromStatic, this.isFullyResolved);
else
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
}
return false;
}
private bool AccessibleMethod(){
MethodInfo meth = (MethodInfo)this.member;
return this.AccessibleMethod(meth);
}
private bool AccessibleMethod(MethodInfo meth){
if (meth == null) return false;
if (this.isNonVirtual && meth.IsAbstract){
this.context.HandleError(JSError.InvalidCall);
return false;
}
if (!meth.IsPublic){
JSWrappedMethod wmeth = meth as JSWrappedMethod;
if (wmeth != null)
meth = wmeth.method;
JSClosureMethod cmeth = meth as JSClosureMethod;
JSFieldMethod fmeth = (cmeth != null ? cmeth.method : meth) as JSFieldMethod;
if (fmeth == null){
if ((meth.IsFamily || meth.IsFamilyOrAssembly) && Binding.InsideClassThatExtends(Globals.ScopeStack.Peek(), meth.ReflectedType))
return true;
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}else if (!fmeth.IsAccessibleFrom(Globals.ScopeStack.Peek())){
if (this.giveErrors)
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
return false;
}
}
if (meth.IsStatic || this.defaultMember != null || !(this is Lookup) || !((Lookup)this).InStaticCode()) return true;
if (meth is JSWrappedMethod && ((Lookup)this).CanPlaceAppropriateObjectOnStack(((JSWrappedMethod)meth).obj)) return true;
if (this.giveErrors) {
if (!meth.IsStatic && this is Lookup && ((Lookup)this).InStaticCode())
this.context.HandleError(JSError.InstanceNotAccessibleFromStatic, this.isFullyResolved);
else
this.context.HandleError(JSError.NotAccessible, this.isFullyResolved);
}
return false;
}
private bool AccessibleProperty(bool checkSetter){
PropertyInfo prop = (PropertyInfo)this.member;
if (this.AccessibleMethod(checkSetter ? JSProperty.GetSetMethod(prop, true) : JSProperty.GetGetMethod(prop, true))) return true;
if (this.giveErrors && !checkSetter) this.context.HandleError(JSError.WriteOnlyProperty);
return false;
}
internal static bool AssignmentCompatible(IReflect lhir, AST rhexpr, IReflect rhir, bool reportError){
if (rhexpr is ConstantWrapper){
Object rhval = rhexpr.Evaluate();
if (rhval is ClassScope){
if (lhir == Typeob.Type || lhir == Typeob.Object || lhir == Typeob.String) return true;
else{
if (reportError) rhexpr.context.HandleError(JSError.TypeMismatch);
return false;
}
}
ClassScope csc = lhir as ClassScope;
if (csc != null){
EnumDeclaration ed = csc.owner as EnumDeclaration;
if (ed != null){
ConstantWrapper cw = rhexpr as ConstantWrapper;
if (cw != null && cw.value is String){
FieldInfo field = csc.GetField((String)cw.value, BindingFlags.Public|BindingFlags.Static);
if (field == null) return false;
ed.PartiallyEvaluate();
cw.value = new EnumWrapper(((JSMemberField)field).value, field.Name, csc);
}
if (rhir == Typeob.String) return true;
lhir = ed.baseType.ToType();
}
}
if (lhir is Type){
try{
Convert.CoerceT(rhval, (Type)lhir);
return true;
}catch(Exception){
if (lhir == Typeob.Single && rhval is Double){
if (((ConstantWrapper)rhexpr).isNumericLiteral) return true;
Double d = (Double)rhval;
Single s = (Single)d;
if (d.ToString().Equals(s.ToString())){
((ConstantWrapper)rhexpr).value = s;
return true;
}
}
if (lhir == Typeob.Decimal){
ConstantWrapper cw = rhexpr as ConstantWrapper;
if (cw != null && cw.isNumericLiteral){
try{
Convert.CoerceT(cw.context.GetCode(), Typeob.Decimal);
return true;
}catch(Exception){}
}
}
if (reportError)
rhexpr.context.HandleError(JSError.TypeMismatch);
}
return false;
}
}else if (rhexpr is ArrayLiteral)
return ((ArrayLiteral)rhexpr).AssignmentCompatible(lhir, reportError);
if (rhir == Typeob.Object) return true; //Too little is known about the expression to complain at compile time
if (rhir == Typeob.Double && Convert.IsPrimitiveNumericType(lhir)) return true; //Arithmetic expressions infer to Double, but might have the right result.
if (lhir is Type && typeof(Delegate).IsAssignableFrom((Type)lhir) && rhir == Typeob.ScriptFunction &&
rhexpr is Binding && ((Binding)rhexpr).IsCompatibleWithDelegate((Type)lhir))
return true;
if (Convert.IsPromotableTo(rhir, lhir))
return true; //rhexpr delivers a value that can be converted to something expected by the assignment target
if (Convert.IsJScriptArray(rhir) && Binding.ArrayAssignmentCompatible(rhexpr, lhir))
return true;
if (lhir == Typeob.String)
return true; // Everything is assignment-compatible to string.
if (rhir == Typeob.String && (lhir == Typeob.Boolean || Convert.IsPrimitiveNumericType(lhir))){
if (reportError) rhexpr.context.HandleError(JSError.PossibleBadConversionFromString);
return true;
}
if ((lhir == Typeob.Char && rhir == Typeob.String) || Convert.IsPromotableTo(lhir, rhir) ||
(Convert.IsPrimitiveNumericType(lhir) && Convert.IsPrimitiveNumericType(rhir))){
if (reportError) rhexpr.context.HandleError(JSError.PossibleBadConversion);
return true;
}
if (reportError)
rhexpr.context.HandleError(JSError.TypeMismatch);
return false;
}
private static bool ArrayAssignmentCompatible(AST ast, IReflect lhir){
// If we've made it here then we already know that lhir is not Object or String
// and that a JScript array is not promotable to lhir. There are cases where
// a JScript array is not promotable but is assignable with a warning. A
// JScript array is assignable to a System.Array or to a rank-one typed array.
if (!Convert.IsArray(lhir))
return false;
else if (lhir == Typeob.Array){
ast.context.HandleError(JSError.ArrayMayBeCopied);
return true;
}else if (Convert.GetArrayRank(lhir) == 1){
ast.context.HandleError(JSError.ArrayMayBeCopied);
return true;
}else
return false;
}
internal void CheckIfDeletable(){
if (this.member != null || this.defaultMember != null)
this.context.HandleError(JSError.NotDeletable);
this.member = null;
this.defaultMember = null;
}
internal void CheckIfUseless(){
if (this.member != null)
this.context.HandleError(JSError.UselessExpression);
}
internal static bool CheckParameters(ParameterInfo[] pars, IReflect[] argIRs, ASTList argAST, Context ctx){
return Binding.CheckParameters(pars, argIRs, argAST, ctx, 0, false, true);
}
internal static bool CheckParameters(ParameterInfo[] pars, IReflect[] argIRs, ASTList argAST, Context ctx, int offset, bool defaultIsUndefined, bool reportError){
int n = argIRs.Length;
int m = pars.Length;
bool tooManyParams = false;
if (n > m-offset) {n = m-offset; tooManyParams = true;}
for (int i = 0; i < n; i++){
IReflect formalIR = (pars[i+offset] is ParameterDeclaration) ? ((ParameterDeclaration)pars[i+offset]).ParameterIReflect : pars[i+offset].ParameterType;
IReflect actualIR = argIRs[i];
if (i == n-1 && ((formalIR is Type && Typeob.Array.IsAssignableFrom((Type)formalIR)) || formalIR is TypedArray) &&
pars[i+offset].IsDefined(Typeob.ParamArrayAttribute, false)){
tooManyParams = false;
int k = argIRs.Length;
if (i == k - 1){
if (Binding.AssignmentCompatible(formalIR, argAST[i], argIRs[i], false))
return true;
}
IReflect elemIR = formalIR is Type ? ((Type)formalIR).GetElementType() : ((TypedArray)formalIR).elementType;
for (int j = i; j < k; j++)
if (!Binding.AssignmentCompatible(elemIR, argAST[j], argIRs[j], reportError)) return false;
return true;
}
if (!Binding.AssignmentCompatible(formalIR, argAST[i], actualIR, reportError)) return false;
}
if (tooManyParams && reportError)
ctx.HandleError(JSError.TooManyParameters);
if (offset == 0 && n < m && !defaultIsUndefined) //Fewer actual parameters than formal parameters
for (int j = n; j < m; j++)
if (pars[j].DefaultValue == System.Convert.DBNull){ //No default value specified
if (j < m-1 || !pars[j].IsDefined(Typeob.ParamArrayAttribute, false)){
if (reportError)
ctx.HandleError(JSError.TooFewParameters);
IReflect formalIR = (pars[j+offset] is ParameterDeclaration) ? ((ParameterDeclaration)pars[j+offset]).ParameterIReflect : pars[j+offset].ParameterType;
Type formalType = formalIR as Type;
if (formalType != null && formalType.IsValueType && !formalType.IsPrimitive && !formalType.IsEnum)
return false; //Can't generate valid code for this since there is no general mapping from undefined to value types
}
}
return true;
}
internal override bool Delete(){
return this.EvaluateAsLateBinding().Delete();
}
internal override Object Evaluate(){
Object ob = this.GetObject();
MemberInfo member = this.member;
if (member != null)
switch(member.MemberType){
case MemberTypes.Field : return ((FieldInfo)member).GetValue(ob);
case MemberTypes.Property :
MemberInfo[] members = new MemberInfo[]{JSProperty.GetGetMethod((PropertyInfo)member, false)};
return LateBinding.CallOneOfTheMembers(members, new Object[0], false, ob, null, null, null, this.Engine);
case MemberTypes.Event : return null;
case MemberTypes.NestedType : return member;
}
if (this.members != null && this.members.Length > 0){
//Special case check for methods on builtin objects
if (this.members.Length == 1 && this.members[0].MemberType == MemberTypes.Method){
MethodInfo meth = (MethodInfo)members[0];
Type dt = meth is JSMethod ? null : meth.DeclaringType;
if (dt == Typeob.GlobalObject ||
(dt != null && dt != Typeob.StringObject && dt != Typeob.NumberObject && dt != Typeob.BooleanObject && dt.IsSubclassOf(Typeob.JSObject))){
//This only happens in fast mode. We could add InitOnly fields to the fast predefined objects and initialize them
//with instances of BuiltinFunction objects, in which case we would never get here, but we would like to avoid
//the start up cost of allocating these objects, particularly if they end up never being used (the expected common case).
return Globals.BuiltinFunctionFor(ob, meth);
//Note that meth is not wrapped because it is static
}
}
return new FunctionWrapper(this.name, ob, this.members);
}
return this.EvaluateAsLateBinding().GetValue();
}
//Returns a list of member infos that are sorted by declaring type. Superclass members always follow derived class members.
private MemberInfoList GetAllKnownInstanceBindingsForThisName(){
IReflect[] classes = this.GetAllEligibleClasses();
MemberInfoList result = new MemberInfoList();
foreach (IReflect c in classes){
if (c is ClassScope){
if (((ClassScope)c).ParentIsInSamePackage())
result.AddRange(c.GetMember(name, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.DeclaredOnly));
else
result.AddRange(c.GetMember(name, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance));
}else
result.AddRange(c.GetMember(name, BindingFlags.Public|BindingFlags.Instance));
}
return result;
}
//Returns a list of the classes visible from the current scope.
//Currently, classes are returned only if they are ancestors of the current class, or reside in the same package.
private IReflect[] GetAllEligibleClasses(){
ArrayList classes = new ArrayList(16);
ClassScope currentClass = null;
PackageScope currentPackage = null;
ScriptObject scope = Globals.ScopeStack.Peek();
while (scope is WithObject || scope is BlockScope)
scope = scope.GetParent();
if (scope is FunctionScope)
scope = ((FunctionScope)scope).owner.enclosing_scope;
if (scope is ClassScope){
currentClass = (ClassScope)scope;
currentPackage = currentClass.package;
}
if (currentClass != null)
currentClass.AddClassesFromInheritanceChain(this.name, classes);
if (currentPackage != null)
currentPackage.AddClassesExcluding(currentClass, this.name, classes);
else
((IActivationObject)scope).GetGlobalScope().AddClassesExcluding(currentClass, this.name, classes);
IReflect[] result = new IReflect[classes.Count]; classes.CopyTo(result);
return result;
}
protected abstract Object GetObject();
protected abstract void HandleNoSuchMemberError();
internal override IReflect InferType(JSField inference_target){
Debug.Assert(this.members != null); //Have to call a PartiallyEvaluate routine before calling InferType
if (this.isArrayElementAccess){
IReflect ir = this.defaultMemberReturnIR;
return ir is TypedArray ? ((TypedArray)ir).elementType : ((Type)ir).GetElementType();
}
if (this.isAssignmentToDefaultIndexedProperty){
if (this.member is PropertyInfo) //Could be null if no binding was found
return ((PropertyInfo)this.member).PropertyType;
return Typeob.Object;
}
MemberInfo member = this.member;
if (member is FieldInfo){
JSWrappedField wf = member as JSWrappedField;
if (wf != null) member = wf.wrappedField;
if (member is JSVariableField)
return ((JSVariableField)member).GetInferredType(inference_target);
else
return ((FieldInfo)member).FieldType;
}
if (member is PropertyInfo)
if (member is JSProperty)
return ((JSProperty)member).PropertyIR();
else{
PropertyInfo prop = (PropertyInfo)member;
if (prop.DeclaringType == Typeob.GlobalObject)
return (IReflect)prop.GetValue(this.Globals.globalObject, null);
else
return prop.PropertyType;
}
if (member is Type)
return Typeob.Type;
if (member is EventInfo)
return typeof(EventInfo);
if (this.members.Length > 0 && this.Engine.doFast)
return Typeob.ScriptFunction;
return Typeob.Object;
}
internal virtual IReflect InferTypeOfCall(JSField inference_target, bool isConstructor){
Debug.Assert(this.members != null); //Have to call a PartiallyEvaluate routine before calling InferTypeOfCall
if (!this.isFullyResolved) return Typeob.Object;
if (this.isArrayConstructor)
return this.defaultMemberReturnIR;
if (this.isArrayElementAccess){
IReflect ir = this.defaultMemberReturnIR;
return ir is TypedArray ? ((TypedArray)ir).elementType : ((Type)ir).GetElementType();
}
MemberInfo member = this.member;
if (member is JSFieldMethod) return isConstructor ? Typeob.Object : ((JSFieldMethod)member).ReturnIR();
if (member is MethodInfo) return ((MethodInfo)member).ReturnType;
if (member is JSConstructor) return ((JSConstructor)member).GetClassScope();
if (member is ConstructorInfo) return ((ConstructorInfo)member).DeclaringType;
if (member is Type) return (Type)member;
if (member is FieldInfo && ((FieldInfo)member).IsLiteral){
Object val = member is JSVariableField ? ((JSVariableField)member).value : ((FieldInfo)member).GetValue(null);
if (val is ClassScope || val is TypedArray) return (IReflect)val;
}
return Typeob.Object;
}
private static bool InsideClassThatExtends(ScriptObject scope, Type type){
while (scope is WithObject || scope is BlockScope)
scope = scope.GetParent();
if (scope is ClassScope)
return type.IsAssignableFrom(((ClassScope)scope).GetBakedSuperType());
if (scope is FunctionScope)
return Binding.InsideClassThatExtends(((FunctionScope)scope).owner.enclosing_scope, type);
//Eval does not see non public members, so don't worry about StackFrame
return false;
}
internal void InvalidateBinding(){
this.isAssignmentToDefaultIndexedProperty = false;
this.isArrayConstructor = false;
this.isArrayElementAccess = false;
this.defaultMember = null;
this.member = null;
this.members = new MemberInfo[0];
}
internal bool IsCompatibleWithDelegate(Type delegateType){
Debug.Assert(this.members != null && this.members.Length > 0);
MethodInfo invoke = delegateType.GetMethod("Invoke");
ParameterInfo[] dparams = invoke.GetParameters();
Type drtype = invoke.ReturnType;
foreach (MemberInfo mem in this.members){
if (mem is MethodInfo){
MethodInfo meth = (MethodInfo)mem;
Type returnType = null;
if (meth is JSFieldMethod){
IReflect returnIR = ((JSFieldMethod)meth).ReturnIR();
if (returnIR is ClassScope)
returnType = ((ClassScope)returnIR).GetBakedSuperType(); //JScript cannot define delegates, so baked is OK.
else if (returnIR is Type)
returnType = (Type)returnIR;
else
returnType = Convert.ToType(returnIR);
if (((JSFieldMethod)meth).func.isExpandoMethod) return false;
}else
returnType = meth.ReturnType;
if (returnType == drtype && Class.ParametersMatch(meth.GetParameters(), dparams)){
this.member = meth;
this.isFullyResolved = true;
return true;
}
}
}
return false;
}
public static bool IsMissing(Object value){
return value is Missing;
}
internal override bool OkToUseAsType(){
MemberInfo member = this.member;
if (member is Type) return this.isFullyResolved = true;
if (member is FieldInfo){
FieldInfo field = (FieldInfo)member;
if (field.IsLiteral){
if (field is JSMemberField && ((JSMemberField)field).value is ClassScope && !field.IsStatic)
return false;
return this.isFullyResolved = true;
}
if (!(member is JSField) && field.IsStatic && field.GetValue(null) is Type) return this.isFullyResolved = true;
}
return false;
}
private int PlaceValuesForHiddenParametersOnStack(ILGenerator il, MethodInfo meth, ParameterInfo[] pars){
int offset = 0;
if (meth is JSFieldMethod){
FunctionObject func = ((JSFieldMethod)meth).func;
if (func != null && func.isMethod) return 0;
if (this is Lookup)
((Lookup)this).TranslateToILDefaultThisObject(il);
else
this.TranslateToILObject(il, Typeob.Object, false);
this.EmitILToLoadEngine(il);
return 0;
}
Object[] attrs = meth.GetCustomAttributes(Typeob.JSFunctionAttribute, false);
if (attrs == null || attrs.Length == 0) return 0;
JSFunctionAttributeEnum attr = ((JSFunctionAttribute)attrs[0]).attributeValue;
if ((attr & JSFunctionAttributeEnum.HasThisObject) != 0){
offset = 1;
Type pt = pars[0].ParameterType;
if (this is Lookup && pt == Typeob.Object)
((Lookup)this).TranslateToILDefaultThisObject(il);
else {
if (Typeob.ArrayObject.IsAssignableFrom(member.DeclaringType))
this.TranslateToILObject(il, Typeob.ArrayObject, false);
else
this.TranslateToILObject(il, pt, false);
}
}
if ((attr & JSFunctionAttributeEnum.HasEngine) != 0){
offset += 1;
this.EmitILToLoadEngine(il);
}
return offset;
}
internal static void PlaceArgumentsOnStack(ILGenerator il, ParameterInfo[] pars, ASTList args, int offset, int rhoffset, AST missing){
int k = args.count;
int n = k+offset;
int m = pars.Length-rhoffset;
bool varargs = m > 0 && pars[m-1].IsDefined(Typeob.ParamArrayAttribute, false) &&
!(k == m && Convert.IsArrayType(args[k-1].InferType(null)));
Type varargElemType = varargs ? pars[--m].ParameterType.GetElementType() : null;
if (n > m) n = m;
for (int i = offset; i < n; i++){
Type ptype = pars[i].ParameterType;
AST arg = args[i-offset];
if (arg is ConstantWrapper && ((ConstantWrapper)arg).value == System.Reflection.Missing.Value){
Object defVal = pars[i].DefaultValue;
((ConstantWrapper)arg).value = defVal != System.Convert.DBNull ? defVal : null;
}
if (ptype.IsByRef)
arg.TranslateToILReference(il, ptype.GetElementType());
else
arg.TranslateToIL(il, ptype);
}
if (n < m){
for (int i = n; i < m; i++){
Type ptype = pars[i].ParameterType;
if (pars[i].DefaultValue == System.Convert.DBNull) //No default value was specified
if (ptype.IsByRef)
missing.TranslateToILReference(il, ptype.GetElementType());
else
missing.TranslateToIL(il, ptype);
else
if (ptype.IsByRef)
(new ConstantWrapper(pars[i].DefaultValue, null)).TranslateToILReference(il, ptype.GetElementType());
else
(new ConstantWrapper(pars[i].DefaultValue, null)).TranslateToIL(il, ptype);
}
}
if (varargs){
n -= offset; //The number of arguments in argList that are already on the stack
m = k>n ? k-n : 0; //The number of arguments in argList that are to be placed in the vararg array
ConstantWrapper.TranslateToILInt(il, m);
il.Emit(OpCodes.Newarr, varargElemType);
bool doLdelema = varargElemType.IsValueType && !varargElemType.IsPrimitive;
for (int i = 0; i < m; i++){
il.Emit(OpCodes.Dup);
ConstantWrapper.TranslateToILInt(il, i);
if (doLdelema)
il.Emit(OpCodes.Ldelema, varargElemType);
args[i+n].TranslateToIL(il, varargElemType);
Binding.TranslateToStelem(il, varargElemType);
}
}
}
internal bool RefersToMemoryLocation(){
if (this.isFullyResolved){
if (this.isArrayElementAccess) return true;
return this.member is FieldInfo;
}
return false;
}
internal override void ResolveCall(ASTList args, IReflect[] argIRs, bool constructor, bool brackets){
this.argIRs = argIRs;
if (this.members == null || this.members.Length == 0){
if (constructor && this.isFullyResolved && this.Engine.doFast)
if (this.member != null && (this.member is Type || (this.member is FieldInfo && ((FieldInfo)this.member).IsLiteral)))
this.context.HandleError(JSError.NoConstructor);
else
this.HandleNoSuchMemberError();
else
this.HandleNoSuchMemberError();
return; //Have to do a runtime lookup
}
MemberInfo member = null;
if (!(this is CallableExpression) && !(constructor && brackets))
try{
if (constructor)
this.member = member = JSBinder.SelectConstructor(this.members, argIRs);
else{
MethodInfo meth;
this.member = member = meth = JSBinder.SelectMethod(this.members, argIRs); //Returns property getters as well
if (meth != null && meth.IsSpecialName){ //Property getter.
ParameterInfo[] pars = meth.GetParameters();
if (pars == null || pars.Length == 0){
//Have to guard against calling some parameterless properties directly
if ((meth is JSWrappedMethod && ((JSWrappedMethod)meth).GetWrappedObject() is GlobalObject) ||
(argIRs.Length > 0 || (!(meth is JSMethod) && Typeob.ScriptFunction.IsAssignableFrom(meth.ReturnType)))){
this.member = member = this.ResolveOtherKindOfCall(args, argIRs, constructor, brackets);
return;
}
IReflect mrt = meth is JSFieldMethod ? ((JSFieldMethod)meth).ReturnIR() : meth.ReturnType;
if (mrt != Typeob.Object && mrt != Typeob.ScriptFunction)
this.context.HandleError(JSError.InvalidCall);
else{
this.member = member = this.ResolveOtherKindOfCall(args, argIRs, constructor, brackets);
return;
}
}else if (this.name == meth.Name){
if (this.name.StartsWith("get_") || this.name.StartsWith("set_")){
this.context.HandleError(JSError.NotMeantToBeCalledDirectly);
this.member = null;
return;
}
}
}
}
}catch(AmbiguousMatchException){
if (constructor)
this.context.HandleError(JSError.AmbiguousConstructorCall, this.isFullyResolved);
else
this.context.HandleError(JSError.AmbiguousMatch, this.isFullyResolved);
return;
}catch(JScriptException e){
this.context.HandleError((Microsoft.JScript.JSError)(e.ErrorNumber & 0xffff), e.Message, true);
return;
}
if (member == null)
member = this.ResolveOtherKindOfCall(args, argIRs, constructor, brackets);
if (member == null) return; //Already complained about it, if applicable. Do runtime lookup.
if (!this.Accessible(false)){
this.member = null;
return;
}
WarnIfObsolete();
if (member is MethodBase){
if (member.IsDefined(Typeob.JSFunctionAttribute, false) && !(this.defaultMember is PropertyInfo)){
int hidden = 0;
Object[] attrs = member.GetCustomAttributes(Typeob.JSFunctionAttribute, false);
JSFunctionAttributeEnum attr = ((JSFunctionAttribute)attrs[0]).attributeValue;
if ((constructor && !(member is ConstructorInfo)) || (attr & JSFunctionAttributeEnum.HasArguments) != 0){
//Can only happen when doing an Eval from slow mode code
//Cannot call such methods directly, so bind to the associated field instead
this.member = LateBinding.SelectMember(this.members);
this.defaultMember = null;
return;
}
if ((attr & JSFunctionAttributeEnum.HasThisObject) != 0)
hidden = 1;
if ((attr & JSFunctionAttributeEnum.HasEngine) != 0)
hidden += 1;
if (!Binding.CheckParameters(((MethodBase)member).GetParameters(), argIRs, args, this.context, hidden, true, this.isFullyResolved)){
this.member = null;
return;
}
}else if (constructor && member is JSFieldMethod){
//Do a late bound call so that the function as constructor semantics work out
this.member = LateBinding.SelectMember(this.members);
return;
}else if (constructor && member is ConstructorInfo && !(member is JSConstructor) && typeof(Delegate).IsAssignableFrom(member.DeclaringType)){
this.context.HandleError(JSError.DelegatesShouldNotBeExplicitlyConstructed);
this.member = null;
return;
}else
if (!Binding.CheckParameters(((MethodBase)member).GetParameters(), argIRs, args, this.context, 0, false, this.isFullyResolved)){
this.member = null;
return;
}
}
return;
}
internal override Object ResolveCustomAttribute(ASTList args, IReflect[] argIRs, AST target){
try{
this.ResolveCall(args, argIRs, true, false);
}catch(AmbiguousMatchException){
this.context.HandleError(JSError.AmbiguousConstructorCall);
return null;
}
JSConstructor jscons = this.member as JSConstructor;
if (jscons != null){
ClassScope csc = jscons.GetClassScope();
if (csc.owner.IsCustomAttribute()) return csc;
}else{
ConstructorInfo cons = this.member as ConstructorInfo;
if (cons != null){
Type attrType = cons.DeclaringType;
if (typeof(System.Attribute).IsAssignableFrom(attrType)){
Object[] usageAttrs = attrType.GetCustomAttributes(typeof(System.AttributeUsageAttribute), false);
if (usageAttrs.Length > 0) return attrType;
}
}
}
this.context.HandleError(JSError.InvalidCustomAttributeClassOrCtor);
return null;
}
internal void ResolveLHValue(){
MemberInfo member = this.member = LateBinding.SelectMember(this.members);
if ((member != null && !this.Accessible(true)) || (this.member == null && this.members.Length > 0)){
this.context.HandleError(JSError.AssignmentToReadOnly, this.isFullyResolved);
this.member = null; //Recover by going late bound. The runtime routine will fail silently
this.members = new MemberInfo[0];
return;
}
if (member is JSPrototypeField){ //Go late bound
this.member = null;
this.members = new MemberInfo[0];
return;
}
this.WarnIfNotFullyResolved();
this.WarnIfObsolete();
return;
}
private MemberInfo ResolveOtherKindOfCall(ASTList argList, IReflect[] argIRs, bool constructor, bool brackets){
//There is no method or constructor corresponding to this binding. But there is something else.
MemberInfo member = this.member = LateBinding.SelectMember(this.members); //Choose something
//If the chosen member is a read-only property on the global object of type Type, replace it with the type.
if (member is PropertyInfo && !(member is JSProperty) && member.DeclaringType == Typeob.GlobalObject){
PropertyInfo prop = (PropertyInfo)member;
Type ptype = prop.PropertyType;
if (ptype == Typeob.Type)
member = (Type)prop.GetValue(null, null);
else if (constructor && brackets){ //Map properties returning fast mode constructor functions to types
MethodInfo meth = ptype.GetMethod("CreateInstance", BindingFlags.Public|BindingFlags.Instance|BindingFlags.DeclaredOnly);
if (meth != null){
Type rtype = meth.ReturnType;
if (rtype == Typeob.BooleanObject)
member = Typeob.Boolean;
else if (rtype == Typeob.StringObject)
member = Typeob.String;
else
member = rtype;
}
}
}
//Check for casts to type expressions, i.e. int[](x)
CallableExpression ce = this as CallableExpression;
if (ce != null){
ConstantWrapper cw = ce.expression as ConstantWrapper;
if (cw != null && cw.InferType(null) is Type)
member = new JSGlobalField(null, null, cw.value, FieldAttributes.Literal|FieldAttributes.Public);
}
//If the chosen member is a literal field containing a class/function or a nested type, we replace members and try again.
if (member is Type){
if (constructor){
if (brackets){
this.isArrayConstructor = true;
this.defaultMember = member;
this.defaultMemberReturnIR = new TypedArray((Type)member, argIRs.Length);
for (int i = 0, n = argIRs.Length; i < n; i++){
if (argIRs[i] != Typeob.Object && !Convert.IsPrimitiveNumericType(argIRs[i])){
argList[i].context.HandleError(JSError.TypeMismatch, this.isFullyResolved);
break;
}
}
return this.member = member;
}else{
ConstructorInfo[] constructors = ((Type)member).GetConstructors(BindingFlags.Instance|BindingFlags.Public);
if (constructors == null || constructors.Length == 0){
this.context.HandleError(JSError.NoConstructor);
this.member = null;
return null;
}
this.members = constructors;
this.ResolveCall(argList, argIRs, true, brackets);
return this.member;
}
}else{
if (!brackets && argIRs.Length == 1){
//Dealing with a type cast
if (typeof(Delegate).IsAssignableFrom((Type)member) && this.isFullyResolved){
Binding b = argList[0] as Binding;
if (b == null || !b.IsCompatibleWithDelegate((Type)member)){
this.context.HandleError(JSError.InvalidCall);
}
}
return member;
}
this.context.HandleError(JSError.InvalidCall);
return this.member = null;
}
}
if (member is JSPrototypeField)
//Have to go late-bound
return this.member = null;
if (member is FieldInfo && ((FieldInfo)member).IsLiteral){
if (!this.AccessibleField(false)) return this.member = null;
Object val = member is JSVariableField ? ((JSVariableField)member).value : ((FieldInfo)member).GetValue(null);
if ((val is ClassScope || val is Type)){
if (constructor){
if (brackets){
this.isArrayConstructor = true;
this.defaultMember = member;
this.defaultMemberReturnIR = new TypedArray((val is ClassScope ? (IReflect)val : (IReflect)val), argIRs.Length);
for (int i = 0, n = argIRs.Length; i < n; i++){
if (argIRs[i] != Typeob.Object && !Convert.IsPrimitiveNumericType(argIRs[i])){
argList[i].context.HandleError(JSError.TypeMismatch, this.isFullyResolved);
break;
}
}
return this.member = member;
}else{
if (val is ClassScope && !((ClassScope)val).owner.isStatic){
ConstantWrapper cw = null;
if (this is Member && (cw = ((Member)this).rootObject as ConstantWrapper) != null && !(cw.Evaluate() is Namespace)){
((Member)this).rootObject.context.HandleError(JSError.NeedInstance);
return null;
}
}
this.members = val is ClassScope ? ((ClassScope)val).constructors : ((Type)val).GetConstructors(BindingFlags.Instance|BindingFlags.Public);
if (this.members == null || this.members.Length == 0){
this.context.HandleError(JSError.NoConstructor);
this.member = null;
return null;
}
this.ResolveCall(argList, argIRs, true, brackets);
return this.member;
}
}else{
if (!brackets && argIRs.Length == 1){
Type ty = val as Type;
if (ty != null && typeof(Delegate).IsAssignableFrom(ty) && this.isFullyResolved){
Binding b = argList[0] as Binding;
if (b == null || !b.IsCompatibleWithDelegate(ty)){
this.context.HandleError(JSError.InvalidCall);
}
}
return this.member = ty != null ? ty : member;
}
this.context.HandleError(JSError.InvalidCall);
return this.member = null;
}
}
if (val is TypedArray){
if (!constructor){ //Casting a value to an array type
if (argIRs.Length != 1 || brackets) goto ReportError;
return this.member = member;
}
if (!brackets) goto ReportError; //new T(...) where T is an array type.
if (argIRs.Length == 0) goto ReportError; //new T[] where T is an array type.
this.isArrayConstructor = true;
this.defaultMember = member;
this.defaultMemberReturnIR = new TypedArray((IReflect)val, argIRs.Length);
for (int i = 0, n = argIRs.Length; i < n; i++){
if (argIRs[i] != Typeob.Object && !Convert.IsPrimitiveNumericType(argIRs[i])){
argList[i].context.HandleError(JSError.TypeMismatch, this.isFullyResolved);
break;
}
}
return this.member = member;
}
if (val is FunctionObject){
FunctionObject func = (FunctionObject)val;
if (!func.isExpandoMethod && !func.Must_save_stack_locals && (func.own_scope.ProvidesOuterScopeLocals == null || func.own_scope.ProvidesOuterScopeLocals.count == 0))
return this.member = ((JSVariableField)this.member).GetAsMethod(func.own_scope);
return this.member; //Have to call via the field so that the stack frame is set up correctly
}
}
//Check the type of this binding for more information
IReflect ir = this.InferType(null);
Type t = ir as Type;
if (!brackets && ((t != null && Typeob.ScriptFunction.IsAssignableFrom(t)) || ir is ScriptFunction)){
this.defaultMember = member;
if (t == null){
this.defaultMemberReturnIR = ir.GetType(); //The appropriate subtype of ScriptFunction
this.member = this.defaultMemberReturnIR.GetMethod(constructor ? "CreateInstance" : "Invoke",
BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.Instance);
if (this.member == null){
this.defaultMemberReturnIR = Typeob.ScriptFunction;
this.member = this.defaultMemberReturnIR.GetMethod(constructor ? "CreateInstance" : "Invoke",
BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.Instance);
}
if (this.member != null){
if (this.member.Name == "CreateInstance" && this.member.DeclaringType == typeof(ActiveXObjectConstructor))
this.context.HandleError(JSError.ActiveXObjectNotSupportedOnRotor, false);
}
return this.member;
}else{
// We have a script function but it might not be an "expando" function.
if (constructor && this.members.Length != 0 && this.members[0] is JSFieldMethod){
JSFieldMethod fieldmethod = (JSFieldMethod)this.members[0];
fieldmethod.func.PartiallyEvaluate();
if (!fieldmethod.func.isExpandoMethod)
this.context.HandleError(JSError.NotAnExpandoFunction, this.isFullyResolved);
}
this.defaultMemberReturnIR = t;
return this.member = t.GetMethod(constructor ? "CreateInstance" : "Invoke",
BindingFlags.DeclaredOnly|BindingFlags.Public|BindingFlags.Instance);
}
}
if (ir == Typeob.Type){ //Type cast to type only known at runtime
//Have to go late-bound
this.member = null;
return null;
}
if (ir == Typeob.Object || (ir is ScriptObject && brackets && !(ir is ClassScope)))
//The result of evaluating this binding could be a callable/constructable thing or a thing with a default indexed property.
//Or it could not, but we don't know enough at compile time to moan about it.
return member;
//Perhaps this is an array element access
if (ir is TypedArray || (ir is Type && ((Type)ir).IsArray)){
int n = argIRs.Length;
int m = ir is TypedArray ? ((TypedArray)ir).rank : ((Type)ir).GetArrayRank();
if (n != m)
this.context.HandleError(JSError.IncorrectNumberOfIndices, this.isFullyResolved);
else{
for (int i = 0; i < m; i++){
if (argIRs[i] != Typeob.Object && (!Convert.IsPrimitiveNumericType(argIRs[i]) || Convert.IsBadIndex(argList[i]))){
argList[i].context.HandleError(JSError.TypeMismatch, this.isFullyResolved);
break;
}
}
}
if (constructor){
if (!brackets) goto ReportError;
//dealing with new Arr[...]. Give an error if the array element type is not Object, Type, or ScriptFunction
IReflect elemIR = ir is TypedArray ? ((TypedArray)ir).elementType : ((Type)ir).GetElementType();
if (ir != Typeob.Object && !(ir is ClassScope) &&
!(ir is Type && !Typeob.Type.IsAssignableFrom((Type)ir) && !Typeob.ScriptFunction.IsAssignableFrom((Type)ir)))
goto ReportError;
}
this.isArrayElementAccess = true;
this.defaultMember = member;
this.defaultMemberReturnIR = ir;
return null; //Delay looking up the method to call until code gen time.
}
//Perhaps The result of evaluating this binding is an object that has a default indexed property/method that can be called
if (constructor) goto ReportError; //new binding(....) does not make sense for a default indexed property or method
//Check for a default indexed property or default method
if (brackets && ir == Typeob.String && (this.argIRs.Length != 1 || !Convert.IsPrimitiveNumericType(argIRs[0])))
ir = Typeob.StringObject;
MemberInfo[] defaultMembers = brackets || !(ir is ScriptObject) ? JSBinder.GetDefaultMembers(ir) : null;
if (defaultMembers != null && defaultMembers.Length > 0){
try{
this.defaultMember = member;
this.defaultMemberReturnIR = ir;
return this.member = JSBinder.SelectMethod(this.members = defaultMembers, argIRs); //Returns property getters as well
}catch(AmbiguousMatchException){
this.context.HandleError(JSError.AmbiguousMatch, this.isFullyResolved);
return this.member = null;
}
}
//Perhaps this is calling a delegate
if (!brackets && ir is Type && (typeof(Delegate)).IsAssignableFrom((Type)ir)){
this.defaultMember = member;
this.defaultMemberReturnIR = ir;
return this.member = ((Type)ir).GetMethod("Invoke");
}
ReportError:
//We now know enough to say that this binding really is not callable/constructable. Say so.
if (constructor)
this.context.HandleError(JSError.NeedType, this.isFullyResolved);
else if (brackets)
this.context.HandleError(JSError.NotIndexable, this.isFullyResolved);
else
this.context.HandleError(JSError.FunctionExpected, this.isFullyResolved);
return this.member = null;
}
protected void ResolveRHValue(){
MemberInfo member = this.member = LateBinding.SelectMember(this.members);
JSLocalField lfield = this.member as JSLocalField;
if (lfield != null){
FunctionObject funcOb = lfield.value as FunctionObject;
if (funcOb != null){
FunctionScope enclosingScope = funcOb.enclosing_scope as FunctionScope;
if (enclosingScope != null) enclosingScope.closuresMightEscape = true;
}
}
if (member is JSPrototypeField){ //Have to go late bound
this.member = null;
return;
}
if (!this.Accessible(false)){
this.member = null;
return;
}
WarnIfObsolete();
this.WarnIfNotFullyResolved();
}
internal override void SetPartialValue(AST partial_value){
Binding.AssignmentCompatible(this.InferType(null), partial_value, partial_value.InferType(null), this.isFullyResolved);
}
internal void SetPartialValue(ASTList argList, IReflect[] argIRs, AST partial_value, bool inBrackets){
if (this.members == null || this.members.Length == 0){
this.HandleNoSuchMemberError();
this.isAssignmentToDefaultIndexedProperty = true;
return; //Have to do a runtime lookup
}
this.PartiallyEvaluate(); //The rhside value of the binding delivers the object with the default indexed property we are assigning to
IReflect ir = this.InferType(null);
this.isAssignmentToDefaultIndexedProperty = true;
if (ir == Typeob.Object){
JSVariableField jsvf = this.member as JSVariableField;
if (jsvf == null || !jsvf.IsLiteral || !(jsvf.value is ClassScope))
return; //Not enough is known at compile time to give an error
ir = Typeob.Type;
goto giveError;
}
//Might be an assignment to an array element
if ((ir is TypedArray || (ir is Type && ((Type)ir).IsArray))){
bool gaveAnError = false;
//Check dimension
int n = argIRs.Length;
int m = ir is TypedArray ? ((TypedArray)ir).rank : ((Type)ir).GetArrayRank();
if (n != m){
this.context.HandleError(JSError.IncorrectNumberOfIndices, this.isFullyResolved);
gaveAnError = true;
}
//Check type of indices
for (int i = 0; i < m; i++){
if (!gaveAnError && i < n && argIRs[i] != Typeob.Object &&
(!Convert.IsPrimitiveNumericType(argIRs[i]) || Convert.IsBadIndex(argList[i]))){
argList[i].context.HandleError(JSError.TypeMismatch, this.isFullyResolved);
gaveAnError = true;
}
}
this.isArrayElementAccess = true;
this.isAssignmentToDefaultIndexedProperty = false;
this.defaultMember = member;
this.defaultMemberReturnIR = ir;
IReflect elemIR = ir is TypedArray ? ((TypedArray)ir).elementType : ((Type)ir).GetElementType();
Binding.AssignmentCompatible(elemIR, partial_value, partial_value.InferType(null), this.isFullyResolved);
return;
}
MemberInfo[] defaultMembers = JSBinder.GetDefaultMembers(ir);
if (defaultMembers != null && defaultMembers.Length > 0 && this.member != null){
try{
PropertyInfo prop = JSBinder.SelectProperty(defaultMembers, argIRs);
if (prop == null){
this.context.HandleError(JSError.NotIndexable, Convert.ToTypeName(ir));
return;
}
if (JSProperty.GetSetMethod(prop, true) == null){
if (ir == Typeob.String)
this.context.HandleError(JSError.UselessAssignment);
else
this.context.HandleError(JSError.AssignmentToReadOnly, this.isFullyResolved&&this.Engine.doFast);
return;
}
if (!Binding.CheckParameters(prop.GetIndexParameters(), argIRs, argList, this.context, 0, false, true)){
return;
}
this.defaultMember = this.member;
this.defaultMemberReturnIR = ir;
this.members = defaultMembers;
this.member = prop;
}catch(AmbiguousMatchException){
this.context.HandleError(JSError.AmbiguousMatch, this.isFullyResolved);
this.member = null;
}
return;
}
giveError:
this.member = null;
if (!inBrackets)
this.context.HandleError(JSError.IllegalAssignment);
else
this.context.HandleError(JSError.NotIndexable, Convert.ToTypeName(ir));
}
internal override void SetValue(Object value){
MemberInfo member = this.member;
Object ob = this.GetObject();
if (member is FieldInfo){
FieldInfo field = (FieldInfo)member;
if (field.IsLiteral || field.IsInitOnly) return;
if (!(field is JSField) || field is JSWrappedField)
value = Convert.CoerceT(value, field.FieldType, false);
field.SetValue(ob, value, BindingFlags.SuppressChangeType, null, null);
return;
}
if (member is PropertyInfo){
PropertyInfo prop = (PropertyInfo)member;
if (ob is ClassScope && !(prop is JSProperty)){
JSProperty.SetValue(prop, ((WithObject)((ClassScope)ob).GetParent()).contained_object, value, null);
return;
}
if (!(prop is JSProperty))
value = Convert.CoerceT(value, prop.PropertyType, false);
JSProperty.SetValue(prop, ob, value, null);
return;
}
if (this.members == null || this.members.Length == 0){
this.EvaluateAsLateBinding().SetValue(value);
return;
}
throw new JScriptException(JSError.IllegalAssignment);
}
internal override void TranslateToIL(ILGenerator il, Type rtype){
this.TranslateToIL(il, rtype, false, false);
}
internal void TranslateToIL(ILGenerator il, Type rtype, bool calledFromDelete){
this.TranslateToIL(il, rtype, false, false, calledFromDelete);
}
private void TranslateToIL(ILGenerator il, Type rtype, bool preSet, bool preSetPlusGet){
this.TranslateToIL(il, rtype, preSet, preSetPlusGet, false);
}
private void TranslateToIL(ILGenerator il, Type rtype, bool preSet, bool preSetPlusGet, bool calledFromDelete){
if (this.member is FieldInfo){
FieldInfo field = (FieldInfo)this.member;
bool isStatic = field.IsStatic || field.IsLiteral;
if (field.IsLiteral && field is JSMemberField){
Object val = ((JSMemberField)field).value;
FunctionObject func = val as FunctionObject;
isStatic = func == null || !func.isExpandoMethod;
}
if (!isStatic || field is JSClosureField){
this.TranslateToILObject(il, field.DeclaringType, true);
if (preSetPlusGet)
il.Emit(OpCodes.Dup);
isStatic = false;
}
if (!preSet){
Object tok = field is JSField ? ((JSField)field).GetMetaData() : field is JSFieldInfo ? ((JSFieldInfo)field).field : field;
if (tok is FieldInfo && !((FieldInfo)tok).IsLiteral)
il.Emit(isStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, (FieldInfo)tok);
else if (tok is LocalBuilder)
il.Emit(OpCodes.Ldloc, (LocalBuilder)tok);
else if (field.IsLiteral){
(new ConstantWrapper(field.GetValue(null), this.context)).TranslateToIL(il, rtype);
return;
}else
Convert.EmitLdarg(il, (short)tok);
Convert.Emit(this, il, field.FieldType, rtype);
}
return;
}
if (this.member is PropertyInfo){
PropertyInfo prop = (PropertyInfo)this.member;
MethodInfo meth = preSet ? JSProperty.GetSetMethod(prop, true) : JSProperty.GetGetMethod(prop, true);
if (meth == null){
if (preSet) return;
if (this is Lookup){
il.Emit(OpCodes.Ldc_I4, (int)JSError.WriteOnlyProperty);
il.Emit(OpCodes.Newobj, CompilerGlobals.scriptExceptionConstructor);
il.Emit(OpCodes.Throw);
}else
il.Emit(OpCodes.Ldnull);
return;
}
bool isStatic = meth.IsStatic && !(meth is JSClosureMethod);
if (!isStatic){
Type obType = meth.DeclaringType;
if (obType == Typeob.StringObject && meth.Name.Equals("get_length")){
this.TranslateToILObject(il, Typeob.String, false);
meth = CompilerGlobals.stringLengthMethod;
}else
this.TranslateToILObject(il, obType, true);
}
if (!preSet){
Debug.Assert(meth.GetParameters().Length == 0);
if (meth is JSMethod)
meth = ((JSMethod)meth).GetMethodInfo(compilerGlobals);
else if (meth is JSMethodInfo)
meth = ((JSMethodInfo)meth).method;
if (isStatic){
il.Emit(OpCodes.Call, meth);
}else{
if (preSetPlusGet)
il.Emit(OpCodes.Dup);
if (!this.isNonVirtual && meth.IsVirtual && !meth.IsFinal && !meth.ReflectedType.IsSealed)
il.Emit(OpCodes.Callvirt, meth);
else
il.Emit(OpCodes.Call, meth);
}
Convert.Emit(this, il, meth.ReturnType, rtype);
}
return;
}
if (this.member is MethodInfo){
MethodInfo meth = this.member is JSMethod ? ((JSMethod)this.member).GetMethodInfo(compilerGlobals) :
(this.member is JSMethodInfo ? ((JSMethodInfo)this.member).method : (MethodInfo)this.member);
if (typeof(Delegate).IsAssignableFrom(rtype)){
if (!meth.IsStatic){
Type obType = meth.DeclaringType;
this.TranslateToILObject(il, obType, false);
if (obType.IsValueType)
il.Emit(OpCodes.Box, obType);
}else
il.Emit(OpCodes.Ldnull);
if (meth.IsVirtual && !meth.IsFinal && !meth.ReflectedType.IsSealed){
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldvirtftn, meth);
}else
il.Emit(OpCodes.Ldftn, meth);
ConstructorInfo cons = rtype.GetConstructor(new Type[]{Typeob.Object, typeof(UIntPtr)});
if (cons == null) cons = rtype.GetConstructor(new Type[]{Typeob.Object, typeof(IntPtr)});
il.Emit(OpCodes.Newobj, cons);
return;
}
//ResolveRHValue will never set this.member to a MethodInfo. The above case only happens
//because IsCompatibleWithDelegate sets this.member to the method that matches the delegate type.
if (member is JSExpandoIndexerMethod){
//Need to put the expando class instance on the stack as the this object in expressions such as exInstance["foo"]90
MemberInfo mem = this.member;
this.member = this.defaultMember;
this.TranslateToIL(il, Typeob.Object);
this.member = mem;
return;
}
il.Emit(OpCodes.Ldnull); //get something on the stack, in case we DO get here
Convert.Emit(this, il, Typeob.Object, rtype);
return;
}
Object done = null;
if (this is Lookup)
((Lookup)this).TranslateToLateBinding(il);
else{
if (!preSet && !preSetPlusGet)
done = this.TranslateToSpeculativeEarlyBindings(il, rtype, false);
((Member)this).TranslateToLateBinding(il, done != null);
if (preSetPlusGet)
done = this.TranslateToSpeculativeEarlyBindings(il, rtype, true);
}
if (preSetPlusGet)
il.Emit(OpCodes.Dup);
if (!preSet){
if (this is Lookup && !calledFromDelete)
il.Emit(OpCodes.Call, CompilerGlobals.getValue2Method);
else
il.Emit(OpCodes.Call, CompilerGlobals.getNonMissingValueMethod);
Convert.Emit(this, il, Typeob.Object, rtype);
if (done != null)
il.MarkLabel((Label)done);
}
}
internal override void TranslateToILCall(ILGenerator il, Type rtype, ASTList argList, bool construct, bool brackets){
MemberInfo member = this.member;
if (this.defaultMember != null){
if (this.isArrayConstructor){
TypedArray tArr = (TypedArray)this.defaultMemberReturnIR;
Type eType = Convert.ToType(tArr.elementType);
Debug.Assert(tArr.rank == argList.count);
int r = tArr.rank;
if (r == 1){
argList[0].TranslateToIL(il, Typeob.Int32);
il.Emit(OpCodes.Newarr, eType);
}else{
Type aType = tArr.ToType();
Type[] dTypes = new Type[r];
for (int i = 0; i < r; i++) dTypes[i] = Typeob.Int32;
for (int i = 0, n = argList.count; i < n; i++)
argList[i].TranslateToIL(il, Typeob.Int32);
TypeBuilder eTypeB = eType as TypeBuilder;
if (eTypeB != null){
MethodInfo cons = ((ModuleBuilder)aType.Module).GetArrayMethod(aType, ".ctor", CallingConventions.HasThis, Typeob.Void, dTypes);
il.Emit(OpCodes.Newobj, cons);
}else{
ConstructorInfo cons = aType.GetConstructor(dTypes);
il.Emit(OpCodes.Newobj, cons);
}
}
Convert.Emit(this, il, tArr.ToType(), rtype);
return;
}
this.member = this.defaultMember;
IReflect defIR = this.defaultMemberReturnIR;
Type defType = defIR is Type ? (Type)defIR : Convert.ToType(defIR);
this.TranslateToIL(il, defType);
if (this.isArrayElementAccess){
Debug.Assert(defType.IsArray);
for (int i = 0, m = argList.count; i < m; i++)
argList[i].TranslateToIL(il, Typeob.Int32);
Type etype = defType.GetElementType();
int n = defType.GetArrayRank();
if (n == 1){
Binding.TranslateToLdelem(il, etype);
}else{
Type[] indexTypes = new Type[n];
for (int i = 0; i < n; i++) indexTypes[i] = Typeob.Int32;
MethodInfo getter = defType.GetMethod("Get", indexTypes);
il.Emit(OpCodes.Call, getter);
}
Convert.Emit(this, il, etype, rtype);
return;
}
this.member = member;
}
if (member is MethodInfo){
MethodInfo meth = (MethodInfo)member;
Type dt = meth.DeclaringType;
Type rt = meth.ReflectedType;
ParameterInfo[] pars = meth.GetParameters();
bool isStatic = meth.IsStatic;
if (!isStatic && this.defaultMember == null)
this.TranslateToILObject(il, dt, true);
if (meth is JSClosureMethod)
this.TranslateToILObject(il, dt, false);
ConstantWrapper missing = null;
int offset = 0;
if (meth is JSFieldMethod || meth.IsDefined(Typeob.JSFunctionAttribute, false)){
offset = this.PlaceValuesForHiddenParametersOnStack(il, meth, pars);
missing = Binding.JScriptMissingCW;
}else
missing = Binding.ReflectionMissingCW;
if (argList.count == 1 && missing == Binding.JScriptMissingCW && this.defaultMember is PropertyInfo){
//Dealing with the CreateInstance method of a constructor function
Debug.Assert(meth.Name == "CreateInstance" || meth.Name == "Invoke");
il.Emit(OpCodes.Ldc_I4_1);
il.Emit(OpCodes.Newarr, Typeob.Object);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldc_I4_0);
argList[0].TranslateToIL(il, Typeob.Object);
il.Emit(OpCodes.Stelem_Ref);
}else
Binding.PlaceArgumentsOnStack(il, pars, argList, offset, 0, missing);
if (meth is JSMethod)
meth = ((JSMethod)meth).GetMethodInfo(compilerGlobals);
else if (meth is JSMethodInfo)
meth = ((JSMethodInfo)meth).method;
if (!this.isNonVirtual && meth.IsVirtual && !meth.IsFinal && !rt.IsSealed)
il.Emit(OpCodes.Callvirt, meth);
else
il.Emit(OpCodes.Call, meth);
Convert.Emit(this, il, meth.ReturnType, rtype);
return;
}
if (member is ConstructorInfo){
Debug.Assert(construct);
ConstructorInfo cons = (ConstructorInfo)member;
ParameterInfo[] pars = cons.GetParameters();
bool instanceNestedClassConstructor = false;
if (cons.IsDefined(Typeob.JSFunctionAttribute, false)){
Object[] attrs = cons.GetCustomAttributes(Typeob.JSFunctionAttribute, false);
instanceNestedClassConstructor = (((JSFunctionAttribute)attrs[0]).attributeValue & JSFunctionAttributeEnum.IsInstanceNestedClassConstructor) != 0;
}
if (instanceNestedClassConstructor){
Binding.PlaceArgumentsOnStack(il, pars, argList, 0, 1, Binding.ReflectionMissingCW);
this.TranslateToILObject(il, pars[pars.Length-1].ParameterType, false);
}else
Binding.PlaceArgumentsOnStack(il, pars, argList, 0, 0, Binding.ReflectionMissingCW);
Type outerClass = null;
if (member is JSConstructor && (outerClass = ((JSConstructor)member).OuterClassType()) != null)
this.TranslateToILObject(il, outerClass, false);
bool needEngine = false;
Type t = cons.DeclaringType;
if (cons is JSConstructor){
cons = ((JSConstructor)cons).GetConstructorInfo(compilerGlobals);
needEngine = true;
}else
needEngine = typeof(INeedEngine).IsAssignableFrom(t);
il.Emit(OpCodes.Newobj, cons);
if (needEngine){
il.Emit(OpCodes.Dup);
this.EmitILToLoadEngine(il);
il.Emit(OpCodes.Callvirt, CompilerGlobals.setEngineMethod);
}
Convert.Emit(this, il, t, rtype);
return;
}
Type mt = member as Type;
if (mt != null){
Debug.Assert(!construct && !brackets);
Debug.Assert(argList.count == 1);
AST arg0 = argList[0];
if (arg0 is NullLiteral){
il.Emit(OpCodes.Ldnull);
return;
}
IReflect arg0ir = arg0.InferType(null);
if (arg0ir == Typeob.ScriptFunction && typeof(Delegate).IsAssignableFrom(mt))
arg0.TranslateToIL(il, mt);
else{
Type argType = Convert.ToType(arg0ir);
arg0.TranslateToIL(il, argType);
Convert.Emit(this, il, argType, mt, true);
}
Convert.Emit(this, il, mt, rtype);
return;
}
if (member is FieldInfo && ((FieldInfo)member).IsLiteral){
Object val = member is JSVariableField ? ((JSVariableField)member).value : ((FieldInfo)member).GetValue(null);
if (val is Type || val is ClassScope || val is TypedArray){
Debug.Assert(argList.count == 1);
AST arg0 = argList[0];
if (arg0 is NullLiteral){
il.Emit(OpCodes.Ldnull);
return;
}
ClassScope csc = val as ClassScope;
if (csc != null){
EnumDeclaration ed = csc.owner as EnumDeclaration;
if (ed != null)
val = ed.baseType.ToType();
}
Type argType = Convert.ToType(arg0.InferType(null));
arg0.TranslateToIL(il, argType);
Type t = val is Type ? (Type)val : val is ClassScope ? Convert.ToType((ClassScope)val) : ((TypedArray)val).ToType();
Convert.Emit(this, il, argType, t, true);
if (!rtype.IsEnum)
Convert.Emit(this, il, t, rtype);
return;
}
}
LocalBuilder loc = null;
for (int i = 0, n = argList.count; i < n; i++){
if (argList[i] is AddressOf){
loc = il.DeclareLocal(typeof(Object[]));
break;
}
}
Object done = null;
if (member == null && (this.members == null || this.members.Length == 0)){
if (this is Lookup)
((Lookup)this).TranslateToLateBinding(il);
else{
done = this.TranslateToSpeculativeEarlyBoundCalls(il, rtype, argList, construct, brackets);
((Member)this).TranslateToLateBinding(il, done != null);
}
argList.TranslateToIL(il, typeof(Object[]));
if (loc != null){
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, loc);
}
if (construct)
il.Emit(OpCodes.Ldc_I4_1);
else
il.Emit(OpCodes.Ldc_I4_0);
if (brackets)
il.Emit(OpCodes.Ldc_I4_1);
else
il.Emit(OpCodes.Ldc_I4_0);
this.EmitILToLoadEngine(il);
il.Emit(OpCodes.Call, CompilerGlobals.callMethod);
Convert.Emit(this, il, Typeob.Object, rtype);
if (loc != null){
for (int i = 0, n = argList.count; i < n; i++){
AddressOf addr = argList[i] as AddressOf;
if (addr != null){
addr.TranslateToILPreSet(il);
il.Emit(OpCodes.Ldloc, loc);
ConstantWrapper.TranslateToILInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
Convert.Emit(this, il, Typeob.Object, Convert.ToType(addr.InferType(null)));
addr.TranslateToILSet(il, null);
}
}
}
if (done != null)
il.MarkLabel((Label)done);
return;
}
this.TranslateToILWithDupOfThisOb(il);
argList.TranslateToIL(il, typeof(Object[]));
if (loc != null){
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, loc);
}
if (construct)
il.Emit(OpCodes.Ldc_I4_1);
else
il.Emit(OpCodes.Ldc_I4_0);
if (brackets)
il.Emit(OpCodes.Ldc_I4_1);
else
il.Emit(OpCodes.Ldc_I4_0);
this.EmitILToLoadEngine(il);
il.Emit(OpCodes.Call, CompilerGlobals.callValueMethod);
Convert.Emit(this, il, Typeob.Object, rtype);
if (loc != null){
for (int i = 0, n = argList.count; i < n; i++){
AddressOf addr = argList[i] as AddressOf;
if (addr != null){
addr.TranslateToILPreSet(il);
il.Emit(OpCodes.Ldloc, loc);
ConstantWrapper.TranslateToILInt(il, i);
il.Emit(OpCodes.Ldelem_Ref);
Convert.Emit(this, il, Typeob.Object, Convert.ToType(addr.InferType(null)));
addr.TranslateToILSet(il, null);
}
}
}
}
internal override void TranslateToILDelete(ILGenerator il, Type rtype){
if (this is Lookup)
((Lookup)this).TranslateToLateBinding(il);
else
((Member)this).TranslateToLateBinding(il, false);
il.Emit(OpCodes.Call, CompilerGlobals.deleteMethod);
Convert.Emit(this, il, Typeob.Boolean, rtype);
}
protected abstract void TranslateToILObject(ILGenerator il, Type obtype, bool noValue);
internal override void TranslateToILPreSet(ILGenerator il){
this.TranslateToIL(il, null, true, false);
}
internal override void TranslateToILPreSet(ILGenerator il, ASTList argList){
if (this.isArrayElementAccess){
this.member = this.defaultMember;
IReflect defIR = this.defaultMemberReturnIR;
Type defType = defIR is Type ? (Type)defIR : Convert.ToType(defIR);
this.TranslateToIL(il, defType);
Debug.Assert(defType.IsArray);
for (int i = 0, m = argList.count; i < m; i++)
argList[i].TranslateToIL(il, Typeob.Int32);
if (defType.GetArrayRank() == 1){
Type etype = defType.GetElementType();
if (etype.IsValueType && !etype.IsPrimitive && !etype.IsEnum)
il.Emit(OpCodes.Ldelema, etype);
}
return;
}
Debug.Assert(this.isAssignmentToDefaultIndexedProperty);
if (this.member is PropertyInfo && this.defaultMember != null){ //early bound to default indexed property
PropertyInfo prop = (PropertyInfo)this.member;
this.member = this.defaultMember;
this.TranslateToIL(il, Convert.ToType(this.defaultMemberReturnIR));
this.member = prop;
Binding.PlaceArgumentsOnStack(il, prop.GetIndexParameters(), argList, 0, 0, Binding.ReflectionMissingCW);
return;
}
base.TranslateToILPreSet(il, argList);
}
internal override void TranslateToILPreSetPlusGet(ILGenerator il){
this.TranslateToIL(il, Convert.ToType(this.InferType(null)), false, true);
}
internal override void TranslateToILPreSetPlusGet(ILGenerator il, ASTList argList, bool inBrackets){
if (this.isArrayElementAccess){
this.member = this.defaultMember;
IReflect defIR = this.defaultMemberReturnIR;
Type defType = defIR is Type ? (Type)defIR : Convert.ToType(defIR);
Debug.Assert(defType.IsArray);
this.TranslateToIL(il, defType);
il.Emit(OpCodes.Dup);
int n = defType.GetArrayRank();
LocalBuilder[] iTemp = new LocalBuilder[n];
for (int i = 0, m = argList.count; i < m; i++){
argList[i].TranslateToIL(il, Typeob.Int32);
iTemp[i] = il.DeclareLocal(Typeob.Int32);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, iTemp[i]);
}
Type etype = defType.GetElementType();
if (n == 1){
if (etype.IsValueType && !etype.IsPrimitive)
il.Emit(OpCodes.Ldelema, etype);
Binding.TranslateToLdelem(il, etype);
}else{
Type[] indexTypes = new Type[n];
for (int i = 0; i < n; i++) indexTypes[i] = Typeob.Int32;
MethodInfo getter = defType.GetMethod("Get", indexTypes);
il.Emit(OpCodes.Call, getter);
}
LocalBuilder eTemp = il.DeclareLocal(etype);
il.Emit(OpCodes.Stloc, eTemp);
for (int i = 0; i < n; i++)
il.Emit(OpCodes.Ldloc, iTemp[i]);
il.Emit(OpCodes.Ldloc, eTemp);
return;
}
Debug.Assert(this.isAssignmentToDefaultIndexedProperty);
if (this.member != null){
//Go late bound. It is too much work, for too little gain, to do this early bound
if (this.defaultMember != null)
this.member = this.defaultMember;
}
base.TranslateToILPreSetPlusGet(il, argList, inBrackets);
}
internal override Object TranslateToILReference(ILGenerator il, Type rtype){
if (this.member is FieldInfo){
FieldInfo field = (FieldInfo)this.member;
Type ftype = field.FieldType;
if (rtype == ftype){
bool isStatic = field.IsStatic;
if (!isStatic)
this.TranslateToILObject(il, field.DeclaringType, true);
Object tok = field is JSField ? ((JSField)field).GetMetaData() : field is JSFieldInfo ? ((JSFieldInfo)field).field : field;
if (tok is FieldInfo)
il.Emit(isStatic ? OpCodes.Ldsflda : OpCodes.Ldflda, (FieldInfo)tok);
else if (tok is LocalBuilder)
il.Emit(OpCodes.Ldloca, (LocalBuilder)tok);
else
il.Emit(OpCodes.Ldarga, (short)tok);
return null;
}
}
return base.TranslateToILReference(il, rtype);
}
internal override void TranslateToILSet(ILGenerator il, AST rhvalue){
if (this.isArrayElementAccess){
IReflect defIR = this.defaultMemberReturnIR;
Type defType = defIR is Type ? (Type)defIR : Convert.ToType(defIR);
Debug.Assert(defType.IsArray);
int n = defType.GetArrayRank();
Type etype = defType.GetElementType();
if (rhvalue != null)
rhvalue.TranslateToIL(il, etype);
if (n == 1){
Binding.TranslateToStelem(il, etype);
}else{
Type[] indexTypes = new Type[n+1];
for (int i = 0; i < n; i++) indexTypes[i] = Typeob.Int32;
indexTypes[n] = etype;
MethodInfo setter = defType.GetMethod("Set", indexTypes);
il.Emit(OpCodes.Call, setter);
}
return;
}
if (this.isAssignmentToDefaultIndexedProperty){
if (this.member is PropertyInfo && this.defaultMember != null){ //early bound to default indexed property
PropertyInfo prop = (PropertyInfo)this.member;
MethodInfo meth = JSProperty.GetSetMethod(prop, false);
//Guard against trying to assign to properties on the Global object
JSWrappedMethod wmeth = meth as JSWrappedMethod;
if (wmeth == null || !(wmeth.GetWrappedObject() is GlobalObject)){
if (meth is JSMethod)
meth = ((JSMethod)meth).GetMethodInfo(compilerGlobals);
else if (meth is JSMethodInfo)
meth = ((JSMethodInfo)meth).method;
if (rhvalue != null)
rhvalue.TranslateToIL(il, prop.PropertyType);
if (meth.IsVirtual && !meth.IsFinal && !meth.ReflectedType.IsSealed)
il.Emit(OpCodes.Callvirt, meth);
else
il.Emit(OpCodes.Call, meth);
return;
}
}
base.TranslateToILSet(il, rhvalue);
return;
}
if (this.member is FieldInfo){
FieldInfo field = (FieldInfo)this.member;
if (rhvalue != null)
rhvalue.TranslateToIL(il, field.FieldType);
if (field.IsLiteral || field.IsInitOnly){
il.Emit(OpCodes.Pop);
return;
}
Object tok = field is JSField ? ((JSField)field).GetMetaData() : field is JSFieldInfo ? ((JSFieldInfo)field).field : field;
FieldInfo f = tok as FieldInfo;
if (f != null)
il.Emit(f.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, f);
else if (tok is LocalBuilder)
il.Emit(OpCodes.Stloc, (LocalBuilder)tok);
else
il.Emit(OpCodes.Starg, (short)tok);
return;
}
if (this.member is PropertyInfo){
PropertyInfo prop = (PropertyInfo)this.member;
if (rhvalue != null)
rhvalue.TranslateToIL(il, prop.PropertyType);
MethodInfo meth = JSProperty.GetSetMethod(prop, true);
if (meth == null){
il.Emit(OpCodes.Pop);
return;
}
if (meth is JSMethod)
meth = ((JSMethod)meth).GetMethodInfo(compilerGlobals);
else if (meth is JSMethodInfo)
meth = ((JSMethodInfo)meth).method;
if (meth.IsStatic && !(meth is JSClosureMethod))
il.Emit(OpCodes.Call, meth);
else{
if (!this.isNonVirtual && meth.IsVirtual && !meth.IsFinal && !meth.ReflectedType.IsSealed)
il.Emit(OpCodes.Callvirt, meth);
else
il.Emit(OpCodes.Call, meth);
}
return;
}
//do speculative early bound assignments
Object done = this.TranslateToSpeculativeEarlyBoundSet(il, rhvalue);
if (rhvalue != null)
rhvalue.TranslateToIL(il, Typeob.Object);
il.Emit(OpCodes.Call, CompilerGlobals.setValueMethod);
if (done != null)
il.MarkLabel((Label)done);
}
protected abstract void TranslateToILWithDupOfThisOb(ILGenerator il);
private static void TranslateToLdelem(ILGenerator il, Type etype){
switch(Type.GetTypeCode(etype)){
case TypeCode.SByte:
il.Emit(OpCodes.Ldelem_I1); break;
case TypeCode.Boolean:
case TypeCode.Byte:
il.Emit(OpCodes.Ldelem_U1); break;
case TypeCode.Int16:
il.Emit(OpCodes.Ldelem_I2); break;
case TypeCode.Char:
case TypeCode.UInt16:
il.Emit(OpCodes.Ldelem_U2); break;
case TypeCode.Int32:
il.Emit(OpCodes.Ldelem_I4); break;
case TypeCode.UInt32:
il.Emit(OpCodes.Ldelem_U4); break;
case TypeCode.Int64:
case TypeCode.UInt64:
il.Emit(OpCodes.Ldelem_I8); break;
case TypeCode.Single:
il.Emit(OpCodes.Ldelem_R4); break;
case TypeCode.Double:
il.Emit(OpCodes.Ldelem_R8); break;
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
case TypeCode.Object:
if (etype.IsValueType){
il.Emit(OpCodes.Ldelema, etype);
il.Emit(OpCodes.Ldobj, etype);
}else
il.Emit(OpCodes.Ldelem_Ref);
break;
}
}
private Object TranslateToSpeculativeEarlyBoundSet(ILGenerator il, AST rhvalue){
this.giveErrors = false;
Object done = null;
bool needObject = true;
LocalBuilder objectLocal = null;
LocalBuilder valueLocal = null;
Label next = il.DefineLabel();
MemberInfoList members = this.GetAllKnownInstanceBindingsForThisName();
for (int i = 0, n = members.count; i < n; i++){
MemberInfo member = members[i];
FieldInfo field = null;
MethodInfo setter = null;
PropertyInfo prop = null;
if (member is FieldInfo){
field = (FieldInfo)member;
if (field.IsLiteral || field.IsInitOnly) continue;
}else if (member is PropertyInfo){
prop = (PropertyInfo)member;
if (prop.GetIndexParameters().Length > 0 ||
(setter = JSProperty.GetSetMethod(prop, true)) == null) continue;
}else
continue;
this.member = member;
if (!this.Accessible(true)) continue;
if (needObject){
needObject = false;
if (rhvalue == null){
valueLocal = il.DeclareLocal(Typeob.Object);
il.Emit(OpCodes.Stloc, valueLocal);
}
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldfld, CompilerGlobals.objectField);
objectLocal = il.DeclareLocal(Typeob.Object);
il.Emit(OpCodes.Stloc, objectLocal);
done = il.DefineLabel();
}
Type t = member.DeclaringType;
il.Emit(OpCodes.Ldloc, objectLocal);
il.Emit(OpCodes.Isinst, t);
LocalBuilder objectTemp = il.DeclareLocal(t);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, objectTemp);
il.Emit(OpCodes.Brfalse, next);
il.Emit(OpCodes.Ldloc, objectTemp);
if (rhvalue == null)
il.Emit(OpCodes.Ldloc, valueLocal);
if (field != null){
if (rhvalue == null)
Convert.Emit(this, il, Typeob.Object, field.FieldType);
else
rhvalue.TranslateToIL(il, field.FieldType);
if (field is JSField)
il.Emit(OpCodes.Stfld, (FieldInfo)((JSField)field).GetMetaData());
else if (field is JSFieldInfo)
il.Emit(OpCodes.Stfld, ((JSFieldInfo)field).field);
else
il.Emit(OpCodes.Stfld, field);
}else{
if (rhvalue == null)
Convert.Emit(this, il, Typeob.Object, prop.PropertyType);
else
rhvalue.TranslateToIL(il, prop.PropertyType);
if (setter is JSMethod)
setter = ((JSMethod)setter).GetMethodInfo(this.compilerGlobals);
else if (setter is JSMethodInfo)
setter = ((JSMethodInfo)setter).method;
if (setter.IsVirtual && !setter.IsFinal && !t.IsSealed)
il.Emit(OpCodes.Callvirt, setter);
else
il.Emit(OpCodes.Call, setter);
}
il.Emit(OpCodes.Pop); //Get rid of the LateBound instance
il.Emit(OpCodes.Br, (Label)done);
il.MarkLabel(next);
next = il.DefineLabel();
}
if (valueLocal != null)
il.Emit(OpCodes.Ldloc, valueLocal);
this.member = null;
return done;
}
private Object TranslateToSpeculativeEarlyBindings(ILGenerator il, Type rtype, bool getObjectFromLateBindingInstance){
//Find all members named this.name to which the current context might have access to.
//Generate early bound access to these, guarded by runtime type checks.
this.giveErrors = false;
Object done = null;
bool needObject = true;
LocalBuilder objectLocal = null;
Label next = il.DefineLabel();
MemberInfoList members = this.GetAllKnownInstanceBindingsForThisName();
for (int i = 0, n = members.count; i < n; i++){
MemberInfo member = members[i];
if (!(member is FieldInfo) && (!(member is PropertyInfo) ||
((PropertyInfo)member).GetIndexParameters().Length > 0 ||
JSProperty.GetGetMethod((PropertyInfo)member, true) == null)) continue;
this.member = member;
if (!this.Accessible(false)) continue;
if (needObject){
needObject = false;
if (getObjectFromLateBindingInstance){
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Ldfld, CompilerGlobals.objectField);
}else
this.TranslateToILObject(il, Typeob.Object, false);
objectLocal = il.DeclareLocal(Typeob.Object);
il.Emit(OpCodes.Stloc, objectLocal);
done = il.DefineLabel();
}
Type t = member.DeclaringType;
il.Emit(OpCodes.Ldloc, objectLocal);
il.Emit(OpCodes.Isinst, t);
LocalBuilder objectTemp = il.DeclareLocal(t);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, objectTemp);
il.Emit(OpCodes.Brfalse_S, next);
il.Emit(OpCodes.Ldloc, objectTemp);
if (member is FieldInfo){
FieldInfo field = (FieldInfo)member;
if (field is JSField)
il.Emit(OpCodes.Ldfld, (FieldInfo)((JSField)field).GetMetaData());
else if (field is JSFieldInfo)
il.Emit(OpCodes.Ldfld, ((JSFieldInfo)field).field);
else
il.Emit(OpCodes.Ldfld, (FieldInfo)field);
Convert.Emit(this, il, field.FieldType, rtype);
}else if (member is PropertyInfo){
MethodInfo getter = JSProperty.GetGetMethod((PropertyInfo)member, true);
if (getter is JSMethod)
getter = ((JSMethod)getter).GetMethodInfo(this.compilerGlobals);
else if (getter is JSMethodInfo)
getter = ((JSMethodInfo)getter).method;
if (getter.IsVirtual && !getter.IsFinal && !t.IsSealed)
il.Emit(OpCodes.Callvirt, getter);
else
il.Emit(OpCodes.Call, getter);
Convert.Emit(this, il, getter.ReturnType, rtype);
}
il.Emit(OpCodes.Br, (Label)done);
il.MarkLabel(next);
next = il.DefineLabel();
}
il.MarkLabel(next);
if (!needObject && !getObjectFromLateBindingInstance)
il.Emit(OpCodes.Ldloc, objectLocal);
this.member = null;
return done;
}
private Object TranslateToSpeculativeEarlyBoundCalls(ILGenerator il, Type rtype, ASTList argList, bool construct, bool brackets){
this.giveErrors = false;
Object done = null;
bool needObject = true;
LocalBuilder objectLocal = null;
Label next = il.DefineLabel();
IReflect[] classes = this.GetAllEligibleClasses();
if (construct){
//dealing with "new ob.foo(...)" or "new ob.foo[...]".
//Early binding only makes sense if foo is a nested type marked as protected or package
//But nested types are static and hence will not show up late bound via the object.
//There is the possibility that a programmer can declare a static field and initialize it to a type.
//In that case late bound access works if the field is public, but not otherwise.
//This does not seem like a core scenario that justifies a lot of code inside this if statement.
return done;
}
Debug.Assert(argList.count == 0 || this.argIRs.Length == argList.count);
foreach (IReflect c in classes){
MemberInfo[] members = c.GetMember(this.name, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance);
try{
MethodInfo meth = JSBinder.SelectMethod(members, this.argIRs); //Returns property getters as well
if (meth != null){
if (!Binding.CheckParameters(meth.GetParameters(), this.argIRs, argList, this.context, 0, true, false))
continue;
//If meth is a base class method or if meth is an override of a base class method and the base class is in the same package as c
//there is no need to have an instance check for c
if (meth is JSFieldMethod){
FunctionObject func = ((JSFieldMethod)meth).func;
if (func != null && (func.attributes & MethodAttributes.NewSlot) == 0 && ((ClassScope)c).ParentIsInSamePackage())
continue;
}else if (meth is JSWrappedMethod && ((JSWrappedMethod)meth).obj is ClassScope && ((JSWrappedMethod)meth).GetPackage() == ((ClassScope)c).package)
continue;
this.member = meth;
if (!this.Accessible(false)) continue;
if (needObject){
needObject = false;
this.TranslateToILObject(il, Typeob.Object, false);
objectLocal = il.DeclareLocal(Typeob.Object);
il.Emit(OpCodes.Stloc, objectLocal);
done = il.DefineLabel();
}
Type t = meth.DeclaringType;
il.Emit(OpCodes.Ldloc, objectLocal);
il.Emit(OpCodes.Isinst, t);
LocalBuilder objectTemp = il.DeclareLocal(t);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Stloc, objectTemp);
il.Emit(OpCodes.Brfalse, next);
il.Emit(OpCodes.Ldloc, objectTemp);
Binding.PlaceArgumentsOnStack(il, meth.GetParameters(), argList, 0, 0, Binding.ReflectionMissingCW);
if (meth is JSMethod)
meth = ((JSMethod)meth).GetMethodInfo(compilerGlobals);
else if (meth is JSMethodInfo)
meth = ((JSMethodInfo)meth).method;
if (meth.IsVirtual && !meth.IsFinal && !t.IsSealed)
il.Emit(OpCodes.Callvirt, meth);
else
il.Emit(OpCodes.Call, meth);
Convert.Emit(this, il, meth.ReturnType, rtype);
il.Emit(OpCodes.Br, (Label)done);
il.MarkLabel(next);
next = il.DefineLabel();
}
}catch(AmbiguousMatchException){}
}
il.MarkLabel(next);
if (!needObject)
il.Emit(OpCodes.Ldloc, objectLocal);
this.member = null;
return done;
}
internal static void TranslateToStelem(ILGenerator il, Type etype){
switch(Type.GetTypeCode(etype)){
case TypeCode.SByte:
case TypeCode.Boolean:
case TypeCode.Byte:
il.Emit(OpCodes.Stelem_I1); break;
case TypeCode.Int16:
case TypeCode.Char:
case TypeCode.UInt16:
il.Emit(OpCodes.Stelem_I2); break;
case TypeCode.Int32:
case TypeCode.UInt32:
il.Emit(OpCodes.Stelem_I4); break;
case TypeCode.Int64:
case TypeCode.UInt64:
il.Emit(OpCodes.Stelem_I8); break;
case TypeCode.Single:
il.Emit(OpCodes.Stelem_R4); break;
case TypeCode.Double:
il.Emit(OpCodes.Stelem_R8); break;
case TypeCode.Decimal:
case TypeCode.DateTime:
case TypeCode.String:
case TypeCode.Object:
if (etype.IsValueType){
il.Emit(OpCodes.Stobj, etype);
}else
il.Emit(OpCodes.Stelem_Ref);
break;
}
}
private void WarnIfNotFullyResolved(){
if (this.isFullyResolved || this.member == null) return;
if (this.member is JSVariableField && ((JSVariableField)this.member).type == null) return;
if (!this.Engine.doFast && this.member is IWrappedMember) return;
ScriptObject scope = Globals.ScopeStack.Peek();
while (scope != null){
if (scope is WithObject && !((WithObject)scope).isKnownAtCompileTime){
this.context.HandleError(JSError.AmbiguousBindingBecauseOfWith);
return;
}else if (scope is ActivationObject && !((ActivationObject)scope).isKnownAtCompileTime){
this.context.HandleError(JSError.AmbiguousBindingBecauseOfEval);
return;
}
scope = scope.GetParent();
}
}
private void WarnIfObsolete(){
Binding.WarnIfObsolete(this.member, this.context);
}
internal static void WarnIfObsolete(MemberInfo member, Context context){
if (member == null)
return;
String message = null;
bool isError = false;
Object[] custAttribs = member.GetCustomAttributes(Typeob.ObsoleteAttribute, false);
if (custAttribs != null && custAttribs.Length > 0){
ObsoleteAttribute attr = (ObsoleteAttribute)custAttribs[0];
message = attr.Message;
isError = attr.IsError;
}else{
custAttribs = member.GetCustomAttributes(Typeob.NotRecommended, false);
if (custAttribs != null && custAttribs.Length > 0){
NotRecommended attr = (NotRecommended)custAttribs[0];
message = ": " + attr.Message;
isError = false;
}else
return;
}
context.HandleError(JSError.Deprecated, message, isError);
}
}
}