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.
500 lines
21 KiB
C#
500 lines
21 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.Collections;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
|
|
public abstract class ScriptObject : IReflect{
|
|
protected ScriptObject parent;
|
|
|
|
//Used by JSObject.GetMembers to cache members that must be delegated to other objects
|
|
internal SimpleHashtable wrappedMemberCache;
|
|
|
|
public VsaEngine engine; //This is only really useful for ScriptFunctions, IActivation objects and prototype objects. It lives here for the sake of simplicity.
|
|
//Prototype objects do not need the scope stack, so in fast mode, all prototype objects can share a common engine.
|
|
|
|
internal ScriptObject(ScriptObject parent){
|
|
this.parent = parent;
|
|
this.wrappedMemberCache = null;
|
|
if (this.parent != null)
|
|
this.engine = parent.engine;
|
|
else
|
|
this.engine = null;
|
|
}
|
|
|
|
internal virtual bool DeleteMember(String name){
|
|
return false;
|
|
}
|
|
|
|
internal virtual Object GetDefaultValue(PreferredType preferred_type){
|
|
throw new JScriptException(JSError.InternalError);
|
|
}
|
|
|
|
public FieldInfo GetField(String name, BindingFlags bindingAttr){
|
|
foreach (MemberInfo member in this.GetMember(name, bindingAttr))
|
|
if (member.MemberType == MemberTypes.Field) return (FieldInfo)member;
|
|
return null;
|
|
}
|
|
|
|
public virtual FieldInfo[] GetFields(BindingFlags bindingAttr){
|
|
ArrayObject arr = this as ArrayObject;
|
|
if (arr != null && arr.denseArrayLength > 0){
|
|
//Interop fails to see elements unless they are returned in a GetXXXs call. An element in
|
|
//the sparse portion of the array is returned as a FieldInfo. When interop asks for these
|
|
//migrate the elements stored in the dense portion of the array to the sparse portion so that
|
|
//they will be accessible.
|
|
uint i, length;
|
|
length = arr.denseArrayLength;
|
|
if (length > arr.len)
|
|
length = arr.len;
|
|
for (i = 0; i < length; i++){
|
|
Object element = arr.denseArray[i];
|
|
if (element != Missing.Value)
|
|
arr.SetMemberValue2(i.ToString(), element); //Implemented on JSObject and bypasses length checking
|
|
}
|
|
arr.denseArrayLength = 0;
|
|
arr.denseArray = null;
|
|
}
|
|
|
|
MemberInfo[] members = this.GetMembers(bindingAttr);
|
|
if (members == null) return new FieldInfo[0];
|
|
int fieldCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Field) fieldCount++;
|
|
FieldInfo[] fields = new FieldInfo[fieldCount];
|
|
fieldCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Field) fields[fieldCount++] = (FieldInfo)member;
|
|
return fields;
|
|
}
|
|
|
|
public abstract MemberInfo[] GetMember(String name, BindingFlags bindingAttr);
|
|
|
|
internal virtual Object GetMemberValue(String name){
|
|
MemberInfo[] members = this.GetMember(name, BindingFlags.Instance|BindingFlags.Public);
|
|
if (members.Length == 0)
|
|
return Missing.Value;
|
|
return LateBinding.GetMemberValue(this, name, LateBinding.SelectMember(members), members);
|
|
}
|
|
|
|
public abstract MemberInfo[] GetMembers(BindingFlags bindingAttr);
|
|
|
|
public MethodInfo GetMethod(String name, BindingFlags bindingAttr){
|
|
return this.GetMethod(name, bindingAttr, JSBinder.ob, Type.EmptyTypes, null);
|
|
}
|
|
|
|
public MethodInfo GetMethod(String name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers){
|
|
MemberInfo[] members = this.GetMember(name, bindingAttr);
|
|
if (members.Length == 1) return members[0] as MethodInfo;
|
|
int methodCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Method) methodCount++;
|
|
if (methodCount == 0) return null;
|
|
MethodInfo[] methods = new MethodInfo[methodCount];
|
|
methodCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Method) methods[methodCount++] = (MethodInfo)member;
|
|
if (binder == null) binder = JSBinder.ob;
|
|
return (MethodInfo)binder.SelectMethod(bindingAttr, methods, types, modifiers);
|
|
}
|
|
|
|
public virtual MethodInfo[] GetMethods(BindingFlags bindingAttr){
|
|
MemberInfo[] members = this.GetMembers(bindingAttr);
|
|
if (members == null) return new MethodInfo[0];
|
|
int methodCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Method) methodCount++;
|
|
MethodInfo[] methods = new MethodInfo[methodCount];
|
|
methodCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Method) methods[methodCount++] = (MethodInfo)member;
|
|
return methods;
|
|
}
|
|
|
|
//Returns the parent object (the object to which the script object delegates requests for properties/methods it does not implement itself).
|
|
public ScriptObject GetParent(){
|
|
return this.parent;
|
|
}
|
|
|
|
internal virtual void GetPropertyEnumerator(ArrayList enums, ArrayList objects){
|
|
MemberInfo[] members = this.GetMembers(BindingFlags.Instance|BindingFlags.Public);
|
|
if (members.Length > 0){
|
|
enums.Add(members.GetEnumerator());
|
|
objects.Add(this);
|
|
}
|
|
ScriptObject parent = this.GetParent();
|
|
if (parent != null)
|
|
parent.GetPropertyEnumerator(enums, objects);
|
|
}
|
|
|
|
public PropertyInfo GetProperty(String name, BindingFlags bindingAttr){
|
|
return this.GetProperty(name, bindingAttr, JSBinder.ob, null, Type.EmptyTypes, null);
|
|
}
|
|
|
|
public PropertyInfo GetProperty(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers){
|
|
MemberInfo[] members = this.GetMember(name, bindingAttr);
|
|
if (members.Length == 1) return members[0] as PropertyInfo;
|
|
int propertyCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Property) propertyCount++;
|
|
if (propertyCount == 0) return null;
|
|
PropertyInfo[] properties = new PropertyInfo[propertyCount];
|
|
propertyCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Property) properties[propertyCount++] = (PropertyInfo)member;
|
|
if (binder == null) binder = JSBinder.ob;
|
|
return (PropertyInfo)binder.SelectProperty(bindingAttr, properties, returnType, types, modifiers);
|
|
}
|
|
|
|
public virtual PropertyInfo[] GetProperties(BindingFlags bindingAttr){
|
|
MemberInfo[] members = this.GetMembers(bindingAttr);
|
|
if (members == null) return new PropertyInfo[0];
|
|
int propertyCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Property) propertyCount++;
|
|
PropertyInfo[] properties = new PropertyInfo[propertyCount];
|
|
propertyCount = 0;
|
|
foreach (MemberInfo member in members)
|
|
if (member.MemberType == MemberTypes.Property) properties[propertyCount++] = (PropertyInfo)member;
|
|
return properties;
|
|
}
|
|
|
|
internal virtual Object GetValueAtIndex(uint index){
|
|
return this.GetMemberValue(index.ToString());
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public virtual Object InvokeMember(String name, BindingFlags invokeAttr, Binder binder, Object target,
|
|
Object[] args, ParameterModifier[] modifiers, CultureInfo locale, String[] namedParameters){
|
|
if (target != this)
|
|
throw new TargetException();
|
|
|
|
bool preferredTypeSpecified = name.StartsWith("< JScript-");
|
|
bool dispid0 = (name == null || name == String.Empty || name.Equals("[DISPID=0]") || preferredTypeSpecified);
|
|
|
|
if ((invokeAttr & BindingFlags.CreateInstance) != 0){
|
|
if ((invokeAttr & (BindingFlags.InvokeMethod|BindingFlags.GetField|BindingFlags.GetProperty|
|
|
BindingFlags.SetField|BindingFlags.SetProperty|BindingFlags.PutDispProperty)) != 0)
|
|
throw new ArgumentException();
|
|
//js: x = new foo() --> dispid0, create
|
|
if (dispid0)
|
|
throw new MissingMethodException();
|
|
|
|
//js: x = new foo.name() --> dispid0, create
|
|
LateBinding lb = new LateBinding(name, this);
|
|
return lb.Call(binder, args, modifiers, locale, namedParameters, true, false, this.engine);
|
|
}
|
|
|
|
//According to docs, name == null is only valid for CreateInstance
|
|
if (name == null)
|
|
throw new ArgumentException();
|
|
|
|
if ((invokeAttr & (BindingFlags.InvokeMethod|BindingFlags.GetField|BindingFlags.GetProperty)) != 0){
|
|
if ((invokeAttr & (BindingFlags.SetField|BindingFlags.SetProperty|BindingFlags.PutDispProperty)) != 0)
|
|
throw new ArgumentException();
|
|
|
|
if (dispid0){
|
|
//All callable functions inherit from ScriptFunction which overrides this method to handle
|
|
//the InvokeMethod case.
|
|
|
|
//js,vbs: x = foo() --> dispid0, invoke
|
|
if ((invokeAttr & (BindingFlags.GetField|BindingFlags.GetProperty)) == 0)
|
|
throw new MissingMethodException();
|
|
|
|
//js: x = foo --> dispid0, propget; vbs: x = foo --> dispid0, invoke|propget
|
|
if (args == null || args.Length == 0){
|
|
if (this is JSObject || this is GlobalScope || this is ClassScope){
|
|
PreferredType preferredType = PreferredType.Either;
|
|
if (preferredTypeSpecified){
|
|
if (name.StartsWith("< JScript-Number"))
|
|
preferredType = PreferredType.Number;
|
|
else if (name.StartsWith("< JScript-String"))
|
|
preferredType = PreferredType.String;
|
|
else if (name.StartsWith("< JScript-LocaleString"))
|
|
preferredType = PreferredType.LocaleString;
|
|
}
|
|
return this.GetDefaultValue(preferredType);
|
|
}
|
|
throw new MissingFieldException();
|
|
}
|
|
|
|
//We support indexed properties with exactly one index on all script objects.
|
|
|
|
//js,vbs: x = foo(1,2) --> dispid0, invoke|propget
|
|
if (args.Length > 1)
|
|
throw new ArgumentException();
|
|
|
|
//js,vbs: x = foo(1) --> dispid0, invoke|propget
|
|
Object val = args[0];
|
|
if (val is Int32)
|
|
return this[(int)val];
|
|
IConvertible ic = Convert.GetIConvertible(val);
|
|
if (ic != null && Convert.IsPrimitiveNumericTypeCode(ic.GetTypeCode())){
|
|
double d = ic.ToDouble(null);
|
|
if (d >= 0 && d <= Int32.MaxValue && d == System.Math.Round(d))
|
|
return this[(int)d];
|
|
}
|
|
return this[Convert.ToString(val)];
|
|
}
|
|
|
|
//If no arguments are supplied, prefer GetXXXX rather than Invoke.
|
|
//js: x = foo.bar --> name="bar", propget; vbs: x = foo.bar --> name="bar", propget|invoke
|
|
if ((args == null || args.Length == 0) && (invokeAttr & (BindingFlags.GetField|BindingFlags.GetProperty)) != 0){
|
|
Object member = this.GetMemberValue(name);
|
|
if (member != Missing.Value)
|
|
return member;
|
|
|
|
//js: x = foo.bar --> name="bar", propget
|
|
if ((invokeAttr & BindingFlags.InvokeMethod) == 0)
|
|
throw new MissingFieldException();
|
|
}
|
|
|
|
//Use LateBinding to call because arguments have been supplied.
|
|
//vbs: x = foo.bar --> name="bar", propget|invoke
|
|
//js,vbs: x = foo.bar() --> name="bar", invoke
|
|
//js,vbs: x = foo.bar(1) --> name="bar", invoke|propget
|
|
LateBinding lb = new LateBinding(name, this);
|
|
return lb.Call(binder, args, modifiers, locale, namedParameters, false, false, this.engine);
|
|
}
|
|
|
|
if ((invokeAttr & (BindingFlags.SetField | BindingFlags.SetProperty | BindingFlags.PutDispProperty)) != 0){
|
|
if (dispid0){
|
|
if (args == null || args.Length != 2)
|
|
throw new ArgumentException();
|
|
Object val = args[0];
|
|
if (val is Int32){
|
|
this[(int)val] = args[1];
|
|
return null;
|
|
}
|
|
IConvertible ic = Convert.GetIConvertible(val);
|
|
if (ic != null && Convert.IsPrimitiveNumericTypeCode(ic.GetTypeCode())){
|
|
double d = ic.ToDouble(null);
|
|
if (d >= 0 && d <= Int32.MaxValue && d == System.Math.Round(d)){
|
|
this[(int)d] = args[1];
|
|
return null;
|
|
}
|
|
}
|
|
this[Convert.ToString(val)] = args[1];
|
|
return null;
|
|
}
|
|
if (args == null || args.Length > 1)
|
|
throw new ArgumentException();
|
|
this.SetMemberValue(name, args[0]);
|
|
return null;
|
|
}
|
|
|
|
throw new ArgumentException();
|
|
}
|
|
|
|
//This routine combines a GetField/GetProperty (with expando) and a subsequent SetValue operation.
|
|
//It is used when the Field/Property cannot be cached. It is overridden by JSObject and GlobalScope to provide expando
|
|
internal virtual void SetMemberValue(String name, Object value){
|
|
MemberInfo[] members = this.GetMember(name, BindingFlags.Instance|BindingFlags.Public);
|
|
LateBinding.SetMemberValue(this, name, value, LateBinding.SelectMember(members), members);
|
|
}
|
|
|
|
internal void SetParent(ScriptObject parent){
|
|
this.parent = parent;
|
|
if (parent != null)
|
|
this.engine = parent.engine;
|
|
}
|
|
|
|
//This routine combines a GetField/GetProperty and a subsequent SetValue operation.
|
|
//It is used when the Field/Property cannot be cached.
|
|
//It has the advantage that Arrays need never create a JSField object to access array elements.
|
|
//Also, it avoids the need to convert the index into a string.
|
|
internal virtual void SetValueAtIndex(uint index, Object value){
|
|
this.SetMemberValue(index.ToString(), value);
|
|
}
|
|
|
|
public Object this[double index]{
|
|
get{
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
Object result;
|
|
if (index >= 0 && index <= UInt32.MaxValue && index == System.Math.Round(index))
|
|
result = this.GetValueAtIndex((uint)index);
|
|
else
|
|
result = this.GetMemberValue(Convert.ToString(index));
|
|
return result is Missing ? null : result;
|
|
}
|
|
set{
|
|
if (index >= 0 && index <= UInt32.MaxValue && index == System.Math.Round(index))
|
|
this.SetValueAtIndex((uint)index, value);
|
|
else
|
|
this.SetMemberValue(Convert.ToString(index), value);
|
|
}
|
|
}
|
|
|
|
public Object this[int index]{
|
|
get{
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
Object result;
|
|
if (index >= 0)
|
|
result = this.GetValueAtIndex((uint)index);
|
|
else
|
|
result = this.GetMemberValue(Convert.ToString(index));
|
|
return result is Missing ? null : result;
|
|
}
|
|
set{
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
if (index >= 0)
|
|
this.SetValueAtIndex((uint)index, value);
|
|
else
|
|
this.SetMemberValue(Convert.ToString(index), value);
|
|
}
|
|
}
|
|
|
|
public Object this[String name]{
|
|
get{
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
Object result = this.GetMemberValue(name);
|
|
return result is Missing ? null : result;
|
|
}
|
|
set{
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
this.SetMemberValue(name, value);
|
|
}
|
|
}
|
|
|
|
public Object this[params Object[] pars]{
|
|
get{
|
|
int n = pars.Length;
|
|
if (n == 0)
|
|
if (this is ScriptFunction || this == null)
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
else
|
|
throw new JScriptException(JSError.TooFewParameters);
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
Object val = pars[n-1];
|
|
if (val is Int32)
|
|
return this[(int)val];
|
|
IConvertible ic = Convert.GetIConvertible(val);
|
|
if (ic != null && Convert.IsPrimitiveNumericTypeCode(ic.GetTypeCode())){
|
|
double d = ic.ToDouble(null);
|
|
if (d >= 0 && d <= Int32.MaxValue && d == System.Math.Round(d))
|
|
return this[(int)d];
|
|
}
|
|
return this[Convert.ToString(val)];
|
|
}
|
|
set{
|
|
int n = pars.Length;
|
|
if (n == 0)
|
|
if (this == null)
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
else if (this is ScriptFunction)
|
|
throw new JScriptException(JSError.CannotAssignToFunctionResult);
|
|
else
|
|
throw new JScriptException(JSError.TooFewParameters);
|
|
if (this == null) throw new JScriptException(JSError.ObjectExpected);
|
|
Object val = pars[n-1];
|
|
if (val is Int32){
|
|
this[(int)val] = value;
|
|
return;
|
|
}
|
|
IConvertible ic = Convert.GetIConvertible(val);
|
|
if (ic != null && Convert.IsPrimitiveNumericTypeCode(ic.GetTypeCode())){
|
|
double d = ic.ToDouble(null);
|
|
if (d >= 0 && d <= Int32.MaxValue && d == System.Math.Round(d)){
|
|
this[(int)d] = value;
|
|
return;
|
|
}
|
|
}
|
|
this[Convert.ToString(val)] = value;
|
|
}
|
|
}
|
|
|
|
public virtual Type UnderlyingSystemType{
|
|
get{
|
|
return this.GetType();
|
|
}
|
|
}
|
|
|
|
protected static MemberInfo[] WrapMembers(MemberInfo[] members, Object obj){
|
|
if (members == null) return null;
|
|
int n = members.Length;
|
|
if (n == 0) return members;
|
|
MemberInfo[] result = new MemberInfo[n];
|
|
for (int i = 0; i < n; i++)
|
|
result[i] = ScriptObject.WrapMember(members[i], obj);
|
|
return result;
|
|
}
|
|
|
|
protected static MemberInfo[] WrapMembers(MemberInfo member, Object obj){
|
|
MemberInfo[] result = new MemberInfo[1];
|
|
result[0] = ScriptObject.WrapMember(member, obj);
|
|
return result;
|
|
}
|
|
|
|
protected static MemberInfo[] WrapMembers(MemberInfo[] members, Object obj, SimpleHashtable cache){
|
|
if (members == null) return null;
|
|
int n = members.Length;
|
|
if (n == 0) return members;
|
|
MemberInfo[] result = new MemberInfo[n];
|
|
for (int i = 0; i < n; i++){
|
|
MemberInfo wrappedMember = (MemberInfo)cache[members[i]];
|
|
if (null == wrappedMember){
|
|
wrappedMember = ScriptObject.WrapMember(members[i], obj);
|
|
cache[members[i]] = wrappedMember;
|
|
}
|
|
result[i] = wrappedMember;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal static MemberInfo WrapMember(MemberInfo member, Object obj){
|
|
switch (member.MemberType){
|
|
case MemberTypes.Field:
|
|
FieldInfo field = (FieldInfo)member;
|
|
if (field.IsStatic || field.IsLiteral)
|
|
return field;
|
|
else if (!(field is JSWrappedField))
|
|
return new JSWrappedField(field, obj);
|
|
else
|
|
return field;
|
|
case MemberTypes.Method:
|
|
MethodInfo method = (MethodInfo)member;
|
|
if (method.IsStatic)
|
|
return method;
|
|
else if (!(method is JSWrappedMethod))
|
|
return new JSWrappedMethod(method, obj);
|
|
else
|
|
return method;
|
|
case MemberTypes.Property:
|
|
PropertyInfo property = (PropertyInfo)member;
|
|
if (property is JSWrappedProperty)
|
|
return property;
|
|
MethodInfo getMethod = JSProperty.GetGetMethod(property, true);
|
|
MethodInfo setMethod = JSProperty.GetSetMethod(property, true);
|
|
if ((getMethod == null || getMethod.IsStatic) && (setMethod == null || setMethod.IsStatic))
|
|
return property;
|
|
else
|
|
return new JSWrappedProperty(property, obj);
|
|
default:
|
|
return member;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|