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.
678 lines
29 KiB
C#
678 lines
29 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 Microsoft.JScript.Vsa;
|
|
using System;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
|
|
internal sealed class Lookup : Binding{
|
|
private int lexLevel; //Keeps track of the number of frames on the explicit scope stack that needs to be searched for a late binding
|
|
private int evalLexLevel; //Keeps track of the number of frames to GetObject should skip
|
|
private LocalBuilder fieldLoc;
|
|
private LocalBuilder refLoc;
|
|
private LateBinding lateBinding;
|
|
private bool thereIsAnObjectOnTheStack;
|
|
|
|
// this is the typical Lookup constructor. The context points to the identifier
|
|
internal Lookup(Context context)
|
|
: base(context, context.GetCode()){
|
|
this.lexLevel = 0;
|
|
this.evalLexLevel = 0;
|
|
this.fieldLoc = null;
|
|
this.refLoc = null;
|
|
this.lateBinding = null;
|
|
this.thereIsAnObjectOnTheStack = false;
|
|
}
|
|
|
|
// this constructor is invoked when there has been a parse error. The typical scenario is a missing identifier.
|
|
internal Lookup(String name, Context context)
|
|
: this(context){
|
|
this.name = name;
|
|
}
|
|
|
|
internal String Name{
|
|
get{
|
|
return this.name;
|
|
}
|
|
}
|
|
|
|
private void BindName(){
|
|
int lexLevel = 0;
|
|
int evalLexLevel = 0;
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
BindingFlags flags = BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.Static|BindingFlags.DeclaredOnly;
|
|
while (scope != null){
|
|
MemberInfo[] members = this.members = scope.GetMember(this.name, flags);
|
|
if (members.Length > 0) break;
|
|
if (scope is WithObject){
|
|
this.isFullyResolved = this.isFullyResolved && ((WithObject)scope).isKnownAtCompileTime;
|
|
lexLevel++;
|
|
}else if (scope is ActivationObject){
|
|
this.isFullyResolved = this.isFullyResolved && ((ActivationObject)scope).isKnownAtCompileTime;
|
|
if (scope is BlockScope || (scope is FunctionScope && ((FunctionScope)scope).mustSaveStackLocals))
|
|
lexLevel++;
|
|
if (scope is ClassScope && ((ClassScope)scope).owner.isStatic)
|
|
flags &= ~BindingFlags.Instance;
|
|
}else if (scope is StackFrame)
|
|
lexLevel++;
|
|
evalLexLevel++;
|
|
scope = scope.GetParent();
|
|
}
|
|
if (this.members.Length > 0){
|
|
this.lexLevel = lexLevel;
|
|
this.evalLexLevel = evalLexLevel;
|
|
}
|
|
}
|
|
|
|
internal bool CanPlaceAppropriateObjectOnStack(Object ob){
|
|
if (ob is LenientGlobalObject) return true;
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
int lexLev = this.lexLevel;
|
|
while (lexLev > 0 && (scope is WithObject || scope is BlockScope)){
|
|
if (scope is WithObject) lexLev--;
|
|
scope = scope.GetParent();
|
|
}
|
|
return scope is WithObject || scope is GlobalScope;
|
|
}
|
|
|
|
internal override void CheckIfOKToUseInSuperConstructorCall(){
|
|
FieldInfo f = this.member as FieldInfo;
|
|
if (f != null){
|
|
if (!f.IsStatic)
|
|
this.context.HandleError(JSError.NotAllowedInSuperConstructorCall);
|
|
return;
|
|
}
|
|
MethodInfo m = this.member as MethodInfo;
|
|
if (m != null){
|
|
if (!m.IsStatic)
|
|
this.context.HandleError(JSError.NotAllowedInSuperConstructorCall);
|
|
return;
|
|
}
|
|
PropertyInfo p = this.member as PropertyInfo;
|
|
if (p != null){
|
|
m = JSProperty.GetGetMethod(p, true);
|
|
if (m != null && !m.IsStatic)
|
|
this.context.HandleError(JSError.NotAllowedInSuperConstructorCall);
|
|
else{
|
|
m = JSProperty.GetSetMethod(p, true);
|
|
if (m != null && !m.IsStatic)
|
|
this.context.HandleError(JSError.NotAllowedInSuperConstructorCall);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override Object Evaluate(){
|
|
Object result = null;
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
if (!this.isFullyResolved){
|
|
result = ((IActivationObject)scope).GetMemberValue(this.name, this.evalLexLevel);
|
|
if (!(result is Missing))
|
|
return result;
|
|
}
|
|
if (this.members == null && !VsaEngine.executeForJSEE){
|
|
this.BindName();
|
|
this.ResolveRHValue();
|
|
}
|
|
result = base.Evaluate();
|
|
if (result is Missing)
|
|
throw new JScriptException(JSError.UndefinedIdentifier, this.context);
|
|
return result;
|
|
}
|
|
|
|
internal override LateBinding EvaluateAsLateBinding(){
|
|
if (!this.isFullyResolved){
|
|
this.BindName(); //Doing this at runtime. Binding is now final (for this call). Do it here so that scope chain logic works as at compile time.
|
|
this.isFullyResolved = false;
|
|
}
|
|
if (this.defaultMember == this.member) this.defaultMember = null;
|
|
Object ob = this.GetObject();
|
|
LateBinding lb = this.lateBinding;
|
|
if (lb == null)
|
|
lb = this.lateBinding = new LateBinding(this.name, ob, VsaEngine.executeForJSEE);
|
|
lb.obj = ob;
|
|
lb.last_object = ob;
|
|
lb.last_members = this.members;
|
|
lb.last_member = this.member;
|
|
if (!this.isFullyResolved)
|
|
this.members = null;
|
|
return lb;
|
|
}
|
|
|
|
internal override WrappedNamespace EvaluateAsWrappedNamespace(bool giveErrorIfNameInUse){
|
|
Namespace ns = Namespace.GetNamespace(this.name, this.Engine);
|
|
GlobalScope scope = ((IActivationObject)Globals.ScopeStack.Peek()).GetGlobalScope();
|
|
FieldInfo field = giveErrorIfNameInUse ? scope.GetLocalField(this.name) : scope.GetField(this.name, BindingFlags.Public|BindingFlags.Static);
|
|
if (field != null){
|
|
if (giveErrorIfNameInUse && (!field.IsLiteral || !(field.GetValue(null) is Namespace)))
|
|
this.context.HandleError(JSError.DuplicateName, true);
|
|
}else{
|
|
field = scope.AddNewField(this.name, ns, FieldAttributes.Literal|FieldAttributes.Public);
|
|
((JSVariableField)field).type = new TypeExpression(new ConstantWrapper(typeof(Namespace), this.context));
|
|
((JSVariableField)field).originalContext = this.context;
|
|
}
|
|
return new WrappedNamespace(this.name, this.Engine);
|
|
}
|
|
|
|
protected override Object GetObject(){
|
|
Object result;
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
if (this.member is JSMemberField){
|
|
while (scope != null){
|
|
StackFrame sf = scope as StackFrame;
|
|
if (sf != null) {result = sf.closureInstance; goto finish;}
|
|
scope = scope.GetParent();
|
|
}
|
|
return null;
|
|
}
|
|
for (int i = this.evalLexLevel; i > 0; i--)
|
|
scope = scope.GetParent();
|
|
result = scope;
|
|
finish:
|
|
if (this.defaultMember != null)
|
|
switch(this.defaultMember.MemberType){
|
|
case MemberTypes.Field: return ((FieldInfo)this.defaultMember).GetValue(result);
|
|
case MemberTypes.Method: return ((MethodInfo)this.defaultMember).Invoke(result, new Object[0]);
|
|
case MemberTypes.Property: return ((PropertyInfo)this.defaultMember).GetValue(result, null);
|
|
case MemberTypes.Event: return null;
|
|
case MemberTypes.NestedType: return member;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected override void HandleNoSuchMemberError(){
|
|
if (!this.isFullyResolved) return;
|
|
this.context.HandleError(JSError.UndeclaredVariable, this.Engine.doFast);
|
|
}
|
|
|
|
internal override IReflect InferType(JSField inference_target){
|
|
if (!this.isFullyResolved)
|
|
return Typeob.Object;
|
|
return base.InferType(inference_target);
|
|
}
|
|
|
|
internal bool InFunctionNestedInsideInstanceMethod(){
|
|
ScriptObject scope = this.Globals.ScopeStack.Peek();
|
|
while (scope is WithObject || scope is BlockScope)
|
|
scope = scope.GetParent();
|
|
FunctionScope fscope = scope as FunctionScope;
|
|
while (fscope != null){
|
|
if (fscope.owner.isMethod) return !fscope.owner.isStatic;
|
|
scope = fscope.owner.enclosing_scope;
|
|
while (scope is WithObject || scope is BlockScope)
|
|
scope = scope.GetParent();
|
|
fscope = scope as FunctionScope;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal bool InStaticCode(){
|
|
ScriptObject scope = this.Globals.ScopeStack.Peek();
|
|
while (scope is WithObject || scope is BlockScope)
|
|
scope = scope.GetParent();
|
|
FunctionScope fscope = scope as FunctionScope;
|
|
if (fscope != null)
|
|
return fscope.isStatic;
|
|
StackFrame sframe = scope as StackFrame; //Might be a StackFrame if called from an eval
|
|
if (sframe != null)
|
|
return sframe.thisObject is Type;
|
|
ClassScope cscope = scope as ClassScope; //Happens when the initializers of variable declarations are partially evaluated
|
|
if (cscope != null)
|
|
return cscope.inStaticInitializerCode;
|
|
return true;
|
|
}
|
|
|
|
internal override AST PartiallyEvaluate(){
|
|
this.BindName();
|
|
if (this.members == null || this.members.Length == 0){
|
|
//Give a warning, unless inside a with statement
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
while (scope is FunctionScope) scope = scope.GetParent();
|
|
if (!(scope is WithObject) || this.isFullyResolved)
|
|
this.context.HandleError(JSError.UndeclaredVariable, this.isFullyResolved && this.Engine.doFast);
|
|
}else{
|
|
this.ResolveRHValue();
|
|
MemberInfo member = this.member;
|
|
if (member is FieldInfo){
|
|
FieldInfo field = (FieldInfo)member;
|
|
if (field is JSLocalField && !((JSLocalField)field).isDefined){
|
|
((JSLocalField)field).isUsedBeforeDefinition = true;
|
|
this.context.HandleError(JSError.VariableMightBeUnitialized);
|
|
}
|
|
if (field.IsLiteral){
|
|
Object val = field is JSVariableField ? ((JSVariableField)field).value : field.GetValue(null);
|
|
if (val is AST){
|
|
AST pval = ((AST)val).PartiallyEvaluate();
|
|
if (pval is ConstantWrapper && this.isFullyResolved)
|
|
return pval;
|
|
val = null;
|
|
}
|
|
if (!(val is FunctionObject) && this.isFullyResolved)
|
|
return new ConstantWrapper(val, this.context);
|
|
}else if (field.IsInitOnly && field.IsStatic && field.DeclaringType == Typeob.GlobalObject && this.isFullyResolved)
|
|
return new ConstantWrapper(field.GetValue(null), this.context);
|
|
}else if (member is PropertyInfo){
|
|
PropertyInfo prop = (PropertyInfo)member;
|
|
if (!prop.CanWrite && !(prop is JSProperty) && prop.DeclaringType == Typeob.GlobalObject && this.isFullyResolved)
|
|
return new ConstantWrapper(prop.GetValue(null, null), this.context);
|
|
}
|
|
if (member is Type && this.isFullyResolved)
|
|
return new ConstantWrapper(member, this.context);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
internal override AST PartiallyEvaluateAsCallable(){
|
|
this.BindName();
|
|
return this;
|
|
}
|
|
|
|
internal override AST PartiallyEvaluateAsReference(){
|
|
this.BindName();
|
|
if (this.members == null || this.members.Length == 0){
|
|
//Give a warning, unless inside a with statement
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
if (!(scope is WithObject) || this.isFullyResolved)
|
|
this.context.HandleError(JSError.UndeclaredVariable, this.isFullyResolved && this.Engine.doFast);
|
|
}else
|
|
this.ResolveLHValue();
|
|
return this;
|
|
}
|
|
|
|
|
|
internal override Object ResolveCustomAttribute(ASTList args, IReflect[] argIRs, AST target){
|
|
if (this.name == "expando")
|
|
this.members = Typeob.Expando.GetConstructors(BindingFlags.Instance|BindingFlags.Public);
|
|
else if (this.name == "override")
|
|
this.members = typeof(Microsoft.JScript.Override).GetConstructors(BindingFlags.Instance|BindingFlags.Public);
|
|
else if (this.name == "hide")
|
|
this.members = typeof(Microsoft.JScript.Hide).GetConstructors(BindingFlags.Instance|BindingFlags.Public);
|
|
else if (this.name == "...")
|
|
this.members = typeof(System.ParamArrayAttribute).GetConstructors(BindingFlags.Instance|BindingFlags.Public);
|
|
else{
|
|
this.name = this.name + "Attribute";
|
|
this.BindName();
|
|
if (this.members == null || this.members.Length == 0){
|
|
this.name = this.name.Substring(0, this.name.Length-9);
|
|
this.BindName();
|
|
}
|
|
}
|
|
return base.ResolveCustomAttribute(args, argIRs, target);
|
|
}
|
|
|
|
internal override void SetPartialValue(AST partial_value){
|
|
//ResolveLHValue has already been called and has already checked if the target is accessible and assignable
|
|
if (this.members == null || this.members.Length == 0) return; //Assignment to an undeclared variable. Nothing further to do.
|
|
if (this.member is JSLocalField){
|
|
//If we are dealing with an assignment to an untyped local variable, we need to tell the type inferencer about the assignment
|
|
JSLocalField lfield = (JSLocalField)this.member;
|
|
if (lfield.type == null){
|
|
IReflect ir = partial_value.InferType(lfield);
|
|
if (ir == Typeob.String && partial_value is Plus)
|
|
lfield.SetInferredType(Typeob.Object, partial_value);
|
|
else
|
|
lfield.SetInferredType(ir, partial_value);
|
|
//but then we are done
|
|
return;
|
|
}
|
|
lfield.isDefined = true;
|
|
}
|
|
Binding.AssignmentCompatible(this.InferType(null), partial_value, partial_value.InferType(null), this.isFullyResolved);
|
|
}
|
|
|
|
internal override void SetValue(Object value){
|
|
if (!this.isFullyResolved){
|
|
this.EvaluateAsLateBinding().SetValue(value);
|
|
return;
|
|
}
|
|
base.SetValue(value);
|
|
}
|
|
|
|
internal void SetWithValue(WithObject scope, Object value){
|
|
Debug.Assert(!this.isFullyResolved);
|
|
FieldInfo withField = scope.GetField(this.name, this.lexLevel);
|
|
if (withField != null){
|
|
withField.SetValue(scope, value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//code in parser relies on this.name being returned from here
|
|
public override String ToString(){
|
|
return this.name;
|
|
}
|
|
|
|
internal override void TranslateToIL(ILGenerator il, Type rtype){
|
|
if (this.isFullyResolved){
|
|
base.TranslateToIL(il, rtype);
|
|
return;
|
|
}
|
|
//Need to do a dynamic lookup to see if there is a dynamically introduced (by eval or with) hiding definition.
|
|
Label done = il.DefineLabel();
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
il.Emit(OpCodes.Castclass, typeof(IActivationObject));
|
|
il.Emit(OpCodes.Ldstr, this.name);
|
|
ConstantWrapper.TranslateToILInt(il, this.lexLevel);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.getMemberValueMethod);
|
|
il.Emit(OpCodes.Dup);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.isMissingMethod);
|
|
il.Emit(OpCodes.Brfalse, done); //dynamic lookup succeeded, do not use the value from the early bound location.
|
|
il.Emit(OpCodes.Pop); //The dynamic lookup returned Missing.Value, discard it and go on to use the value from the early bound location.
|
|
base.TranslateToIL(il, Typeob.Object);
|
|
il.MarkLabel(done);
|
|
Convert.Emit(this, il, Typeob.Object, rtype);
|
|
}
|
|
|
|
internal override void TranslateToILCall(ILGenerator il, Type rtype, ASTList argList, bool construct, bool brackets){
|
|
if (this.isFullyResolved){
|
|
base.TranslateToILCall(il, rtype, argList, construct, brackets);
|
|
return;
|
|
}
|
|
//Need to do a dynamic lookup to see if there is a dynamically introduced (by eval or with) hiding definition.
|
|
Label lateBoundCall = il.DefineLabel();
|
|
Label done = il.DefineLabel();
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
il.Emit(OpCodes.Castclass, typeof(IActivationObject));
|
|
il.Emit(OpCodes.Ldstr, this.name);
|
|
ConstantWrapper.TranslateToILInt(il, this.lexLevel);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.getMemberValueMethod);
|
|
il.Emit(OpCodes.Dup);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.isMissingMethod);
|
|
il.Emit(OpCodes.Brfalse, lateBoundCall); //dynamic lookup succeeded, do not call early bound method
|
|
il.Emit(OpCodes.Pop); //The dynamic lookup returned Missing.Value, discard it and go on to use the value from the early bound location.
|
|
base.TranslateToILCall(il, Typeob.Object, argList, construct, brackets);
|
|
il.Emit(OpCodes.Br, done); //Skip over the latebound call sequence
|
|
il.MarkLabel(lateBoundCall);
|
|
this.TranslateToILDefaultThisObject(il);
|
|
argList.TranslateToIL(il, typeof(Object[]));
|
|
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.callValue2Method);
|
|
il.MarkLabel(done);
|
|
Convert.Emit(this, il, Typeob.Object, rtype);
|
|
}
|
|
|
|
internal void TranslateToILDefaultThisObject(ILGenerator il){
|
|
this.TranslateToILDefaultThisObject(il, 0);
|
|
}
|
|
|
|
private void TranslateToILDefaultThisObject(ILGenerator il, int lexLevel){
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
while (lexLevel-- > 0)
|
|
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
|
|
il.Emit(OpCodes.Castclass, CompilerGlobals.iActivationObjectClass);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.getDefaultThisObjectMethod);
|
|
}
|
|
|
|
internal override void TranslateToILInitializer(ILGenerator il){
|
|
if (this.defaultMember != null) return;
|
|
if (this.member != null)
|
|
switch(this.member.MemberType){
|
|
case MemberTypes.Constructor:
|
|
case MemberTypes.Method:
|
|
case MemberTypes.NestedType:
|
|
case MemberTypes.Property:
|
|
case MemberTypes.TypeInfo: return;
|
|
case MemberTypes.Field:
|
|
if (this.member is JSExpandoField){
|
|
this.member = null; break;
|
|
}
|
|
return;
|
|
}
|
|
this.refLoc = il.DeclareLocal(typeof(LateBinding));
|
|
il.Emit(OpCodes.Ldstr, this.name);
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
il.Emit(OpCodes.Newobj, CompilerGlobals.lateBindingConstructor2);
|
|
il.Emit(OpCodes.Stloc, this.refLoc);
|
|
}
|
|
|
|
protected override void TranslateToILObject(ILGenerator il, Type obType, bool noValue){
|
|
this.thereIsAnObjectOnTheStack = true;
|
|
if (this.member is IWrappedMember){
|
|
Object obj = ((IWrappedMember)this.member).GetWrappedObject();
|
|
if (obj is LenientGlobalObject){
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.getLenientGlobalObjectMethod);
|
|
}else if (obj is Type || obj is ClassScope){
|
|
if (obType.IsAssignableFrom(Typeob.Type)){
|
|
(new ConstantWrapper(obj, null)).TranslateToIL(il, Typeob.Type);
|
|
return;
|
|
}
|
|
//this.name is the name of an instance member of the superclass of the class (or an outer class) in which this expression appears
|
|
//get class instance on stack
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
while (scope is WithObject || scope is BlockScope)
|
|
scope = scope.GetParent();
|
|
if (scope is FunctionScope){
|
|
FunctionObject func = ((FunctionScope)scope).owner;
|
|
if (func.isMethod)
|
|
il.Emit(OpCodes.Ldarg_0);
|
|
else{ //Need to get the StackFrame.closureInstance on the stack
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
scope = this.Globals.ScopeStack.Peek();
|
|
while (scope is WithObject || scope is BlockScope){
|
|
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
|
|
scope = scope.GetParent();
|
|
}
|
|
il.Emit(OpCodes.Castclass, CompilerGlobals.stackFrameClass);
|
|
il.Emit(OpCodes.Ldfld, CompilerGlobals.closureInstanceField);
|
|
}
|
|
}else if (scope is ClassScope){
|
|
il.Emit(OpCodes.Ldarg_0); //Inside instance initializer
|
|
}
|
|
|
|
//If dealing with a member of an outer class, get the outer class instance on the stack
|
|
scope = Globals.ScopeStack.Peek();
|
|
while (scope != null){
|
|
ClassScope csc = scope as ClassScope;
|
|
if (csc != null){ //Might be dealing with a reference from within a nested class to an instance member of an outer class
|
|
if (csc.IsSameOrDerivedFrom(obType)) break;
|
|
il.Emit(OpCodes.Ldfld, csc.outerClassField);
|
|
}
|
|
scope = scope.GetParent();
|
|
}
|
|
}else{
|
|
this.TranslateToILDefaultThisObject(il, this.lexLevel);
|
|
Convert.Emit(this, il, Typeob.Object, obType);
|
|
}
|
|
}else{
|
|
ScriptObject scope = Globals.ScopeStack.Peek();
|
|
while (scope is WithObject || scope is BlockScope)
|
|
scope = scope.GetParent();
|
|
if (scope is FunctionScope){
|
|
FunctionObject func = ((FunctionScope)scope).owner;
|
|
if (!func.isMethod){ //Need to get the StackFrame.closureInstance on the stack
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
scope = this.Globals.ScopeStack.Peek();
|
|
while (scope is WithObject || scope is BlockScope){
|
|
il.Emit(OpCodes.Call, CompilerGlobals.getParentMethod);
|
|
scope = scope.GetParent();
|
|
}
|
|
il.Emit(OpCodes.Castclass, CompilerGlobals.stackFrameClass);
|
|
il.Emit(OpCodes.Ldfld, CompilerGlobals.closureInstanceField);
|
|
while (scope != null){
|
|
if (scope is ClassScope){ //Might be dealing with a reference from within a nested class to an instance member of an outer class
|
|
ClassScope csc = (ClassScope)scope;
|
|
if (csc.IsSameOrDerivedFrom(obType)) break;
|
|
il.Emit(OpCodes.Castclass, csc.GetTypeBuilder());
|
|
il.Emit(OpCodes.Ldfld, csc.outerClassField);
|
|
}
|
|
scope = scope.GetParent();
|
|
}
|
|
il.Emit(OpCodes.Castclass, obType);
|
|
return;
|
|
}
|
|
}
|
|
il.Emit(OpCodes.Ldarg_0);
|
|
while (scope != null){
|
|
if (scope is ClassScope){ //Might be dealing with a reference from within a nested class to an instance member of an outer class
|
|
ClassScope csc = (ClassScope)scope;
|
|
if (csc.IsSameOrDerivedFrom(obType)) break;
|
|
il.Emit(OpCodes.Ldfld, csc.outerClassField);
|
|
}
|
|
scope = scope.GetParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void TranslateToILPreSet(ILGenerator il){
|
|
this.TranslateToILPreSet(il, false);
|
|
}
|
|
|
|
internal void TranslateToILPreSet(ILGenerator il, bool doBoth){
|
|
if (this.isFullyResolved){
|
|
base.TranslateToILPreSet(il);
|
|
return;
|
|
}
|
|
//Need to do a dynamic lookup to see if there is a dynamically introduced (by eval or with) hiding definition.
|
|
//Leave a FieldInfo on the stack if there is such a member, otherwise leave null on the stack
|
|
Label done = il.DefineLabel();
|
|
LocalBuilder fieldLoc = this.fieldLoc = il.DeclareLocal(typeof(FieldInfo));
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
il.Emit(OpCodes.Castclass, typeof(IActivationObject));
|
|
il.Emit(OpCodes.Ldstr, this.name);
|
|
ConstantWrapper.TranslateToILInt(il, this.lexLevel);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.getFieldMethod);
|
|
il.Emit(OpCodes.Stloc, fieldLoc);
|
|
if (!doBoth){
|
|
il.Emit(OpCodes.Ldloc, fieldLoc);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.Emit(OpCodes.Bne_Un_S, done);
|
|
}
|
|
base.TranslateToILPreSet(il);
|
|
if (this.thereIsAnObjectOnTheStack){
|
|
Label reallyDone = il.DefineLabel();
|
|
il.Emit(OpCodes.Br_S, reallyDone);
|
|
il.MarkLabel(done);
|
|
il.Emit(OpCodes.Ldnull); //Push a dummy object so that both latebound and early bound code paths leave an object on the stack
|
|
il.MarkLabel(reallyDone);
|
|
}else
|
|
il.MarkLabel(done);
|
|
}
|
|
|
|
internal override void TranslateToILPreSetPlusGet(ILGenerator il){
|
|
if (this.isFullyResolved){
|
|
base.TranslateToILPreSetPlusGet(il);
|
|
return;
|
|
}
|
|
//Need to do a dynamic lookup to see if there is a dynamically introduced (by eval or with) hiding definition.
|
|
//Leave a FieldInfo on the stack if there is such a member, otherwise leave null on the stack
|
|
Label done = il.DefineLabel();
|
|
Label latebound = il.DefineLabel();
|
|
LocalBuilder fieldTok = this.fieldLoc = il.DeclareLocal(typeof(FieldInfo));
|
|
this.EmitILToLoadEngine(il);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.scriptObjectStackTopMethod);
|
|
il.Emit(OpCodes.Castclass, typeof(IActivationObject));
|
|
il.Emit(OpCodes.Ldstr, this.name);
|
|
ConstantWrapper.TranslateToILInt(il, this.lexLevel);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.getFieldMethod);
|
|
il.Emit(OpCodes.Stloc, fieldTok);
|
|
il.Emit(OpCodes.Ldloc, fieldTok);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.Emit(OpCodes.Bne_Un_S, latebound);
|
|
base.TranslateToILPreSetPlusGet(il);
|
|
il.Emit(OpCodes.Br_S, done);
|
|
il.MarkLabel(latebound);
|
|
if (this.thereIsAnObjectOnTheStack)
|
|
il.Emit(OpCodes.Ldnull); //Push a dummy object so that both latebound and early bound code paths leave an object on the stack
|
|
il.Emit(OpCodes.Ldloc, this.fieldLoc);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.getFieldValueMethod);
|
|
il.MarkLabel(done);
|
|
}
|
|
|
|
internal override void TranslateToILSet(ILGenerator il, AST rhvalue){
|
|
this.TranslateToILSet(il, false, rhvalue);
|
|
}
|
|
|
|
internal void TranslateToILSet(ILGenerator il, bool doBoth, AST rhvalue){
|
|
if (this.isFullyResolved){
|
|
base.TranslateToILSet(il, rhvalue);
|
|
return;
|
|
}
|
|
if (rhvalue != null)
|
|
rhvalue.TranslateToIL(il, Typeob.Object);
|
|
if (this.fieldLoc == null){
|
|
//There is a callable value plus parameters on the stack
|
|
il.Emit(OpCodes.Call, CompilerGlobals.setIndexedPropertyValueStaticMethod);
|
|
return;
|
|
}
|
|
LocalBuilder temp = il.DeclareLocal(Typeob.Object);
|
|
if (doBoth){
|
|
//save copy of rh value
|
|
il.Emit(OpCodes.Dup);
|
|
il.Emit(OpCodes.Stloc, temp);
|
|
//store it in early bound location
|
|
this.isFullyResolved = true;
|
|
Convert.Emit(this, il, Typeob.Object, Convert.ToType(this.InferType(null)));
|
|
base.TranslateToILSet(il, null);
|
|
}
|
|
//See if there is a late bound field
|
|
Label earlyBound = il.DefineLabel();
|
|
il.Emit(OpCodes.Ldloc, this.fieldLoc);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.Emit(OpCodes.Beq_S, earlyBound); //No late bound field
|
|
|
|
//store it in the late bound field
|
|
Label done = il.DefineLabel();
|
|
if (!doBoth){
|
|
il.Emit(OpCodes.Stloc, temp);
|
|
if (this.thereIsAnObjectOnTheStack)
|
|
il.Emit(OpCodes.Pop);
|
|
}
|
|
il.Emit(OpCodes.Ldloc, this.fieldLoc);
|
|
il.Emit(OpCodes.Ldnull);
|
|
il.Emit(OpCodes.Ldloc, temp);
|
|
il.Emit(OpCodes.Callvirt, CompilerGlobals.setFieldValueMethod);
|
|
il.Emit(OpCodes.Br_S, done);
|
|
|
|
//Alternative store it in the early bound location
|
|
il.MarkLabel(earlyBound);
|
|
if (!doBoth){
|
|
this.isFullyResolved = true;
|
|
Convert.Emit(this, il, Typeob.Object, Convert.ToType(this.InferType(null)));
|
|
base.TranslateToILSet(il, null);
|
|
}
|
|
il.MarkLabel(done);
|
|
}
|
|
|
|
protected override void TranslateToILWithDupOfThisOb(ILGenerator il){
|
|
this.TranslateToILDefaultThisObject(il);
|
|
this.TranslateToIL(il, Typeob.Object);
|
|
}
|
|
|
|
internal void TranslateToLateBinding(ILGenerator il){
|
|
this.thereIsAnObjectOnTheStack = true;
|
|
il.Emit(OpCodes.Ldloc, this.refLoc);
|
|
}
|
|
}
|
|
}
|