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

472 lines
19 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.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices.Expando;
public class JSObject : ScriptObject, IEnumerable, IExpando{
private bool isASubClass;
private IReflect subClassIR;
private SimpleHashtable memberCache;
internal bool noExpando;
internal SimpleHashtable name_table;
protected ArrayList field_table;
internal JSObject outer_class_instance;
public JSObject()
: this(null, false){
this.noExpando = false;
}
internal JSObject(ScriptObject parent)
: this(parent, true) {
}
internal JSObject(ScriptObject parent, bool checkSubType)
: base(parent) {
this.memberCache = null;
this.isASubClass = false;
this.subClassIR = null;
if (checkSubType){
Type subType = this.GetType();
Debug.Assert(subType != typeof(BuiltinFunction));
if (subType != Typeob.JSObject){
this.isASubClass = true;
this.subClassIR = TypeReflector.GetTypeReflectorFor(subType);
}
}else
Debug.Assert(this.GetType() == Typeob.JSObject);
this.noExpando = this.isASubClass;
this.name_table = null;
this.field_table = null;
this.outer_class_instance = null;
}
internal JSObject(ScriptObject parent, Type subType)
: base(parent) {
this.memberCache = null;
this.isASubClass = false;
this.subClassIR = null;
Debug.Assert(subType == this.GetType() || this.GetType() == typeof(BuiltinFunction));
if (subType != Typeob.JSObject){
this.isASubClass = true;
this.subClassIR = TypeReflector.GetTypeReflectorFor(subType);
}
this.noExpando = this.isASubClass;
this.name_table = null;
this.field_table = null;
}
public FieldInfo AddField(String name){
if (this.noExpando)
return null;
FieldInfo field = (FieldInfo)this.NameTable[name];
if (field == null){
field = new JSExpandoField(name);
this.name_table[name] = field;
this.field_table.Add(field);
}
return field;
}
MethodInfo IExpando.AddMethod(String name, Delegate method){
return null;
}
PropertyInfo IExpando.AddProperty(String name){
return null;
}
internal override bool DeleteMember(String name){
FieldInfo field = (FieldInfo)this.NameTable[name];
if (field != null){
if (field is JSExpandoField){
field.SetValue(this, Missing.Value);
this.name_table.Remove(name);
this.field_table.Remove(field);
return true;
}else if (field is JSPrototypeField){
field.SetValue(this, Missing.Value);
return true;
}else
return false;
}else if (this.parent != null)
return LateBinding.DeleteMember(this.parent, name);
else
return false;
}
internal virtual String GetClassName(){
return "Object";
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal override Object GetDefaultValue(PreferredType preferred_type){
if (preferred_type == PreferredType.String){
ScriptFunction toString = this.GetMemberValue("toString") as ScriptFunction;
if (toString != null){
Object result = toString.Call(new Object[0], this);
if (result == null) return result;
IConvertible ic = Convert.GetIConvertible(result);
if (ic != null && ic.GetTypeCode() != TypeCode.Object) return result;
}
ScriptFunction valueOf = this.GetMemberValue("valueOf") as ScriptFunction;
if (valueOf != null){
Object result = valueOf.Call(new Object[0], this);
if (result == null) return result;
IConvertible ic = Convert.GetIConvertible(result);
if (ic != null && ic.GetTypeCode() != TypeCode.Object) return result;
}
}else if (preferred_type == PreferredType.LocaleString){
ScriptFunction toLocaleString = this.GetMemberValue("toLocaleString") as ScriptFunction;
if (toLocaleString != null){
return toLocaleString.Call(new Object[0], this);
}
}else{
if (preferred_type == PreferredType.Either && this is DateObject)
return this.GetDefaultValue(PreferredType.String);
ScriptFunction valueOf = this.GetMemberValue("valueOf") as ScriptFunction;
if (valueOf != null){
Object result = valueOf.Call(new Object[0], this);
if (result == null) return result;
IConvertible ic = Convert.GetIConvertible(result);
if (ic != null && ic.GetTypeCode() != TypeCode.Object) return result;
}
ScriptFunction toString = this.GetMemberValue("toString") as ScriptFunction;
if (toString != null){
Object result = toString.Call(new Object[0], this);
if (result == null) return result;
IConvertible ic = Convert.GetIConvertible(result);
if (ic != null && ic.GetTypeCode() != TypeCode.Object) return result;
}
}
return this;
}
IEnumerator IEnumerable.GetEnumerator(){
return ForIn.JScriptGetEnumerator(this);
}
private MemberInfo[] GetLocalMember(String name, BindingFlags bindingAttr, bool wrapMembers){
MemberInfo[] result = null;
FieldInfo field = this.name_table == null ? null : (FieldInfo)this.name_table[name];
if (field == null && this.isASubClass){
if (this.memberCache != null){
result = (MemberInfo[])this.memberCache[name];
if (result != null) return result;
}
bindingAttr &= ~BindingFlags.NonPublic; //Never expose non public fields of old style objects
result = this.subClassIR.GetMember(name, bindingAttr);
if (result.Length == 0)
result = this.subClassIR.GetMember(name, (bindingAttr&~BindingFlags.Instance)|BindingFlags.Static);
int n = result.Length;
if (n > 0){
//Suppress any members that are declared in JSObject or earlier. But keep the ones in Object.
int hiddenMembers = 0;
foreach (MemberInfo mem in result){
Type mtype = mem.DeclaringType;
if (mtype == Typeob.JSObject || mtype == Typeob.ScriptObject || mtype == Typeob.ArrayWrapper)
hiddenMembers++;
}
if (hiddenMembers > 0 && !(n == 1 && this is ObjectPrototype && name == "ToString")){
MemberInfo[] newResult = new MemberInfo[n-hiddenMembers];
int j = 0;
foreach (MemberInfo mem in result){
Type mtype = mem.DeclaringType;
if (mtype != Typeob.JSObject && mtype != Typeob.ScriptObject && mtype != Typeob.ArrayWrapper)
newResult[j++] = mem;
}
result = newResult;
}
}
if ((result == null || result.Length == 0) && (bindingAttr & BindingFlags.Public) != 0 && (bindingAttr & BindingFlags.Instance) != 0){
BindingFlags flags = (bindingAttr & BindingFlags.IgnoreCase) | BindingFlags.Public | BindingFlags.Instance;
if (this is StringObject)
result = TypeReflector.GetTypeReflectorFor(Typeob.String).GetMember(name, flags);
else if (this is NumberObject)
result = TypeReflector.GetTypeReflectorFor(((NumberObject)this).baseType).GetMember(name, flags);
else if (this is BooleanObject)
result = TypeReflector.GetTypeReflectorFor(Typeob.Boolean).GetMember(name, flags);
else if (this is StringConstructor)
result = TypeReflector.GetTypeReflectorFor(Typeob.String).GetMember(name, (flags|BindingFlags.Static)&~BindingFlags.Instance);
else if (this is BooleanConstructor)
result = TypeReflector.GetTypeReflectorFor(Typeob.Boolean).GetMember(name, (flags|BindingFlags.Static)&~BindingFlags.Instance);
else if (this is ArrayWrapper)
result = TypeReflector.GetTypeReflectorFor(Typeob.Array).GetMember(name, flags);
}
if (result != null && result.Length > 0){
if (wrapMembers)
result = ScriptObject.WrapMembers(result, this);
if (this.memberCache == null) this.memberCache = new SimpleHashtable(32);
this.memberCache[name] = result;
return result;
}
}
if ((bindingAttr&BindingFlags.IgnoreCase) != 0 && (result == null || result.Length == 0)){
result = null;
IDictionaryEnumerator e = this.name_table.GetEnumerator();
while (e.MoveNext()){
if (String.Compare(e.Key.ToString(), name, true, CultureInfo.InvariantCulture) == 0){
field = (FieldInfo)e.Value;
break;
}
}
}
if (field != null)
return new MemberInfo[]{field};
if (result == null) result = new MemberInfo[0];
return result;
}
public override MemberInfo[] GetMember(String name, BindingFlags bindingAttr){
return this.GetMember(name, bindingAttr, false);
}
private MemberInfo[] GetMember(String name, BindingFlags bindingAttr, bool wrapMembers){
MemberInfo[] members = this.GetLocalMember(name, bindingAttr, wrapMembers);
if (members.Length > 0) return members;
if (this.parent != null){
if (this.parent is JSObject){
members = ((JSObject)this.parent).GetMember(name, bindingAttr, true);
wrapMembers = false;
}else
members = this.parent.GetMember(name, bindingAttr);
foreach (MemberInfo mem in members){
if (mem.MemberType == MemberTypes.Field){
FieldInfo field = (FieldInfo)mem;
JSMemberField mfield = mem as JSMemberField;
if (mfield != null){ //This can only happen when running in the Evaluator
if (!mfield.IsStatic){
JSGlobalField gfield = new JSGlobalField(this, name, mfield.value, FieldAttributes.Public);
this.NameTable[name] = gfield;
this.field_table.Add(gfield);
field = mfield;
}
}else{
field = new JSPrototypeField(this.parent, (FieldInfo)mem);
if (!this.noExpando){
this.NameTable[name] = field;
this.field_table.Add(field);
}
}
return new MemberInfo[]{field};
}
if (!this.noExpando){
if (mem.MemberType == MemberTypes.Method){
FieldInfo field = new JSPrototypeField(this.parent,
new JSGlobalField(this, name,
LateBinding.GetMemberValue(this.parent, name, null, members),
FieldAttributes.Public|FieldAttributes.InitOnly));
this.NameTable[name] = field;
this.field_table.Add(field);
return new MemberInfo[]{field};
}
}
}
if (wrapMembers)
return ScriptObject.WrapMembers(members, this.parent);
else
return members;
}
return new MemberInfo[0];
}
public override MemberInfo[] GetMembers(BindingFlags bindingAttr){
MemberInfoList mems = new MemberInfoList();
SimpleHashtable uniqueMems = new SimpleHashtable(32);
if (!this.noExpando && this.field_table != null){ //Add any expando properties
IEnumerator enu = this.field_table.GetEnumerator();
while (enu.MoveNext()){
FieldInfo field = (FieldInfo)enu.Current;
mems.Add(field);
uniqueMems[field.Name] = field;
}
}
//Add the public members of the built-in objects if they don't already exist
if (this.isASubClass){
MemberInfo[] ilMembers = this.GetType().GetMembers(bindingAttr & ~BindingFlags.NonPublic); //Never expose non public members of old style objects
for (int i = 0, n = ilMembers.Length; i < n; i++){
MemberInfo ilMem = ilMembers[i];
//Hide any infrastructure stuff in JSObject
if (!ilMem.DeclaringType.IsAssignableFrom(Typeob.JSObject) && uniqueMems[ilMem.Name] == null){
MethodInfo method = ilMem as MethodInfo;
if (method == null || !method.IsSpecialName){
mems.Add(ilMem);
uniqueMems[ilMem.Name] = ilMem;
}
}
}
}
//Add parent members if they don't already exist
if (this.parent != null){
SimpleHashtable cache = this.parent.wrappedMemberCache;
if (cache == null)
cache = this.parent.wrappedMemberCache = new SimpleHashtable(8);
MemberInfo[] parentMems = ScriptObject.WrapMembers(((IReflect)this.parent).GetMembers(bindingAttr & ~BindingFlags.NonPublic), this.parent, cache);
for(int i = 0, n = parentMems.Length; i < n; i++){
MemberInfo parentMem = parentMems[i];
if(uniqueMems[parentMem.Name] == null){
mems.Add(parentMem);
//uniqueMems[parentMem.Name] = parentMem; //No need to add to lookup table - no one else will be looking.
}
}
}
return mems.ToArray();
}
internal override void GetPropertyEnumerator(ArrayList enums, ArrayList objects){
if (this.field_table == null) this.field_table = new ArrayList();
enums.Add(new ListEnumerator(this.field_table));
objects.Add(this);
if (this.parent != null)
this.parent.GetPropertyEnumerator(enums, objects);
}
internal override Object GetValueAtIndex(uint index){ //used by array functions
String name = System.Convert.ToString(index);
//Do not defer to to routine below, since Array objects override it and could call back to this routine
FieldInfo field = (FieldInfo)(this.NameTable[name]);
if (field != null)
return field.GetValue(this);
else{
Object result = null;
if (this.parent != null)
result = this.parent.GetMemberValue(name);
else
result = Missing.Value;
if (this is StringObject && result == Missing.Value){
String str = ((StringObject)this).value;
if (index < str.Length)
return str[(int)index];
}
return result;
}
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal override Object GetMemberValue(String name){
FieldInfo field = (FieldInfo)this.NameTable[name];
if (field == null && this.isASubClass){
field = this.subClassIR.GetField(name, BindingFlags.Instance|BindingFlags.Static|BindingFlags.Public);
if (field != null){
if (field.DeclaringType == Typeob.ScriptObject) return Missing.Value;
}else{
PropertyInfo prop = this.subClassIR.GetProperty(name, BindingFlags.Instance|BindingFlags.Public);
if (prop != null && !prop.DeclaringType.IsAssignableFrom(Typeob.JSObject))
return JSProperty.GetGetMethod(prop, false).Invoke(this, BindingFlags.SuppressChangeType, null, null, null);
try{
MethodInfo method = this.subClassIR.GetMethod(name, BindingFlags.Public|BindingFlags.Static);
if (method != null){
Type dt = method.DeclaringType;
if (dt != Typeob.JSObject && dt != Typeob.ScriptObject && dt != Typeob.Object)
return new BuiltinFunction(this, method);
}
}catch(AmbiguousMatchException){}
}
}
if (field != null)
return field.GetValue(this);
if (this.parent != null)
return this.parent.GetMemberValue(name);
return Missing.Value;
}
internal SimpleHashtable NameTable{
get{
SimpleHashtable result = this.name_table;
if (result == null){
this.name_table = result = new SimpleHashtable(16);
this.field_table = new ArrayList();
}
return result;
}
}
void IExpando.RemoveMember(MemberInfo m){
this.DeleteMember(m.Name);
}
#if !DEBUG
[DebuggerStepThroughAttribute]
[DebuggerHiddenAttribute]
#endif
internal override void SetMemberValue(String name, Object value){
this.SetMemberValue2(name, value);
}
public void SetMemberValue2(String name, Object value){
FieldInfo field = (FieldInfo)this.NameTable[name];
if (field == null && this.isASubClass)
field = this.GetType().GetField(name);
if (field == null){
if (this.noExpando)
return;
field = new JSExpandoField(name);
this.name_table[name] = field;
this.field_table.Add(field);
}
if (!field.IsInitOnly && !field.IsLiteral)
field.SetValue(this, value);
}
internal override void SetValueAtIndex(uint index, Object value){
this.SetMemberValue(System.Convert.ToString(index), value);
}
internal virtual void SwapValues(uint left, uint right){
String left_name = System.Convert.ToString(left);
String right_name = System.Convert.ToString(right);
FieldInfo left_field = (FieldInfo)(this.NameTable[left_name]);
FieldInfo right_field = (FieldInfo)(this.name_table[right_name]);
if (left_field == null)
if (right_field == null)
return;
else{
this.name_table[left_name] = right_field;
this.name_table.Remove(right_name);
}
else if (right_field == null){
this.name_table[right_name] = left_field;
this.name_table.Remove(left_name);
}else{
this.name_table[left_name] = right_field;
this.name_table[right_name] = left_field;
}
}
public override String ToString(){
return Convert.ToString(this);
}
}
}