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

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;
}
}
}
}