mirror of
https://github.com/SSCLI/sscli_20021101
synced 2026-06-08 12:28:57 +00:00
9fa3874800
Moved the original file to the archive subfolder.
2115 lines
95 KiB
C#
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);
|
|
|
|
}
|
|
}
|
|
}
|