The Labs \ Source Viewer \ SSCLI \ Microsoft.JScript \ FunctionScope

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. namespace Microsoft.JScript
  16. {
  17.    
  18.     using System;
  19.     using System.Reflection;
  20.     using System.Reflection.Emit;
  21.     using System.Collections;
  22.    
  23.     internal sealed class FunctionScope : ActivationObject
  24.     {
  25.         internal bool isMethod;
  26.         internal bool isStatic;
  27.         internal bool mustSaveStackLocals;
  28.         internal JSLocalField returnVar;
  29.         internal FunctionObject owner;
  30.         internal ArrayList nested_functions;
  31.         private ArrayList fields_for_nested_functions;
  32.         internal SimpleHashtable ProvidesOuterScopeLocals;
  33.         internal bool closuresMightEscape;
  34.        
  35.         internal FunctionScope(ScriptObject parent) : this(parent, false)
  36.         {
  37.         }
  38.        
  39.         internal FunctionScope(ScriptObject parent, bool isMethod) : base(parent)
  40.         {
  41.             this.isKnownAtCompileTime = true;
  42.             this.isMethod = isMethod;
  43.             this.mustSaveStackLocals = false;
  44.             if (parent != null && parent is ActivationObject)
  45.                 this.fast = ((ActivationObject)parent).fast;
  46.             else
  47.                 this.fast = false;
  48.             this.returnVar = null;
  49.             this.owner = null;
  50.             //Given its real value inside new FunctionObject
  51.             this.isStatic = false;
  52.             //Given its real value elsewhere
  53.             this.nested_functions = null;
  54.             this.fields_for_nested_functions = null;
  55.             if (parent is FunctionScope)
  56.                 this.ProvidesOuterScopeLocals = new SimpleHashtable(16);
  57.             else
  58.                 this.ProvidesOuterScopeLocals = null;
  59.             this.closuresMightEscape = false;
  60.         }
  61.        
  62.         internal JSVariableField AddNewField(string name, FieldAttributes attributeFlags, FunctionObject func)
  63.         {
  64.             if (this.nested_functions == null) {
  65.                 this.nested_functions = new ArrayList();
  66.                 this.fields_for_nested_functions = new ArrayList();
  67.             }
  68.             this.nested_functions.Add(func);
  69.             JSVariableField result = this.AddNewField(name, (object)func, attributeFlags);
  70.             this.fields_for_nested_functions.Add(result);
  71.             return result;
  72.         }
  73.        
  74.         protected override JSVariableField CreateField(string name, FieldAttributes attributeFlags, object value)
  75.         {
  76.             if ((attributeFlags & FieldAttributes.Static) != 0)
  77.                 return new JSGlobalField(this, name, value, attributeFlags);
  78.             else
  79.                 return new JSLocalField(name, this, this.field_table.Count, value);
  80.         }
  81.        
  82.         internal void AddOuterScopeField(string name, JSLocalField field)
  83.         {
  84.             this.name_table[name] = field;
  85.             this.field_table.Add(field);
  86.         }
  87.        
  88.         internal void AddReturnValueField()
  89.         {
  90.             if (this.name_table["return value"] != null)
  91.                 return;
  92.             this.returnVar = new JSLocalField("return value", this, this.field_table.Count, Missing.Value);
  93.             this.name_table["return value"] = this.returnVar;
  94.             this.field_table.Add(this.returnVar);
  95.         }
  96.        
  97.         internal void CloseNestedFunctions(StackFrame sf)
  98.         {
  99.             if (this.nested_functions == null)
  100.                 return;
  101.             IEnumerator funcs = this.nested_functions.GetEnumerator();
  102.             IEnumerator fields = this.fields_for_nested_functions.GetEnumerator();
  103.             while (funcs.MoveNext() && fields.MoveNext()) {
  104.                 FieldInfo field = (FieldInfo)fields.Current;
  105.                 FunctionObject func = (FunctionObject)funcs.Current;
  106.                 func.enclosing_scope = sf;
  107.                 field.SetValue(sf, new Closure(func));
  108.             }
  109.         }
  110.        
  111.         internal BitArray DefinedFlags {
  112.             get {
  113.                 int n = this.field_table.Count;
  114.                 BitArray result = new BitArray(n);
  115.                 for (int i = 0; i < n; i++) {
  116.                     JSLocalField field = (JSLocalField)this.field_table[i];
  117.                     if (field.isDefined)
  118.                         result[i] = true;
  119.                 }
  120.                 return result;
  121.             }
  122.             set {
  123.                 int n = value.Count;
  124.                 for (int i = 0; i < n; i++) {
  125.                     JSLocalField field = (JSLocalField)this.field_table[i];
  126.                     field.isDefined = value[i];
  127.                 }
  128.             }
  129.         }
  130.        
  131.         internal JSLocalField[] GetLocalFields()
  132.         {
  133.             int n = this.field_table.Count;
  134.             JSLocalField[] result = new JSLocalField[this.field_table.Count];
  135.             for (int i = 0; i < n; i++)
  136.                 result[i] = (JSLocalField)this.field_table[i];
  137.             return result;
  138.         }
  139.        
  140.         public override MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
  141.         {
  142.             FieldInfo field = (FieldInfo)(this.name_table[name]);
  143.             if (field != null)
  144.                 return new MemberInfo[] {field};
  145.             bool nestedInInstanceMethod = false;
  146.             ScriptObject parent = this.parent;
  147.             //Treat locals of outer functions as if they were locals of this function
  148.             while (parent is FunctionScope) {
  149.                 FunctionScope fscope = (FunctionScope)parent;
  150.                 nestedInInstanceMethod = fscope.isMethod && !fscope.isStatic;
  151.                 JSLocalField lfield = (JSLocalField)fscope.name_table[name];
  152.                 if (lfield == null) {
  153.                     parent = parent.GetParent();
  154.                     continue;
  155.                 }
  156.                 if (lfield.IsLiteral && !(lfield.value is FunctionObject))
  157.                     return new MemberInfo[] {lfield};
  158.                 JSLocalField f = new JSLocalField(lfield.Name, this, this.field_table.Count, Missing.Value);
  159.                 f.outerField = lfield;
  160.                 f.debugOn = lfield.debugOn;
  161.                 if (!f.debugOn && this.owner.funcContext.document.debugOn && fscope.owner.funcContext.document.debugOn) {
  162.                     //Check to see it is off because outer field is a parameter
  163.                     f.debugOn = Array.IndexOf(fscope.owner.formal_parameters, lfield.Name) >= 0;
  164.                 }
  165.                 f.isDefined = lfield.isDefined;
  166.                 f.debuggerName = "outer" + "." + f.Name;
  167.                 if (lfield.IsLiteral) {
  168.                     f.attributeFlags |= FieldAttributes.Literal;
  169.                     f.value = lfield.value;
  170.                 }
  171.                 this.AddOuterScopeField(name, f);
  172.                 if (this.ProvidesOuterScopeLocals[parent] == null)
  173.                     this.ProvidesOuterScopeLocals[parent] = parent;
  174.                 ((FunctionScope)parent).mustSaveStackLocals = true;
  175.                 return new MemberInfo[] {f};
  176.             }
  177.             if (parent is ClassScope && nestedInInstanceMethod) {
  178.                 //return class members as if they were local to the function. It is more convenient to wrap up instance members
  179.                 //at this stage than it is to figure out later that a special case is involved.
  180.                 MemberInfo[] members = parent.GetMember(name, bindingAttr & ~BindingFlags.DeclaredOnly);
  181.                 int n = members.Length;
  182.                 bool giveBadNews = false;
  183.                 for (int i = 0; i < n; i++) {
  184.                     MemberInfo member = members[i];
  185.                     switch (member.MemberType) {
  186.                         case MemberTypes.Field:
  187.                             field = (FieldInfo)member;
  188.                             if (field.IsLiteral) {
  189.                                 JSMemberField mfield = field as JSMemberField;
  190.                                 if (mfield != null && mfield.value is ClassScope && !((ClassScope)mfield.value).owner.IsStatic)
  191.                                     giveBadNews = true;
  192.                             }
  193.                             if (!field.IsStatic && !field.IsLiteral) {
  194.                                 members[i] = new JSClosureField(field);
  195.                                 giveBadNews = true;
  196.                             }
  197.                             break;
  198.                         case MemberTypes.Method:
  199.                             MethodInfo meth = (MethodInfo)member;
  200.                             if (!meth.IsStatic) {
  201.                                 members[i] = new JSClosureMethod(meth);
  202.                                 giveBadNews = true;
  203.                             }
  204.                             break;
  205.                         case MemberTypes.Property:
  206.                             PropertyInfo prop = (PropertyInfo)member;
  207.                             MethodInfo getMeth = JSProperty.GetGetMethod(prop, (bindingAttr & BindingFlags.NonPublic) != 0);
  208.                             MethodInfo setMeth = JSProperty.GetSetMethod(prop, (bindingAttr & BindingFlags.NonPublic) != 0);
  209.                             bool nonStatic = false;
  210.                             if (getMeth != null && !getMeth.IsStatic) {
  211.                                 nonStatic = true;
  212.                                 getMeth = new JSClosureMethod(getMeth);
  213.                             }
  214.                             if (setMeth != null && !setMeth.IsStatic) {
  215.                                 nonStatic = true;
  216.                                 setMeth = new JSClosureMethod(setMeth);
  217.                             }
  218.                             if (nonStatic) {
  219.                                 members[i] = new JSClosureProperty(prop, getMeth, setMeth);
  220.                                 giveBadNews = true;
  221.                             }
  222.                             break;
  223.                     }
  224.                 }
  225.                 if (giveBadNews)
  226.                     this.GiveOuterFunctionsTheBadNews();
  227.                 //They have to create explicit stack frames
  228.                 if (n > 0)
  229.                     return members;
  230.             }
  231.             if ((bindingAttr & BindingFlags.DeclaredOnly) != 0)
  232.                 return new MemberInfo[0];
  233.             return parent.GetMember(name, bindingAttr);
  234.         }
  235.        
  236.         internal override string GetName()
  237.         {
  238.             string parentName = null;
  239.             if (this.parent != null)
  240.                 parentName = ((ActivationObject)this.parent).GetName();
  241.             if (parentName != null)
  242.                 return parentName + "." + this.owner.name;
  243.             else
  244.                 return this.owner.name;
  245.         }
  246.        
  247.         internal int GetNextSlotNumber()
  248.         {
  249.             return this.field_table.Count;
  250.         }
  251.        
  252.         internal JSLocalField GetOuterLocalField(string name)
  253.         {
  254.             FieldInfo result = (FieldInfo)(this.name_table[name]);
  255.             if (result != null && result is JSLocalField && ((JSLocalField)result).outerField != null)
  256.                 return (JSLocalField)result;
  257.             else
  258.                 return null;
  259.         }
  260.        
  261.         private void GiveOuterFunctionsTheBadNews()
  262.         {
  263.             FunctionScope parent = (FunctionScope)this.parent;
  264.             parent.mustSaveStackLocals = true;
  265.             while (!parent.isMethod) {
  266.                 parent = (FunctionScope)parent.GetParent();
  267.                 parent.mustSaveStackLocals = true;
  268.             }
  269.         }
  270.        
  271.         internal void HandleUnitializedVariables()
  272.         {
  273.             for (int i = 0int n = this.field_table.Count; i < n; i++) {
  274.                 JSLocalField field = (JSLocalField)this.field_table[i];
  275.                 if (field.isUsedBeforeDefinition)
  276.                     field.SetInferredType(Typeob.Object, null);
  277.             }
  278.         }
  279.        
  280.         internal override void SetMemberValue(string name, object value)
  281.         {
  282.             FieldInfo field = (FieldInfo)(this.name_table[name]);
  283.             if (field != null)
  284.                 field.SetValue(this, value);
  285.             else
  286.                 this.parent.SetMemberValue(name, value);
  287.             //The above behaviour is inconsistent with the general idea of SetMemberValue, in as much as it does
  288.             //not create a new property on the target object. This is to simulate the JScript behavior of adding
  289.             //implicitly declared variables to the global scope, not the local scope.
  290.            
  291.             //WARNING: This means that the engine itself may not use SetMemberValue when it wishes to create a property
  292.             //in an activation object.
  293.         }
  294.        
  295.         internal void SetMemberValue(string name, object value, StackFrame sf)
  296.         {
  297.             FieldInfo field = (FieldInfo)(this.name_table[name]);
  298.             if (field != null)
  299.                 field.SetValue(sf, value);
  300.             else
  301.                 this.parent.SetMemberValue(name, value);
  302.         }
  303.     }
  304. }

Developer Fusion