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

  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.Collections;
  20.     using System.Diagnostics;
  21.     using System.Globalization;
  22.     using System.Reflection;
  23.     using System.Runtime.InteropServices.Expando;
  24.    
  25.     public class JSObject : ScriptObject, IEnumerable, IExpando
  26.     {
  27.         private bool isASubClass;
  28.         private IReflect subClassIR;
  29.         private SimpleHashtable memberCache;
  30.         internal bool noExpando;
  31.         internal SimpleHashtable name_table;
  32.         protected ArrayList field_table;
  33.         internal JSObject outer_class_instance;
  34.        
  35.         public JSObject() : this(null, false)
  36.         {
  37.             this.noExpando = false;
  38.         }
  39.        
  40.         internal JSObject(ScriptObject parent) : this(parent, true)
  41.         {
  42.         }
  43.        
  44.         internal JSObject(ScriptObject parent, bool checkSubType) : base(parent)
  45.         {
  46.             this.memberCache = null;
  47.             this.isASubClass = false;
  48.             this.subClassIR = null;
  49.             if (checkSubType) {
  50.                 Type subType = Globals.TypeRefs.ToReferenceContext(this.GetType());
  51.                 Debug.Assert(subType != Typeob.BuiltinFunction);
  52.                 if (subType != Typeob.JSObject) {
  53.                     this.isASubClass = true;
  54.                     this.subClassIR = TypeReflector.GetTypeReflectorFor(subType);
  55.                 }
  56.             }
  57.             else
  58.                 Debug.Assert(Globals.TypeRefs.ToReferenceContext(this.GetType()) == Typeob.JSObject);
  59.             this.noExpando = this.isASubClass;
  60.             this.name_table = null;
  61.             this.field_table = null;
  62.             this.outer_class_instance = null;
  63.         }
  64.        
  65.         internal JSObject(ScriptObject parent, Type subType) : base(parent)
  66.         {
  67.             this.memberCache = null;
  68.             this.isASubClass = false;
  69.             this.subClassIR = null;
  70.             Debug.Assert(subType == this.GetType() || this.GetType() == typeof(BuiltinFunction));
  71.            
  72.             subType = Globals.TypeRefs.ToReferenceContext(subType);
  73.             if (subType != Typeob.JSObject) {
  74.                 this.isASubClass = true;
  75.                 this.subClassIR = TypeReflector.GetTypeReflectorFor(subType);
  76.             }
  77.             this.noExpando = this.isASubClass;
  78.             this.name_table = null;
  79.             this.field_table = null;
  80.         }
  81.        
  82.         public FieldInfo AddField(string name)
  83.         {
  84.             if (this.noExpando)
  85.                 return null;
  86.             FieldInfo field = (FieldInfo)this.NameTable[name];
  87.             if (field == null) {
  88.                 field = new JSExpandoField(name);
  89.                 this.name_table[name] = field;
  90.                 this.field_table.Add(field);
  91.             }
  92.             return field;
  93.         }
  94.        
  95.         MethodInfo IExpando.AddMethod(string name, Delegate method)
  96.         {
  97.             return null;
  98.         }
  99.        
  100.         PropertyInfo IExpando.AddProperty(string name)
  101.         {
  102.             return null;
  103.         }
  104.        
  105.         internal override bool DeleteMember(string name)
  106.         {
  107.             FieldInfo field = (FieldInfo)this.NameTable[name];
  108.             if (field != null) {
  109.                 if (field is JSExpandoField) {
  110.                     field.SetValue(this, Missing.Value);
  111.                     this.name_table.Remove(name);
  112.                     this.field_table.Remove(field);
  113.                     return true;
  114.                 }
  115.                 else if (field is JSPrototypeField) {
  116.                     field.SetValue(this, Missing.Value);
  117.                     return true;
  118.                 }
  119.                 else
  120.                     return false;
  121.             }
  122.             else if (this.parent != null)
  123.                 return LateBinding.DeleteMember(this.parent, name);
  124.             else
  125.                 return false;
  126.         }
  127.        
  128.         internal virtual string GetClassName()
  129.         {
  130.             return "Object";
  131.         }
  132.        
  133.         #if !DEBUG
  134.         [DebuggerStepThroughAttribute()]
  135.         [DebuggerHiddenAttribute()]
  136.         #endif
  137.         internal override object GetDefaultValue(PreferredType preferred_type)
  138.         {
  139.             if (preferred_type == PreferredType.String) {
  140.                 ScriptFunction toString = this.GetMemberValue("toString") as ScriptFunction;
  141.                 if (toString != null) {
  142.                     object result = toString.Call(new object[0], this);
  143.                     if (result == null)
  144.                         return result;
  145.                     IConvertible ic = Convert.GetIConvertible(result);
  146.                     if (ic != null && ic.GetTypeCode() != TypeCode.Object)
  147.                         return result;
  148.                 }
  149.                 ScriptFunction valueOf = this.GetMemberValue("valueOf") as ScriptFunction;
  150.                 if (valueOf != null) {
  151.                     object result = valueOf.Call(new object[0], this);
  152.                     if (result == null)
  153.                         return result;
  154.                     IConvertible ic = Convert.GetIConvertible(result);
  155.                     if (ic != null && ic.GetTypeCode() != TypeCode.Object)
  156.                         return result;
  157.                 }
  158.             }
  159.             else if (preferred_type == PreferredType.LocaleString) {
  160.                 ScriptFunction toLocaleString = this.GetMemberValue("toLocaleString") as ScriptFunction;
  161.                 if (toLocaleString != null) {
  162.                     return toLocaleString.Call(new object[0], this);
  163.                 }
  164.             }
  165.             else {
  166.                 if (preferred_type == PreferredType.Either && this is DateObject)
  167.                     return this.GetDefaultValue(PreferredType.String);
  168.                 ScriptFunction valueOf = this.GetMemberValue("valueOf") as ScriptFunction;
  169.                 if (valueOf != null) {
  170.                     object result = valueOf.Call(new object[0], this);
  171.                     if (result == null)
  172.                         return result;
  173.                     IConvertible ic = Convert.GetIConvertible(result);
  174.                     if (ic != null && ic.GetTypeCode() != TypeCode.Object)
  175.                         return result;
  176.                 }
  177.                 ScriptFunction toString = this.GetMemberValue("toString") as ScriptFunction;
  178.                 if (toString != null) {
  179.                     object result = toString.Call(new object[0], this);
  180.                     if (result == null)
  181.                         return result;
  182.                     IConvertible ic = Convert.GetIConvertible(result);
  183.                     if (ic != null && ic.GetTypeCode() != TypeCode.Object)
  184.                         return result;
  185.                 }
  186.             }
  187.             return this;
  188.         }
  189.        
  190.         IEnumerator IEnumerable.GetEnumerator()
  191.         {
  192.             return ForIn.JScriptGetEnumerator(this);
  193.         }
  194.        
  195.         private static bool IsHiddenMember(MemberInfo mem)
  196.         {
  197.             // Members that are declared in super classes of JSObject are hidden except for those
  198.             // in Object.
  199.             Type mtype = mem.DeclaringType;
  200.             if (mtype == Typeob.JSObject || mtype == Typeob.ScriptObject || (mtype == Typeob.ArrayWrapper && mem.Name != "length"))
  201.                 return true;
  202.             return false;
  203.         }
  204.        
  205.         private MemberInfo[] GetLocalMember(string name, BindingFlags bindingAttr, bool wrapMembers)
  206.         {
  207.             MemberInfo[] result = null;
  208.             FieldInfo field = this.name_table == null ? null : (FieldInfo)this.name_table[name];
  209.             if (field == null && this.isASubClass) {
  210.                 if (this.memberCache != null) {
  211.                     result = (MemberInfo[])this.memberCache[name];
  212.                     if (result != null)
  213.                         return result;
  214.                 }
  215.                 bindingAttr &= ~BindingFlags.NonPublic;
  216.                 //Never expose non public fields of old style objects
  217.                 result = this.subClassIR.GetMember(name, bindingAttr);
  218.                 if (result.Length == 0)
  219.                     result = this.subClassIR.GetMember(name, (bindingAttr & ~BindingFlags.Instance) | BindingFlags.Static);
  220.                 int n = result.Length;
  221.                 if (n > 0) {
  222.                     //Suppress any members that are declared in JSObject or earlier. But keep the ones in Object.
  223.                     int hiddenMembers = 0;
  224.                     foreach (MemberInfo mem in result) {
  225.                         if (JSObject.IsHiddenMember(mem))
  226.                             hiddenMembers++;
  227.                     }
  228.                     if (hiddenMembers > 0 && !(n == 1 && this is ObjectPrototype && name == "ToString")) {
  229.                         MemberInfo[] newResult = new MemberInfo[n - hiddenMembers];
  230.                         int j = 0;
  231.                         foreach (MemberInfo mem in result) {
  232.                             if (!JSObject.IsHiddenMember(mem))
  233.                                 newResult[j++] = mem;
  234.                         }
  235.                         result = newResult;
  236.                     }
  237.                 }
  238.                 if ((result == null || result.Length == 0) && (bindingAttr & BindingFlags.Public) != 0 && (bindingAttr & BindingFlags.Instance) != 0) {
  239.                     BindingFlags flags = (bindingAttr & BindingFlags.IgnoreCase) | BindingFlags.Public | BindingFlags.Instance;
  240.                     if (this is StringObject)
  241.                         result = TypeReflector.GetTypeReflectorFor(Typeob.String).GetMember(name, flags);
  242.                     else if (this is NumberObject)
  243.                         result = TypeReflector.GetTypeReflectorFor(((NumberObject)this).baseType).GetMember(name, flags);
  244.                     else if (this is BooleanObject)
  245.                         result = TypeReflector.GetTypeReflectorFor(Typeob.Boolean).GetMember(name, flags);
  246.                     else if (this is StringConstructor)
  247.                         result = TypeReflector.GetTypeReflectorFor(Typeob.String).GetMember(name, (flags | BindingFlags.Static) & ~BindingFlags.Instance);
  248.                     else if (this is BooleanConstructor)
  249.                         result = TypeReflector.GetTypeReflectorFor(Typeob.Boolean).GetMember(name, (flags | BindingFlags.Static) & ~BindingFlags.Instance);
  250.                     else if (this is ArrayWrapper)
  251.                         result = TypeReflector.GetTypeReflectorFor(Typeob.Array).GetMember(name, flags);
  252.                 }
  253.                 if (result != null && result.Length > 0) {
  254.                     if (wrapMembers)
  255.                         result = ScriptObject.WrapMembers(result, this);
  256.                     if (this.memberCache == null)
  257.                         this.memberCache = new SimpleHashtable(32);
  258.                     this.memberCache[name] = result;
  259.                     return result;
  260.                 }
  261.             }
  262.             if ((bindingAttr & BindingFlags.IgnoreCase) != 0 && (result == null || result.Length == 0)) {
  263.                 result = null;
  264.                 IDictionaryEnumerator e = this.name_table.GetEnumerator();
  265.                 while (e.MoveNext()) {
  266.                     if (String.Compare(e.Key.ToString(), name, StringComparison.OrdinalIgnoreCase) == 0) {
  267.                         field = (FieldInfo)e.Value;
  268.                         break;
  269.                     }
  270.                 }
  271.             }
  272.             if (field != null)
  273.                 return new MemberInfo[] {field};
  274.             if (result == null)
  275.                 result = new MemberInfo[0];
  276.             return result;
  277.         }
  278.        
  279.         public override MemberInfo[] GetMember(string name, BindingFlags bindingAttr)
  280.         {
  281.             return this.GetMember(name, bindingAttr, false);
  282.         }
  283.        
  284.         private MemberInfo[] GetMember(string name, BindingFlags bindingAttr, bool wrapMembers)
  285.         {
  286.             MemberInfo[] members = this.GetLocalMember(name, bindingAttr, wrapMembers);
  287.             if (members.Length > 0)
  288.                 return members;
  289.             if (this.parent != null) {
  290.                 if (this.parent is JSObject) {
  291.                     members = ((JSObject)this.parent).GetMember(name, bindingAttr, true);
  292.                     wrapMembers = false;
  293.                 }
  294.                 else
  295.                     members = this.parent.GetMember(name, bindingAttr);
  296.                 foreach (MemberInfo mem in members) {
  297.                     if (mem.MemberType == MemberTypes.Field) {
  298.                         FieldInfo field = (FieldInfo)mem;
  299.                         JSMemberField mfield = mem as JSMemberField;
  300.                         if (mfield != null) {
  301.                             //This can only happen when running in the Evaluator
  302.                             if (!mfield.IsStatic) {
  303.                                 JSGlobalField gfield = new JSGlobalField(this, name, mfield.value, FieldAttributes.Public);
  304.                                 this.NameTable[name] = gfield;
  305.                                 this.field_table.Add(gfield);
  306.                                 field = mfield;
  307.                             }
  308.                         }
  309.                         else {
  310.                             field = new JSPrototypeField(this.parent, (FieldInfo)mem);
  311.                             if (!this.noExpando) {
  312.                                 this.NameTable[name] = field;
  313.                                 this.field_table.Add(field);
  314.                             }
  315.                         }
  316.                         return new MemberInfo[] {field};
  317.                     }
  318.                     if (!this.noExpando) {
  319.                         if (mem.MemberType == MemberTypes.Method) {
  320.                             FieldInfo field = new JSPrototypeField(this.parent, new JSGlobalField(this, name, LateBinding.GetMemberValue(this.parent, name, null, members), FieldAttributes.Public | FieldAttributes.InitOnly));
  321.                             this.NameTable[name] = field;
  322.                             this.field_table.Add(field);
  323.                             return new MemberInfo[] {field};
  324.                         }
  325.                     }
  326.                 }
  327.                 if (wrapMembers)
  328.                     return ScriptObject.WrapMembers(members, this.parent);
  329.                 else
  330.                     return members;
  331.             }
  332.             return new MemberInfo[0];
  333.         }
  334.        
  335.         public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
  336.         {
  337.             MemberInfoList mems = new MemberInfoList();
  338.             SimpleHashtable uniqueMems = new SimpleHashtable(32);
  339.            
  340.             if (!this.noExpando && this.field_table != null) {
  341.                 //Add any expando properties
  342.                 IEnumerator enu = this.field_table.GetEnumerator();
  343.                 while (enu.MoveNext()) {
  344.                     FieldInfo field = (FieldInfo)enu.Current;
  345.                     mems.Add(field);
  346.                     uniqueMems[field.Name] = field;
  347.                 }
  348.             }
  349.            
  350.             //Add the public members of the built-in objects if they don't already exist
  351.             if (this.isASubClass) {
  352.                 MemberInfo[] ilMembers = this.GetType().GetMembers(bindingAttr & ~BindingFlags.NonPublic);
  353.                 //Never expose non public members of old style objects
  354.                 for (int i = 0int n = ilMembers.Length; i < n; i++) {
  355.                     MemberInfo ilMem = ilMembers[i];
  356.                    
  357.                     //Hide any infrastructure stuff in JSObject
  358.                     if (!ilMem.DeclaringType.IsAssignableFrom(Typeob.JSObject) && uniqueMems[ilMem.Name] == null) {
  359.                         MethodInfo method = ilMem as MethodInfo;
  360.                         if (method == null || !method.IsSpecialName) {
  361.                             mems.Add(ilMem);
  362.                             uniqueMems[ilMem.Name] = ilMem;
  363.                         }
  364.                     }
  365.                 }
  366.             }
  367.            
  368.             //Add parent members if they don't already exist
  369.             if (this.parent != null) {
  370.                 SimpleHashtable cache = this.parent.wrappedMemberCache;
  371.                 if (cache == null)
  372.                     cache = this.parent.wrappedMemberCache = new SimpleHashtable(8);
  373.                 MemberInfo[] parentMems = ScriptObject.WrapMembers(((IReflect)this.parent).GetMembers(bindingAttr & ~BindingFlags.NonPublic), this.parent, cache);
  374.                 for (int i = 0int n = parentMems.Length; i < n; i++) {
  375.                     MemberInfo parentMem = parentMems[i];
  376.                     if (uniqueMems[parentMem.Name] == null) {
  377.                         mems.Add(parentMem);
  378.                         //uniqueMems[parentMem.Name] = parentMem; //No need to add to lookup table - no one else will be looking.
  379.                     }
  380.                 }
  381.             }
  382.            
  383.             return mems.ToArray();
  384.         }
  385.        
  386.         internal override void GetPropertyEnumerator(ArrayList enums, ArrayList objects)
  387.         {
  388.             if (this.field_table == null)
  389.                 this.field_table = new ArrayList();
  390.             enums.Add(new ListEnumerator(this.field_table));
  391.             objects.Add(this);
  392.             if (this.parent != null)
  393.                 this.parent.GetPropertyEnumerator(enums, objects);
  394.         }
  395.        
  396.         internal override object GetValueAtIndex(uint index)
  397.         {
  398.             //used by array functions
  399.             string name = System.Convert.ToString(index, CultureInfo.InvariantCulture);
  400.             //Do not defer to to routine below, since Array objects override it and could call back to this routine
  401.             FieldInfo field = (FieldInfo)(this.NameTable[name]);
  402.             if (field != null)
  403.                 return field.GetValue(this);
  404.             else {
  405.                 object result = null;
  406.                 if (this.parent != null)
  407.                     result = this.parent.GetMemberValue(name);
  408.                 else
  409.                     result = Missing.Value;
  410.                 if (this is StringObject && result == Missing.Value) {
  411.                     string str = ((StringObject)this).value;
  412.                     if (index < str.Length)
  413.                         return str[(int)index];
  414.                 }
  415.                 return result;
  416.             }
  417.         }
  418.        
  419.         #if !DEBUG
  420.         [DebuggerStepThroughAttribute()]
  421.         [DebuggerHiddenAttribute()]
  422.         #endif
  423.         internal override object GetMemberValue(string name)
  424.         {
  425.             FieldInfo field = (FieldInfo)this.NameTable[name];
  426.             if (field == null && this.isASubClass) {
  427.                 field = this.subClassIR.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public);
  428.                 if (field != null) {
  429.                     if (field.DeclaringType == Typeob.ScriptObject)
  430.                         return Missing.Value;
  431.                 }
  432.                 else {
  433.                     PropertyInfo prop = this.subClassIR.GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
  434.                     if (prop != null && !prop.DeclaringType.IsAssignableFrom(Typeob.JSObject))
  435.                         return JSProperty.GetGetMethod(prop, false).Invoke(this, BindingFlags.SuppressChangeType, null, null, null);
  436.                     try {
  437.                         MethodInfo method = this.subClassIR.GetMethod(name, BindingFlags.Public | BindingFlags.Static);
  438.                         if (method != null) {
  439.                             Type dt = method.DeclaringType;
  440.                             if (dt != Typeob.JSObject && dt != Typeob.ScriptObject && dt != Typeob.Object)
  441.                                 return new BuiltinFunction(this, method);
  442.                         }
  443.                     }
  444.                     catch (AmbiguousMatchException) {
  445.                     }
  446.                 }
  447.             }
  448.             if (field != null)
  449.                 return field.GetValue(this);
  450.             if (this.parent != null)
  451.                 return this.parent.GetMemberValue(name);
  452.             return Missing.Value;
  453.         }
  454.        
  455.         internal SimpleHashtable NameTable {
  456.             get {
  457.                 SimpleHashtable result = this.name_table;
  458.                 if (result == null) {
  459.                     this.name_table = result = new SimpleHashtable(16);
  460.                     this.field_table = new ArrayList();
  461.                 }
  462.                 return result;
  463.             }
  464.         }
  465.        
  466.         void IExpando.RemoveMember(MemberInfo m)
  467.         {
  468.             this.DeleteMember(m.Name);
  469.         }
  470.        
  471.         #if !DEBUG
  472.         [DebuggerStepThroughAttribute()]
  473.         [DebuggerHiddenAttribute()]
  474.         #endif
  475.         internal override void SetMemberValue(string name, object value)
  476.         {
  477.             this.SetMemberValue2(name, value);
  478.         }
  479.        
  480.         public void SetMemberValue2(string name, object value)
  481.         {
  482.             FieldInfo field = (FieldInfo)this.NameTable[name];
  483.             if (field == null && this.isASubClass)
  484.                 field = this.GetType().GetField(name);
  485.             if (field == null) {
  486.                 if (this.noExpando)
  487.                     return;
  488.                 field = new JSExpandoField(name);
  489.                 this.name_table[name] = field;
  490.                 this.field_table.Add(field);
  491.             }
  492.             if (!field.IsInitOnly && !field.IsLiteral)
  493.                 field.SetValue(this, value);
  494.         }
  495.        
  496.         internal override void SetValueAtIndex(uint index, object value)
  497.         {
  498.             this.SetMemberValue(System.Convert.ToString(index, CultureInfo.InvariantCulture), value);
  499.         }
  500.        
  501.         internal virtual void SwapValues(uint left, uint right)
  502.         {
  503.             string left_name = System.Convert.ToString(left, CultureInfo.InvariantCulture);
  504.             string right_name = System.Convert.ToString(right, CultureInfo.InvariantCulture);
  505.             FieldInfo left_field = (FieldInfo)(this.NameTable[left_name]);
  506.             FieldInfo right_field = (FieldInfo)(this.name_table[right_name]);
  507.             if (left_field == null)
  508.                 if (right_field == null)
  509.                     return;
  510.                 else {
  511.                     this.name_table[left_name] = right_field;
  512.                     this.name_table.Remove(right_name);
  513.                 }
  514.             else if (right_field == null) {
  515.                 this.name_table[right_name] = left_field;
  516.                 this.name_table.Remove(left_name);
  517.             }
  518.             else {
  519.                 this.name_table[left_name] = right_field;
  520.                 this.name_table[right_name] = left_field;
  521.             }
  522.         }
  523.        
  524.         public override string ToString()
  525.         {
  526.             return Convert.ToString(this);
  527.         }
  528.        
  529.     }
  530. }

Developer Fusion