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.
1289 lines
60 KiB
C#
1289 lines
60 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.Diagnostics;
|
|
using System.Collections;
|
|
using System.Globalization;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices.Expando;
|
|
|
|
public sealed class LateBinding{
|
|
private Object last_ir;
|
|
internal MemberInfo last_member;
|
|
internal MemberInfo[] last_members;
|
|
internal Object last_object;
|
|
private String name; //If this is null, the default indexed property should be called
|
|
public Object obj;
|
|
private bool checkForDebugger;
|
|
|
|
public LateBinding(String name)
|
|
: this(name, null, false){
|
|
}
|
|
|
|
public LateBinding(String name, Object obj)
|
|
: this(name, obj, false){
|
|
}
|
|
|
|
internal LateBinding(String name, Object obj, bool checkForDebugger){
|
|
this.last_ir = null;
|
|
this.last_member = null;
|
|
this.last_members = null;
|
|
this.last_object = null;
|
|
this.name = name;
|
|
this.obj = obj;
|
|
this.checkForDebugger = checkForDebugger;
|
|
}
|
|
|
|
internal MemberInfo BindToMember(){
|
|
Debug.Assert(this.obj != null && this.name != null);
|
|
if (this.obj == this.last_object && this.last_member != null)
|
|
return this.last_member;
|
|
BindingFlags flags = BindingFlags.Instance|BindingFlags.Public;
|
|
Object obj = this.obj;
|
|
Type t = obj.GetType();
|
|
TypeReflector tr = TypeReflector.GetTypeReflectorFor(t);
|
|
IReflect ir = null;
|
|
if (tr.ImplementsIReflect()){
|
|
ir = obj as ScriptObject;
|
|
if (ir != null){
|
|
if (obj is ClassScope)
|
|
flags = BindingFlags.Static|BindingFlags.Public;
|
|
}else{
|
|
ir = obj as Type;
|
|
if (ir != null)
|
|
flags = BindingFlags.Static|BindingFlags.Public;
|
|
else
|
|
ir = (IReflect)obj;
|
|
}
|
|
}else
|
|
ir = tr;
|
|
this.last_object = this.obj;
|
|
this.last_ir = ir;
|
|
MemberInfo[] members = this.last_members = ir.GetMember(this.name, flags);
|
|
this.last_member = LateBinding.SelectMember(members);
|
|
if (this.obj is Type){ //Add instance members of type Type to the member list
|
|
MemberInfo[] tmembers = Typeob.Type.GetMember(this.name, BindingFlags.Instance|BindingFlags.Public);
|
|
int n = 0, m = 0;
|
|
if (tmembers != null && (n = tmembers.Length) > 0){
|
|
if (members == null || (m = members.Length) == 0)
|
|
this.last_member = LateBinding.SelectMember(this.last_members = tmembers);
|
|
else{
|
|
MemberInfo[] jmembers = new MemberInfo[n + m];
|
|
ArrayObject.Copy(members, 0, jmembers, 0, m);
|
|
ArrayObject.Copy(tmembers, 0, jmembers, m, n);
|
|
this.last_member = LateBinding.SelectMember(this.last_members = jmembers);
|
|
}
|
|
}
|
|
}
|
|
return this.last_member;
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public Object Call(Object[] arguments, bool construct, bool brackets, VsaEngine engine){
|
|
try{
|
|
if (this.name == null)
|
|
//Happens when (non simple expr)(args) is evaluated from Eval or the Debugger
|
|
return LateBinding.CallValue(this.obj, arguments, construct, brackets, engine,
|
|
((IActivationObject)engine.ScriptObjectStackTop()).GetDefaultThisObject(), JSBinder.ob, null, null);
|
|
else
|
|
return this.Call(JSBinder.ob, arguments, null, null, null, construct, brackets, engine);
|
|
}catch(TargetInvocationException e){
|
|
throw e.InnerException;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal Object Call(Binder binder, Object[] arguments, ParameterModifier[] modifiers, CultureInfo culture,
|
|
String[] namedParameters, bool construct, bool brackets, VsaEngine engine){
|
|
MemberInfo member = this.BindToMember(); //Do a GetMember call and remember the result of the lookup for next time around
|
|
|
|
|
|
//On ScriptObjects, fields/properties pre-empt everything else. Call whatever is the value of the field/property. If it is junk, throw an exception.
|
|
if (this.obj is ScriptObject || this.obj is GlobalObject){
|
|
//Handle WithObjects that wrap COMObjects specially
|
|
if (this.obj is WithObject){
|
|
Object wob = ((WithObject)this.obj).contained_object;
|
|
if (!(wob is ScriptObject)){
|
|
IReflect irw = LateBinding.GetIRForObjectThatRequiresInvokeMember(wob, VsaEngine.executeForJSEE);
|
|
if (irw != null)
|
|
return LateBinding.CallCOMObject(irw, this.name, wob, binder, arguments, modifiers, culture, namedParameters, construct, brackets, engine);
|
|
}
|
|
}
|
|
|
|
if (member is FieldInfo)
|
|
return LateBinding.CallValue(((FieldInfo)member).GetValue(this.obj), arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
else if (member is PropertyInfo && !(member is JSProperty)){ //JSProperty comes up when running the evaluator and using proper properties.
|
|
if (!brackets){
|
|
JSWrappedPropertyAndMethod propAndMethod = member as JSWrappedPropertyAndMethod;
|
|
if (propAndMethod != null){
|
|
BindingFlags flags = arguments == null || arguments.Length == 0
|
|
? BindingFlags.InvokeMethod
|
|
: BindingFlags.InvokeMethod | BindingFlags.GetProperty;
|
|
return propAndMethod.Invoke(this.obj, flags, JSBinder.ob, arguments, null);
|
|
}
|
|
}
|
|
return LateBinding.CallValue(JSProperty.GetValue((PropertyInfo)member, this.obj, null), arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
}
|
|
//The code paths below happen because Lookup and Member set last_member. Without the cache, BindToMember will not deliver a MethodInfo.
|
|
else if (member is MethodInfo){
|
|
if (member is JSMethod)
|
|
if (construct)
|
|
return ((JSMethod)member).Construct(arguments);
|
|
else
|
|
return ((JSMethod)member).Invoke(this.obj, this.obj, (BindingFlags)0, JSBinder.ob, arguments, null);
|
|
else{
|
|
Type dt = member.DeclaringType;
|
|
if (dt == Typeob.Object)
|
|
return LateBinding.CallMethod((MethodInfo)member, arguments, this.obj, binder, culture, namedParameters);
|
|
else if (dt == Typeob.String)
|
|
return LateBinding.CallMethod((MethodInfo)member, arguments, Convert.ToString(this.obj), binder, culture, namedParameters);
|
|
else if (Convert.IsPrimitiveNumericType(dt))
|
|
return LateBinding.CallMethod((MethodInfo)member, arguments, Convert.CoerceT(this.obj, dt), binder, culture, namedParameters);
|
|
else if (dt == Typeob.Boolean)
|
|
return LateBinding.CallMethod((MethodInfo)member, arguments, Convert.ToBoolean(this.obj), binder, culture, namedParameters);
|
|
else if (dt == Typeob.StringObject || dt == Typeob.BooleanObject || dt == Typeob.NumberObject || brackets)
|
|
return LateBinding.CallMethod((MethodInfo)member, arguments, Convert.ToObject(this.obj, engine), binder, culture, namedParameters);
|
|
else if (dt == Typeob.GlobalObject && ((MethodInfo)member).IsSpecialName)
|
|
return LateBinding.CallValue(((MethodInfo)member).Invoke(this.obj, null), arguments, construct, false, engine, this.obj, JSBinder.ob, null, null);
|
|
else if (!(this.obj is ClassScope)){
|
|
if (member.IsDefined(Typeob.JSFunctionAttribute, false)){
|
|
FieldInfo f = LateBinding.SelectMember(this.last_members) as FieldInfo;
|
|
if (f != null){ //Field holds a closure, call the closure
|
|
Object obj = this.obj;
|
|
if (!(obj is Closure))
|
|
obj = f.GetValue(this.obj);
|
|
return LateBinding.CallValue(obj, arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
}
|
|
}
|
|
return LateBinding.CallValue(new BuiltinFunction(this.obj, (MethodInfo)member), arguments, construct, false, engine, this.obj, JSBinder.ob, null, null);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MethodInfo meth = member as MethodInfo;
|
|
if (meth != null) //Inside the evaluator, and dealing with an early bound method
|
|
//Get here because of EvaluateAsLateBinding in Lookup and Member. BindToMember will not have set this.member to be a method
|
|
return LateBinding.CallMethod(meth, arguments, this.obj, binder, culture, namedParameters);
|
|
JSConstructor jscons = member as JSConstructor;
|
|
if (jscons != null) //Inside the evaluator, and dealing with an early bound constructor
|
|
return LateBinding.CallValue(jscons.cons, arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
if (member is Type) //Inside the evaluator and dealing with early bound conversion
|
|
return LateBinding.CallValue(member, arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
if (member is ConstructorInfo) //Inside the evaluator, and dealing with an early bound constructor
|
|
return LateBinding.CallOneOfTheMembers(new MemberInfo[]{this.last_member}, arguments, true, this.obj, binder, culture, namedParameters, engine);
|
|
|
|
if (!construct && member is PropertyInfo){
|
|
//Might be a parameterless property that results in an object that has a default method or default indexed property that can be called with the params
|
|
//Need to special case this, otherwise the next if will end up calling the parameterless property getter, discarding the parameters
|
|
if (arguments.Length > 0 && ((PropertyInfo)member).GetIndexParameters().Length == 0){
|
|
Type rtype = ((PropertyInfo)member).PropertyType;
|
|
if (rtype == Typeob.Object){
|
|
MethodInfo getter = JSProperty.GetGetMethod((PropertyInfo)member, false);
|
|
if (getter != null){
|
|
Object ob = getter.Invoke(this.obj, null);
|
|
return LateBinding.CallValue(ob, arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
}
|
|
}
|
|
MemberInfo[] defaultMembers = TypeReflector.GetTypeReflectorFor(rtype).GetDefaultMembers();
|
|
if (defaultMembers != null && defaultMembers.Length > 0){
|
|
MethodInfo getter = JSProperty.GetGetMethod((PropertyInfo)member, false);
|
|
if (getter != null){
|
|
Object ob = getter.Invoke(this.obj, null);
|
|
return LateBinding.CallOneOfTheMembers(defaultMembers, arguments, false, ob, binder, culture, namedParameters, engine);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Otherwise, if there are members, we give preference to constructors, methods and properties with matching formal parameter lists.
|
|
if (this.last_members != null && this.last_members.Length > 0)
|
|
try{
|
|
return LateBinding.CallOneOfTheMembers(this.last_members, arguments, construct, this.obj, binder, culture, namedParameters, engine);
|
|
}catch(MissingMemberException){
|
|
//No suitable constructor or method or property getter, but the value of (this.obj).(this.name) might contain a callable thing, so carry on.
|
|
}
|
|
|
|
//If the object is an IDispatch(Ex) use InvokeMember to do the call
|
|
//In all other cases we can assume that BindToMember has already found all the members that are to be found.
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(this.obj, VsaEngine.executeForJSEE);
|
|
if (ir != null)
|
|
return LateBinding.CallCOMObject(ir, this.name, this.obj, binder, arguments, modifiers, culture, namedParameters, construct, brackets, engine);
|
|
|
|
//If the object has a field or parameterless property called this.name, we have to try and call the value of that field or property
|
|
Object value = LateBinding.GetMemberValue(this.obj, this.name, this.last_member, this.last_members);
|
|
if (!(value is Missing))
|
|
return LateBinding.CallValue(value, arguments, construct, brackets, engine, this.obj, JSBinder.ob, null, null);
|
|
|
|
//Give up
|
|
if (brackets)
|
|
if (this.obj is IActivationObject)
|
|
throw new JScriptException(JSError.ObjectExpected);
|
|
else
|
|
throw new JScriptException(JSError.OLENoPropOrMethod);
|
|
else
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
private static Object CallCOMObject(IReflect ir, String name, Object ob, Binder binder, Object[] arguments,
|
|
ParameterModifier[] modifiers, CultureInfo culture, String[] namedParameters,
|
|
bool construct, bool brackets, VsaEngine engine){
|
|
try{
|
|
try{
|
|
LateBinding.Change64bitIntegersToDouble(arguments);
|
|
BindingFlags flags =
|
|
BindingFlags.Public|BindingFlags.Instance|BindingFlags.Static|BindingFlags.OptionalParamBinding;
|
|
if (construct)
|
|
return ir.InvokeMember(name, flags|BindingFlags.CreateInstance, binder, ob, arguments, modifiers, culture, namedParameters);
|
|
else if (brackets){
|
|
// Expression like ob.name[args]. First try to invoke as an indexed property. If that
|
|
// results in a ArgumentException, try to evaluate (ob.name) and then invoke
|
|
// the default indexed property on the result.
|
|
try {
|
|
return ir.InvokeMember(name, flags|BindingFlags.GetProperty|BindingFlags.GetField, binder, ob, arguments, modifiers, culture, namedParameters);
|
|
}catch(TargetInvocationException){
|
|
Object ob1 = ir.InvokeMember(name, flags|BindingFlags.GetProperty|BindingFlags.GetField, binder, ob, new Object[0], modifiers, culture, new String[0]);
|
|
return LateBinding.CallValue(ob1, arguments, construct, brackets, engine, ob1, binder, culture, namedParameters);
|
|
}
|
|
}else{
|
|
int length = arguments == null ? 0 : arguments.Length;
|
|
if (namedParameters != null && namedParameters.Length > 0 && (namedParameters[0].Equals("[DISPID=-613]") || namedParameters[0].Equals("this")))
|
|
length--;
|
|
flags |= length > 0
|
|
? BindingFlags.InvokeMethod | BindingFlags.GetProperty
|
|
: BindingFlags.InvokeMethod;
|
|
return ir.InvokeMember(name, flags, binder, ob, arguments, modifiers, culture, namedParameters);
|
|
}
|
|
}catch(MissingMemberException){
|
|
if (brackets) //ob["x"]. Tried ob.Item()["x"] but failed. So return undefined.
|
|
return null;
|
|
else //ob("x"). Tried ob.Item()("x") but failed. Complain that ob is not a callable thing.
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
}
|
|
}catch(JScriptException e){
|
|
if ((e.Number & 0xFFFF) == (int)JSError.FunctionExpected){
|
|
//try to find a method on System.Object
|
|
MemberInfo[] members = Typeob.Object.GetMember(name, BindingFlags.Public|BindingFlags.Instance);
|
|
if (members != null && members.Length > 0)
|
|
return LateBinding.CallOneOfTheMembers(members, arguments, construct, ob, binder, culture, namedParameters, engine);
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
private static Object CallMethod(MethodInfo method, Object[] arguments, Object thisob, Binder binder, CultureInfo culture, String[] namedParameters){
|
|
if (namedParameters != null && namedParameters.Length > 0)
|
|
if (arguments.Length >= namedParameters.Length)
|
|
arguments = JSBinder.ArrangeNamedArguments(method, arguments, namedParameters);
|
|
else
|
|
throw new JScriptException(JSError.MoreNamedParametersThanArguments);
|
|
Object[] newargs = LateBinding.LickArgumentsIntoShape(method.GetParameters(), arguments, binder, culture);
|
|
try{
|
|
Object result = method.Invoke(thisob, BindingFlags.SuppressChangeType, null, newargs, null);
|
|
if (newargs != arguments && newargs != null && arguments != null){
|
|
//Copy elements of newargs back to arguments, in case any of them were passed by reference
|
|
int n = arguments.Length;
|
|
int m = newargs.Length;
|
|
if (m < n) n = m;
|
|
for (int i = 0; i < n; i++) arguments[i] = newargs[i];
|
|
}
|
|
return result;
|
|
}catch(TargetException e){
|
|
ClassScope csc = thisob as ClassScope;
|
|
if (csc != null) return csc.FakeCallToTypeMethod(method, newargs, e);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static Object CallOneOfTheMembers(MemberInfo[] members, Object[] arguments, bool construct, Object thisob, Binder binder, CultureInfo culture, String[] namedParameters, VsaEngine engine){
|
|
//Note that SelectConstructor and SelectMethod take care of named parameters
|
|
if (construct){
|
|
ConstructorInfo cons = JSBinder.SelectConstructor(members, ref arguments, namedParameters);
|
|
if (cons != null){
|
|
if (cons.IsDefined(Typeob.JSFunctionAttribute, false)){
|
|
if (thisob is StackFrame) thisob = ((StackFrame)thisob).closureInstance;
|
|
int n = arguments.Length;
|
|
Object[] newArguments = new Object[n+1];
|
|
ArrayObject.Copy(arguments, 0, newArguments, 0, n);
|
|
newArguments[n] = thisob;
|
|
arguments = newArguments;
|
|
}
|
|
Object result = null;
|
|
JSConstructor jscons = cons as JSConstructor;
|
|
if (jscons != null)
|
|
result = jscons.Construct(thisob, LateBinding.LickArgumentsIntoShape(cons.GetParameters(), arguments, JSBinder.ob, culture));
|
|
else
|
|
result = cons.Invoke(BindingFlags.SuppressChangeType, null,
|
|
LateBinding.LickArgumentsIntoShape(cons.GetParameters(), arguments, JSBinder.ob, culture), null);
|
|
if (result is INeedEngine)
|
|
((INeedEngine)result).SetEngine(engine);
|
|
return result;
|
|
}
|
|
}else{
|
|
Object[] originalArguments = arguments;
|
|
MethodInfo meth = JSBinder.SelectMethod(members, ref arguments, namedParameters);
|
|
if (meth != null) {
|
|
if (meth is JSMethod )
|
|
return ((JSMethod)meth).Invoke(thisob, thisob, (BindingFlags)0, JSBinder.ob, arguments, null);
|
|
if (meth.IsDefined(Typeob.JSFunctionAttribute, false)){ //Dealing with a builtin function
|
|
if (!construct){
|
|
JSBuiltin biFunc = ((JSFunctionAttribute)meth.GetCustomAttributes(Typeob.JSFunctionAttribute, false)[0]).builtinFunction;
|
|
if (biFunc != 0){
|
|
IActivationObject iaob = thisob as IActivationObject;
|
|
if (iaob != null) thisob = iaob.GetDefaultThisObject();
|
|
return BuiltinFunction.QuickCall(arguments, thisob, biFunc, null, engine);
|
|
}
|
|
}
|
|
return LateBinding.CallValue(new BuiltinFunction(thisob, meth), arguments, construct, false, engine, thisob, JSBinder.ob, null, null);
|
|
}else{
|
|
Object[] args = LateBinding.LickArgumentsIntoShape(meth.GetParameters(), arguments, JSBinder.ob, culture);
|
|
if (thisob != null && !meth.DeclaringType.IsAssignableFrom(thisob.GetType())){
|
|
if (thisob is StringObject)
|
|
return meth.Invoke(((StringObject)thisob).value, BindingFlags.SuppressChangeType, null, args, null);
|
|
if (thisob is NumberObject)
|
|
return meth.Invoke(((NumberObject)thisob).value, BindingFlags.SuppressChangeType, null, args, null);
|
|
if (thisob is BooleanObject)
|
|
return meth.Invoke(((BooleanObject)thisob).value, BindingFlags.SuppressChangeType, null, args, null);
|
|
if (thisob is ArrayWrapper)
|
|
return meth.Invoke(((ArrayWrapper)thisob).value, BindingFlags.SuppressChangeType, null, args, null);
|
|
}
|
|
Object result = meth.Invoke(thisob, BindingFlags.SuppressChangeType, null, args, null);
|
|
if (args != originalArguments && arguments == originalArguments && args != null && arguments != null){
|
|
//Different because of optional parameters or param arrays. Update the original array in case there were call by reference parameters.
|
|
int n = arguments.Length;
|
|
int m = args.Length;
|
|
if (m < n) n = m;
|
|
for (int i = 0; i < n; i++) arguments[i] = args[i];
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
throw new MissingMemberException();
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public static Object CallValue(Object thisob, Object val, Object[] arguments, bool construct, bool brackets, VsaEngine engine){
|
|
try{
|
|
return LateBinding.CallValue(val, arguments, construct, brackets, engine, thisob, JSBinder.ob, null, null);
|
|
}catch(TargetInvocationException e){
|
|
throw e.InnerException;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public static Object CallValue2(Object val, Object thisob, Object[] arguments, bool construct, bool brackets, VsaEngine engine){
|
|
try{
|
|
return LateBinding.CallValue(val, arguments, construct, brackets, engine, thisob, JSBinder.ob, null, null);
|
|
}catch(TargetInvocationException e){
|
|
throw e.InnerException;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static Object CallValue(Object val, Object[] arguments, bool construct, bool brackets, VsaEngine engine, Object thisob,
|
|
Binder binder, CultureInfo culture, String[] namedParameters){
|
|
if (construct){
|
|
if (val is ScriptFunction){
|
|
ScriptFunction fun = (ScriptFunction)val;
|
|
if (!brackets) goto old_style_behavior;
|
|
Object propVal = propVal = fun[arguments];
|
|
if (propVal != null)
|
|
return CallValue(propVal, new Object[0], true, false, engine, thisob, binder, culture, namedParameters);
|
|
Type t = (Type)TypeExpression.PredefinedType[fun.name];
|
|
if (t != null){ //It is a function that could be used in a type expression
|
|
int n = arguments.Length;
|
|
int[] indices = new int[n];
|
|
n = 0;
|
|
foreach (Object arg in arguments){
|
|
if (arg is Int32){
|
|
indices[n++] = (int)arg;
|
|
continue;
|
|
}
|
|
IConvertible ic = Convert.GetIConvertible(arg);
|
|
if (ic == null || !Convert.IsPrimitiveNumericTypeCode(ic.GetTypeCode())) goto old_style_behavior;
|
|
double d = ic.ToDouble(null);
|
|
int i = (int)d;
|
|
if (d != (double)i) goto old_style_behavior;
|
|
indices[n++] = i;
|
|
}
|
|
return System.Array.CreateInstance(t, indices);
|
|
}
|
|
old_style_behavior:
|
|
FunctionObject func = fun as FunctionObject;
|
|
if (func != null){
|
|
return func.Construct(thisob as JSObject, arguments == null ? new Object[0] : arguments);
|
|
}else{
|
|
Object result = fun.Construct(arguments == null ? new Object[0] : arguments);
|
|
JSObject jsob = result as JSObject;
|
|
if (jsob != null) jsob.outer_class_instance = thisob as JSObject;
|
|
return result;
|
|
}
|
|
}else if (val is ClassScope){
|
|
if (brackets)
|
|
return System.Array.CreateInstance(Typeob.Object, LateBinding.ToIndices(arguments));
|
|
JSObject result = (JSObject)LateBinding.CallOneOfTheMembers(((ClassScope)val).constructors, arguments, construct,
|
|
thisob, binder, culture, namedParameters, engine);
|
|
result.noExpando = ((ClassScope)val).noExpando;
|
|
return result;
|
|
}else if (val is Type){
|
|
Type t = (Type)val;
|
|
if (brackets) //we have "new val[x, y, z]" where val is a Type, so create an array of val.
|
|
return System.Array.CreateInstance(t, LateBinding.ToIndices(arguments));
|
|
ConstructorInfo[] cons = t.GetConstructors();
|
|
Object result = null;
|
|
if (cons == null || cons.Length == 0) //Happens for interop
|
|
result = Activator.CreateInstance(t, (BindingFlags)0, JSBinder.ob, arguments, null);
|
|
else
|
|
result = LateBinding.CallOneOfTheMembers(cons, arguments, construct, thisob, binder, culture, namedParameters, engine);
|
|
if (result is INeedEngine)
|
|
((INeedEngine)result).SetEngine(engine);
|
|
return result;
|
|
}else if (val is TypedArray && brackets)
|
|
return System.Array.CreateInstance(Typeob.Object, LateBinding.ToIndices(arguments));
|
|
}
|
|
|
|
if (brackets){
|
|
ScriptObject scrob = val as ScriptObject;
|
|
if (scrob != null){
|
|
Object v = scrob[arguments];
|
|
if (construct) return LateBinding.CallValue(thisob, v, new Object[0], true, false, engine);
|
|
return v;
|
|
}
|
|
}else{
|
|
if (val is ScriptFunction){
|
|
if (thisob is IActivationObject)
|
|
thisob = ((IActivationObject)thisob).GetDefaultThisObject();
|
|
return ((ScriptFunction)val).Call(arguments == null ? new Object[0] : arguments, thisob, binder, culture);
|
|
}else if (val is Delegate)
|
|
return CallMethod(((Delegate)val).Method, arguments, thisob, binder, culture, namedParameters);
|
|
else if (val is MethodInfo)
|
|
return CallMethod((MethodInfo)val, arguments, thisob, binder, culture, namedParameters);
|
|
else if (val is Type && arguments.Length == 1)
|
|
return Convert.CoerceT(arguments[0], (Type)val, true);
|
|
else if (val is ClassScope){
|
|
if (arguments == null || arguments.Length != 1)
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
if (((ClassScope)val).HasInstance(arguments[0])) return arguments[0];
|
|
throw new InvalidCastException(null);
|
|
}else if (val is TypedArray && arguments.Length == 1)
|
|
return Convert.Coerce(arguments[0], val, true);
|
|
else if (val is ScriptObject)
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
else if (val is MemberInfo[])
|
|
return LateBinding.CallOneOfTheMembers((MemberInfo[])val, arguments, construct, thisob, binder, culture, namedParameters, engine);
|
|
}
|
|
|
|
if (val != null){
|
|
System.Array arr = val as System.Array;
|
|
if (arr != null){
|
|
if (arguments.Length != arr.Rank)
|
|
throw new JScriptException(JSError.IncorrectNumberOfIndices);
|
|
return arr.GetValue(LateBinding.ToIndices(arguments));
|
|
}
|
|
//See if val has a default indexed property that we can call
|
|
val = Convert.ToObject(val, engine);
|
|
ScriptObject scrob = val as ScriptObject;
|
|
if (scrob != null)
|
|
if (brackets)
|
|
return scrob[arguments];
|
|
else{
|
|
ScriptFunction sf = scrob as ScriptFunction;
|
|
if (sf != null){
|
|
IActivationObject iaob = thisob as IActivationObject;
|
|
if (iaob != null) thisob = iaob.GetDefaultThisObject();
|
|
return sf.Call(arguments == null ? new Object[0] : arguments, thisob, binder, culture);
|
|
}
|
|
throw new JScriptException(JSError.InvalidCall);
|
|
}
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(val, VsaEngine.executeForJSEE);
|
|
if (ir != null){
|
|
if (brackets){
|
|
String name = String.Empty;
|
|
int n = arguments.Length;
|
|
if (n > 0)
|
|
name = Convert.ToString(arguments[n-1]);
|
|
return CallCOMObject(ir, name, val, binder, null, null, culture, namedParameters, false, true, engine);
|
|
}
|
|
|
|
if (!(val is IReflect))
|
|
return CallCOMObject(ir, String.Empty, val, binder, arguments, null, culture, namedParameters, false, brackets, engine);
|
|
|
|
// Pass in the 'this' object as an additional named parameter
|
|
Object[] arguments2 = new Object[(arguments != null ? arguments.Length : 0) + 1];
|
|
arguments2[0] = thisob;
|
|
if (arguments != null)
|
|
ArrayObject.Copy(arguments, 0, arguments2, 1, arguments.Length);
|
|
String[] namedParameters2 = new String[(namedParameters != null ? namedParameters.Length : 0) + 1];
|
|
namedParameters2[0] = "[DISPID=-613]";
|
|
if (namedParameters != null)
|
|
ArrayObject.Copy(namedParameters, 0, namedParameters2, 1, namedParameters.Length);
|
|
return CallCOMObject(ir, "[DISPID=0]", val, binder, arguments2, null, culture, namedParameters2, false, brackets, engine);
|
|
}
|
|
|
|
|
|
MemberInfo[] defaultMembers = TypeReflector.GetTypeReflectorFor(val.GetType()).GetDefaultMembers();
|
|
if (defaultMembers != null && defaultMembers.Length > 0){
|
|
MethodInfo meth = JSBinder.SelectMethod(defaultMembers, ref arguments, namedParameters); //This also selects property getters
|
|
if (meth != null)
|
|
return LateBinding.CallMethod(meth, arguments, val, binder, culture, namedParameters);
|
|
}
|
|
}
|
|
|
|
throw new JScriptException(JSError.FunctionExpected);
|
|
}
|
|
|
|
private static void Change64bitIntegersToDouble(Object[] arguments){
|
|
if (arguments == null) return;
|
|
for (int i = 0, n = arguments.Length; i < n; i++){
|
|
Object val = arguments[i];
|
|
IConvertible ic = Convert.GetIConvertible(val);
|
|
TypeCode code = Convert.GetTypeCode(val, ic);
|
|
switch(code){
|
|
case TypeCode.Int64:
|
|
case TypeCode.UInt64:
|
|
arguments[i] = ic.ToDouble(null);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool Delete(){
|
|
return LateBinding.DeleteMember(this.obj, this.name);
|
|
}
|
|
|
|
public static bool DeleteMember(Object obj, String name){
|
|
if (name == null || obj == null) return false;
|
|
if (obj is ScriptObject)
|
|
return ((ScriptObject)obj).DeleteMember(name);
|
|
if (obj is IExpando)
|
|
try{
|
|
IExpando eob = (IExpando)obj;
|
|
MemberInfo[] members = eob.GetMember(name, BindingFlags.Instance|BindingFlags.Public);
|
|
MemberInfo member = LateBinding.SelectMember(members);
|
|
if (member != null){
|
|
eob.RemoveMember(member);
|
|
return true;
|
|
}
|
|
return false;
|
|
}catch{
|
|
return false;
|
|
}
|
|
if (obj is IDictionary){
|
|
IDictionary dict = (IDictionary)obj;
|
|
if (dict.Contains(name)){
|
|
dict.Remove(name);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
Type obType = obj.GetType();
|
|
MethodInfo deleteOp = TypeReflector.GetTypeReflectorFor(obType).GetMethod(
|
|
"op_Delete", BindingFlags.ExactBinding|BindingFlags.Public|BindingFlags.Static, null, new Type[]{obType, typeof(Object[])}, null);
|
|
if (deleteOp == null || (deleteOp.Attributes & MethodAttributes.SpecialName) == 0 || deleteOp.ReturnType != Typeob.Boolean)
|
|
return false; //This is in the spirit of the spec, but not backwards compatible with JS5. The latter throws an exception.
|
|
else
|
|
return (bool)deleteOp.Invoke(null, new Object[]{obj, new Object[]{name}});
|
|
}
|
|
|
|
internal static bool DeleteValueAtIndex(Object obj, ulong index){
|
|
if (obj is ArrayObject && index < UInt32.MaxValue)
|
|
return ((ArrayObject)obj).DeleteValueAtIndex((uint)index);
|
|
return LateBinding.DeleteMember(obj, index.ToString());
|
|
}
|
|
|
|
private static IReflect GetIRForObjectThatRequiresInvokeMember(Object obj, bool checkForDebugger){
|
|
return null;
|
|
}
|
|
|
|
private static IReflect GetIRForObjectThatRequiresInvokeMember(Object obj, bool checkForDebugger, TypeCode tcode){
|
|
return tcode != TypeCode.Object ? null : LateBinding.GetIRForObjectThatRequiresInvokeMember(obj, checkForDebugger);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static Object GetMemberValue(Object obj, String name){
|
|
if (obj is ScriptObject)
|
|
return ((ScriptObject)obj).GetMemberValue(name);
|
|
//Create a LateBinding instance so that we can reuse the ugly logic in BindToMember to get the members.
|
|
LateBinding lb = new LateBinding(name, obj);
|
|
return lb.GetNonMissingValue();
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static Object GetMemberValue2(Object obj, String name){
|
|
if (obj is ScriptObject)
|
|
return ((ScriptObject)obj).GetMemberValue(name);
|
|
//Create a LateBinding instance so that we can reuse the ugly logic in BindToMember to get the members.
|
|
LateBinding lb = new LateBinding(name, obj);
|
|
return lb.GetValue();
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static Object GetMemberValue(Object obj, String name, MemberInfo member, MemberInfo[] members){
|
|
if (member != null){
|
|
try{
|
|
switch(member.MemberType){
|
|
case MemberTypes.Field:
|
|
Object val = ((FieldInfo)member).GetValue(obj);
|
|
Type et = obj as Type;
|
|
if (et != null && et.IsEnum)
|
|
try{
|
|
val = Enum.ToObject(et, ((IConvertible)val).ToUInt64(null));
|
|
}catch{}
|
|
return val;
|
|
case MemberTypes.Property:
|
|
PropertyInfo prop = (PropertyInfo)member;
|
|
if (prop.DeclaringType == Typeob.ArrayObject){
|
|
ArrayObject aob = obj as ArrayObject;
|
|
if (aob != null) return aob.length;
|
|
}else if (prop.DeclaringType == Typeob.StringObject){
|
|
StringObject strob = obj as StringObject;
|
|
if (strob != null) return strob.length;
|
|
}
|
|
return JSProperty.GetValue(prop, obj, null);
|
|
case MemberTypes.Event: return null;
|
|
case MemberTypes.NestedType: return member;
|
|
}
|
|
}catch(Exception e){
|
|
if (obj is StringObject)
|
|
return LateBinding.GetMemberValue(((StringObject)obj).value, name, member, members);
|
|
if (obj is NumberObject)
|
|
return LateBinding.GetMemberValue(((NumberObject)obj).value, name, member, members);
|
|
if (obj is BooleanObject)
|
|
return LateBinding.GetMemberValue(((BooleanObject)obj).value, name, member, members);
|
|
if (obj is ArrayWrapper)
|
|
return LateBinding.GetMemberValue(((ArrayWrapper)obj).value, name, member, members);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
if (members != null && members.Length > 0){
|
|
//Special case check for methods on builtin objects
|
|
if (members.Length == 1 && members[0].MemberType == MemberTypes.Method){
|
|
MethodInfo meth = (MethodInfo)members[0];
|
|
Type dt = meth.DeclaringType;
|
|
if (dt == Typeob.GlobalObject ||
|
|
(dt != null && dt != Typeob.StringObject && dt != Typeob.NumberObject && dt != Typeob.BooleanObject && dt.IsSubclassOf(Typeob.JSObject))){
|
|
//This only happens in fast mode. We could add InitOnly fields to the fast predefined objects and initialize them
|
|
//with instances of BuiltinFunction objects, in which case we would never get here, but we would like to avoid
|
|
//the start up cost of allocating these objects, particularly if they end up never being used (the expected common case).
|
|
return Globals.BuiltinFunctionFor(obj, meth);
|
|
}
|
|
}
|
|
|
|
return new FunctionWrapper(name, obj, members);
|
|
}
|
|
|
|
//Check for expando field on ScriptObject, despite the fact that it did not show up in member.
|
|
//This could happen if the object is referred to in a loop and the expando was created after the first time the reference is encountered
|
|
//It is also need to get to the $ properties on RegExp
|
|
if (obj is ScriptObject)
|
|
return ((ScriptObject)obj).GetMemberValue(name);
|
|
|
|
|
|
//A namespace does not implement IReflect and hence cannot deliver a member for the cache. Check it now.
|
|
if (obj is Namespace){
|
|
Namespace ns = (Namespace)obj;
|
|
String qualid = ns.Name+"."+name;
|
|
Type type = ns.GetType(qualid);
|
|
if (type != null) return type;
|
|
return Namespace.GetNamespace(qualid, ns.engine);
|
|
}
|
|
|
|
//Handle COM objects and IExpando objects.
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(obj, true);
|
|
if (ir != null){
|
|
try{
|
|
const BindingFlags flags =
|
|
BindingFlags.Public|BindingFlags.Instance|BindingFlags.OptionalParamBinding|
|
|
BindingFlags.GetProperty|BindingFlags.GetField;
|
|
return ir.InvokeMember(name, flags, JSBinder.ob, obj, null, null, null, null);
|
|
}catch(MissingMemberException){
|
|
}
|
|
}
|
|
|
|
//If we get here the object does not have such a member
|
|
return Missing.Value;
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public Object GetNonMissingValue(){
|
|
Object result = this.GetValue();
|
|
if (result is Missing)
|
|
return null;
|
|
return result;
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal Object GetValue(){
|
|
this.BindToMember(); //Do a GetMember call and remember the result of the lookup for next time around
|
|
return LateBinding.GetMemberValue(this.obj, this.name, this.last_member, this.last_members);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public Object GetValue2(){
|
|
Object result = this.GetValue();
|
|
if (result == Missing.Value){
|
|
throw new JScriptException(JSError.UndefinedIdentifier, new Context(new DocumentContext("", null), this.name));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static Object GetValueAtIndex(Object obj, ulong index){
|
|
if (obj is ScriptObject)
|
|
if (index < UInt32.MaxValue)
|
|
return ((ScriptObject)obj).GetValueAtIndex((uint)index);
|
|
else
|
|
return ((ScriptObject)obj).GetMemberValue(index.ToString());
|
|
default_property_case:
|
|
if (obj is IList)
|
|
checked {return ((IList)obj)[(int)index];}
|
|
if (obj is Array)
|
|
checked {return ((Array)obj).GetValue((int)index);}
|
|
Type type = obj.GetType();
|
|
if (type.IsCOMObject || obj is IReflect || index > Int32.MaxValue)
|
|
return LateBinding.GetMemberValue(obj, index.ToString());
|
|
|
|
//Assume that the object does not itself have a field/property with numerical name.
|
|
//See if the object has a default indexed property, or default property that results in an Array or List and work with that.
|
|
MethodInfo getter = JSBinder.GetDefaultPropertyForArrayIndex(type, (int)index, null, false);
|
|
if (getter != null){
|
|
ParameterInfo[] pars = getter.GetParameters();
|
|
if (pars == null || pars.Length == 0){
|
|
obj = getter.Invoke(obj, BindingFlags.SuppressChangeType, null, null, null);
|
|
goto default_property_case;
|
|
}else
|
|
return getter.Invoke(obj, (BindingFlags)0, JSBinder.ob, new Object[]{(int)index}, null);
|
|
}
|
|
|
|
return Missing.Value;
|
|
}
|
|
|
|
//Do not call this method for arguments that are going out to COM2 objects via interop.
|
|
private static Object[] LickArgumentsIntoShape(ParameterInfo[] pars, Object[] arguments, Binder binder, CultureInfo culture){
|
|
if (arguments == null)
|
|
return null;
|
|
int formals = pars.Length;
|
|
if (formals == 0)
|
|
return null;
|
|
Object[] newArgs = arguments;
|
|
int actuals = arguments.Length;
|
|
if (actuals != formals)
|
|
newArgs = new Object[formals];
|
|
int m = formals-1; //Position of last formal param
|
|
int k = actuals < m ? actuals : m; //Number of formal params that have corresponding actual arguments, bar the last one, which may be a varArg array.
|
|
//Copy all actual args that match formal param, bar the last one. Change DBNull to null. Coerce actual value to formal param type.
|
|
for (int i = 0; i < k; i++){
|
|
Object arg = arguments[i];
|
|
if (arg is DBNull)
|
|
newArgs[i] = null; //Outside of the world of OLE Variants, undefined == null == the null pointer.
|
|
else
|
|
newArgs[i] = binder.ChangeType(arguments[i], pars[i].ParameterType, culture);
|
|
}
|
|
//Supply default values for all formal params, bar the last one, that do not have corresponding actual arguments
|
|
for (int i = k; i < m; i++){
|
|
Object dv = pars[i].DefaultValue;
|
|
if (dv == System.Convert.DBNull) //No default value was specified
|
|
dv = binder.ChangeType(null, pars[i].ParameterType, culture); //Substitute undefined
|
|
newArgs[i] = dv;
|
|
}
|
|
//If the last formal param is a vararg param, treat it specially.
|
|
if (pars[m].IsDefined(Typeob.ParamArrayAttribute, false)){
|
|
int numVarArgs = actuals - m;
|
|
if (numVarArgs < 0) numVarArgs = 0;
|
|
Type t = pars[m].ParameterType.GetElementType();
|
|
Array pa = Array.CreateInstance(t, numVarArgs);
|
|
for (int j = 0; j < numVarArgs; j++)
|
|
pa.SetValue(binder.ChangeType(arguments[j+m], t, culture), j);
|
|
newArgs[m] = pa;
|
|
}else if (actuals < formals){
|
|
Object dv = pars[m].DefaultValue;
|
|
if (dv == System.Convert.DBNull) //No default value was specified
|
|
dv = binder.ChangeType(null, pars[m].ParameterType, culture); //Substitute undefined
|
|
newArgs[m] = dv;
|
|
}else
|
|
newArgs[m] = binder.ChangeType(arguments[m], pars[m].ParameterType, culture);
|
|
return newArgs;
|
|
}
|
|
|
|
internal static MemberInfo SelectMember(MemberInfo[] mems){
|
|
if (mems == null) return null;
|
|
MemberInfo result = null;
|
|
foreach (MemberInfo mem in mems){
|
|
switch(mem.MemberType){
|
|
case MemberTypes.Field:
|
|
if (result == null || result.MemberType != MemberTypes.Field)
|
|
result = mem;
|
|
break;
|
|
case MemberTypes.Property:
|
|
if (result == null || (result.MemberType != MemberTypes.Field && result.MemberType != MemberTypes.Property)){
|
|
ParameterInfo[] pars = ((PropertyInfo)mem).GetIndexParameters();
|
|
if (pars != null && pars.Length == 0)
|
|
result = mem;
|
|
}
|
|
break;
|
|
case MemberTypes.TypeInfo:
|
|
case MemberTypes.NestedType:
|
|
if (result == null)
|
|
result = mem;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal void SetIndexedDefaultPropertyValue(Object ob, Object[] arguments, Object value){
|
|
ScriptObject scrob = ob as ScriptObject;
|
|
if (scrob != null){
|
|
scrob[arguments] = value;
|
|
return;
|
|
}
|
|
|
|
System.Array arr = ob as System.Array;
|
|
if (arr != null){
|
|
if (arguments.Length != arr.Rank)
|
|
throw new JScriptException(JSError.IncorrectNumberOfIndices);
|
|
arr.SetValue(value, LateBinding.ToIndices(arguments));
|
|
return;
|
|
}
|
|
|
|
TypeCode tcode = Convert.GetTypeCode(ob);
|
|
if (Convert.NeedsWrapper(tcode)) return; //senseless assignment to implicit wrapper. Do nothing.
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(ob, this.checkForDebugger, tcode);
|
|
|
|
if (ir != null){
|
|
try{
|
|
int n = arguments.Length + 1;
|
|
Object[] newargs = new Object[n];
|
|
ArrayObject.Copy(arguments, 0, newargs, 0, n-1);
|
|
newargs[n-1] = value;
|
|
const BindingFlags flags =
|
|
BindingFlags.Public|BindingFlags.Instance|BindingFlags.OptionalParamBinding|
|
|
BindingFlags.SetProperty|BindingFlags.SetField;
|
|
ir.InvokeMember(String.Empty, flags, JSBinder.ob, ob, newargs, null, null, null);
|
|
return;
|
|
}catch(MissingMemberException){
|
|
throw new JScriptException(JSError.OLENoPropOrMethod);
|
|
}
|
|
}
|
|
|
|
MemberInfo[] defaultMembers = TypeReflector.GetTypeReflectorFor(ob.GetType()).GetDefaultMembers();
|
|
if (defaultMembers != null && defaultMembers.Length > 0){
|
|
PropertyInfo prop = JSBinder.SelectProperty(defaultMembers, arguments);
|
|
if (prop != null){
|
|
MethodInfo setter = JSProperty.GetSetMethod(prop, false);
|
|
if (setter != null){
|
|
arguments = LateBinding.LickArgumentsIntoShape(prop.GetIndexParameters(), arguments, JSBinder.ob, null);
|
|
value = Convert.CoerceT(value, prop.PropertyType);
|
|
int n = arguments.Length + 1;
|
|
Object[] newargs = new Object[n];
|
|
ArrayObject.Copy(arguments, 0, newargs, 0, n-1);
|
|
newargs[n-1] = value;
|
|
setter.Invoke(ob, newargs);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
throw new JScriptException(JSError.OLENoPropOrMethod);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal void SetIndexedPropertyValue(Object[] arguments, Object value){
|
|
if (this.obj == null)
|
|
throw new JScriptException(JSError.ObjectExpected);
|
|
if (this.name == null){
|
|
this.SetIndexedDefaultPropertyValue(this.obj, arguments, value);
|
|
return;
|
|
}
|
|
MemberInfo member = this.BindToMember(); //Do a GetMember call and remember the result of the lookup for next time around
|
|
|
|
//If the object has any members called this.name, we try to find a suitable indexed property.
|
|
if (this.last_members != null && this.last_members.Length > 0){
|
|
PropertyInfo prop = JSBinder.SelectProperty(this.last_members, arguments);
|
|
if (prop != null){
|
|
if (arguments.Length > 0 && prop.GetIndexParameters().Length == 0){
|
|
//Might be a parameterless property that results in an object that has a indexed property that can be assigned to
|
|
MethodInfo getter = JSProperty.GetGetMethod(prop, false);
|
|
if (getter != null){
|
|
LateBinding.SetIndexedPropertyValueStatic(getter.Invoke(this.obj, null), arguments, value);
|
|
return;
|
|
}
|
|
}
|
|
arguments = LateBinding.LickArgumentsIntoShape(prop.GetIndexParameters(), arguments, JSBinder.ob, null);
|
|
value = Convert.CoerceT(value, prop.PropertyType);
|
|
JSProperty.SetValue(prop, this.obj, value, arguments);
|
|
return;
|
|
}
|
|
}
|
|
|
|
TypeCode tcode = Convert.GetTypeCode(obj);
|
|
if (Convert.NeedsWrapper(tcode)) return; //senseless assignment to implicit wrapper. Do nothing.
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(obj, this.checkForDebugger, tcode);
|
|
if (ir != null){
|
|
int n = arguments.Length + 1;
|
|
Object[] newargs = new Object[n];
|
|
ArrayObject.Copy(arguments, 0, newargs, 0, n-1);
|
|
newargs[n-1] = value;
|
|
const BindingFlags flags =
|
|
BindingFlags.Public|BindingFlags.Instance|BindingFlags.OptionalParamBinding|
|
|
BindingFlags.SetProperty|BindingFlags.SetField;
|
|
ir.InvokeMember(this.name, flags, JSBinder.ob, this.obj, newargs, null, null, null);
|
|
return;
|
|
}
|
|
|
|
//<ob.foo> might be an object that has a default indexed property
|
|
Object ob = this.GetValue();
|
|
if (ob != null && !(ob is Missing)){
|
|
this.SetIndexedDefaultPropertyValue(ob, arguments, value);
|
|
return;
|
|
}
|
|
|
|
//Give up
|
|
throw new JScriptException(JSError.OLENoPropOrMethod);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public static void SetIndexedPropertyValueStatic(Object obj, Object[] arguments, Object value){
|
|
if (obj == null)
|
|
throw new JScriptException(JSError.ObjectExpected);
|
|
|
|
ScriptObject scrob = obj as ScriptObject;
|
|
if (scrob != null){
|
|
scrob[arguments] = value;
|
|
return;
|
|
}
|
|
|
|
Array arr = obj as Array;
|
|
if (arr != null){
|
|
if (arguments.Length != arr.Rank)
|
|
throw new JScriptException(JSError.IncorrectNumberOfIndices);
|
|
arr.SetValue(value, LateBinding.ToIndices(arguments));
|
|
return;
|
|
}
|
|
|
|
TypeCode tcode = Convert.GetTypeCode(obj);
|
|
if (Convert.NeedsWrapper(tcode)) return; //senseless assignment to implicit wrapper. Do nothing.
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(obj, true, tcode); //Either called from IL, or on an type object
|
|
if (ir != null){
|
|
//Legacy behavior is to convert the last argument to a string and treat it as the name of the property being set
|
|
String name = String.Empty;
|
|
int n = arguments.Length;
|
|
if (n > 0)
|
|
name = Convert.ToString(arguments[n-1]);
|
|
const BindingFlags flags =
|
|
BindingFlags.Public|BindingFlags.Instance|BindingFlags.OptionalParamBinding|
|
|
BindingFlags.SetProperty|BindingFlags.SetField;
|
|
ir.InvokeMember(name, flags, JSBinder.ob, obj, new Object[]{value}, null, null, null);
|
|
return;
|
|
}
|
|
|
|
MemberInfo[] defaultMembers = TypeReflector.GetTypeReflectorFor(obj.GetType()).GetDefaultMembers();
|
|
if (defaultMembers != null && defaultMembers.Length > 0){
|
|
PropertyInfo prop = JSBinder.SelectProperty(defaultMembers, arguments);
|
|
if (prop != null){
|
|
MethodInfo setter = JSProperty.GetSetMethod(prop, false);
|
|
if (setter != null){
|
|
arguments = LateBinding.LickArgumentsIntoShape(prop.GetIndexParameters(), arguments, JSBinder.ob, null);
|
|
value = Convert.CoerceT(value, prop.PropertyType);
|
|
int n = arguments.Length + 1;
|
|
Object[] newargs = new Object[n];
|
|
ArrayObject.Copy(arguments, 0, newargs, 0, n-1);
|
|
newargs[n-1] = value;
|
|
setter.Invoke(obj, newargs);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new JScriptException(JSError.OLENoPropOrMethod);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
private static void SetMember(Object obj, Object value, MemberInfo member){
|
|
switch(member.MemberType){
|
|
case MemberTypes.Field:
|
|
FieldInfo field = (FieldInfo)member;
|
|
if (!field.IsLiteral && !field.IsInitOnly){
|
|
if (field is JSField)
|
|
field.SetValue(obj, value);
|
|
else
|
|
field.SetValue(obj, Convert.CoerceT(value, field.FieldType), BindingFlags.SuppressChangeType, null, null);
|
|
}
|
|
return;
|
|
case MemberTypes.Property:
|
|
PropertyInfo property = (PropertyInfo)member;
|
|
if (property is JSProperty || property is JSWrappedProperty){
|
|
property.SetValue(obj, value, null);
|
|
return;
|
|
}
|
|
MethodInfo setMeth = JSProperty.GetSetMethod(property, false);
|
|
if (setMeth != null)
|
|
try{
|
|
setMeth.Invoke(obj, BindingFlags.SuppressChangeType, null, new Object[]{Convert.CoerceT(value, property.PropertyType)}, null);
|
|
}catch(TargetInvocationException e){
|
|
throw e.InnerException;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static void SetMemberValue(Object obj, String name, Object value){
|
|
if (obj is ScriptObject){
|
|
((ScriptObject)obj).SetMemberValue(name, value);
|
|
return;
|
|
}
|
|
LateBinding lb = new LateBinding(name, obj);
|
|
lb.SetValue(value);
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static void SetMemberValue(Object obj, String name, Object value, MemberInfo member, MemberInfo[] members){
|
|
if (member != null){
|
|
LateBinding.SetMember(obj, value, member);
|
|
return;
|
|
}
|
|
if (obj is ScriptObject){ //Create an expando the fast way. Also needed for $ properties on RegExp
|
|
((ScriptObject)obj).SetMemberValue(name, value);
|
|
return;
|
|
}
|
|
|
|
TypeCode tcode = Convert.GetTypeCode(obj);
|
|
if (Convert.NeedsWrapper(tcode)) return; //senseless assignment to implicit wrapper. Do nothing.
|
|
IReflect ir = LateBinding.GetIRForObjectThatRequiresInvokeMember(obj, true, tcode);
|
|
if (ir != null){
|
|
try{
|
|
Object[] args = new Object[]{value};
|
|
IConvertible iconv = value as IConvertible;
|
|
|
|
//BindingFlags.SetProperty makes interop call with DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF .
|
|
BindingFlags flags = BindingFlags.Public|BindingFlags.Instance|BindingFlags.OptionalParamBinding|
|
|
BindingFlags.SetProperty|BindingFlags.SetField;
|
|
ir.InvokeMember(name, flags, JSBinder.ob, obj, args, null, null, null);
|
|
return;
|
|
}catch(MissingMemberException){
|
|
}
|
|
}
|
|
|
|
if (obj is IExpando){ //Create an expando the slow way
|
|
PropertyInfo prop = ((IExpando)obj).AddProperty(name);
|
|
if (prop != null)
|
|
prop.SetValue(obj, value, null);
|
|
else{
|
|
FieldInfo field = ((IExpando)obj).AddField(name);
|
|
if (field != null){
|
|
field.SetValue(obj, value);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
internal static void SetValueAtIndex(Object obj, ulong index, Object value){
|
|
if (obj is ScriptObject){
|
|
if (index < UInt32.MaxValue)
|
|
((ScriptObject)obj).SetValueAtIndex((uint)index, value);
|
|
else
|
|
((ScriptObject)obj).SetMemberValue(index.ToString(), value);
|
|
return;
|
|
}
|
|
default_property_case:
|
|
if (obj is IList){
|
|
IList list = (IList)obj;
|
|
if (index < (ulong)list.Count)
|
|
list[(int)index] = value;
|
|
else
|
|
list.Insert((int)index, value);
|
|
return;
|
|
}
|
|
if (obj is Array){
|
|
checked{((Array)obj).SetValue(Convert.CoerceT(value, obj.GetType().GetElementType()), (int)index);}
|
|
return;
|
|
}
|
|
Type type = obj.GetType();
|
|
if (type.IsCOMObject || obj is IReflect || index > Int32.MaxValue){
|
|
LateBinding.SetMemberValue(obj, index.ToString(), value);
|
|
return;
|
|
}
|
|
|
|
//Assume that the object does not itself have a field/property with numerical name.
|
|
//See if the object has a default indexed property, or default property that results in an Array or List and work with that.
|
|
MethodInfo setter = JSBinder.GetDefaultPropertyForArrayIndex(type, (int)index, null, true);
|
|
if (setter != null){
|
|
ParameterInfo[] pars = setter.GetParameters();
|
|
if (pars == null || pars.Length == 0){
|
|
obj = setter.Invoke(obj, BindingFlags.SuppressChangeType, null, null, null);
|
|
goto default_property_case;
|
|
}else
|
|
setter.Invoke(obj, (BindingFlags)0, JSBinder.ob, new Object[]{(int)index, value}, null);
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public void SetValue(Object value){
|
|
this.BindToMember(); //Do a GetMember call and remember the result of the lookup for next time around
|
|
LateBinding.SetMemberValue(this.obj, this.name, value, this.last_member, this.last_members);
|
|
}
|
|
|
|
internal static void SwapValues(Object obj, uint left, uint right){
|
|
if (obj is JSObject){
|
|
((JSObject)obj).SwapValues(left, right);
|
|
return;
|
|
}
|
|
if (obj is IList){
|
|
IList list = (IList)obj;
|
|
Object temp = list[(int)left];
|
|
list[(int)left] = list[(int)right];
|
|
list[(int)right] = temp;
|
|
return;
|
|
}
|
|
if (obj is Array){
|
|
Array arr = (Array)obj;
|
|
Object temp = arr.GetValue((int)left);
|
|
arr.SetValue(arr.GetValue((int)right), (int)left);
|
|
arr.SetValue(temp, (int)right);
|
|
return;
|
|
}
|
|
if (obj is IExpando){
|
|
String left_name = System.Convert.ToString(left);
|
|
String right_name = System.Convert.ToString(right);
|
|
IExpando eob = (IExpando)obj;
|
|
FieldInfo left_field = eob.GetField(left_name, BindingFlags.Instance|BindingFlags.Public);
|
|
FieldInfo right_field = eob.GetField(right_name, BindingFlags.Instance|BindingFlags.Public);
|
|
if (left_field == null)
|
|
if (right_field == null)
|
|
return;
|
|
else
|
|
try{
|
|
left_field = eob.AddField(left_name);
|
|
left_field.SetValue(obj, right_field.GetValue(obj));
|
|
eob.RemoveMember(right_field);
|
|
}catch{
|
|
throw new JScriptException(JSError.ActionNotSupported);
|
|
}
|
|
else if (right_field == null)
|
|
try{
|
|
right_field = eob.AddField(right_name);
|
|
right_field.SetValue(obj, left_field.GetValue(obj));
|
|
eob.RemoveMember(left_field);
|
|
}catch{
|
|
throw new JScriptException(JSError.ActionNotSupported);
|
|
}
|
|
Object temp = left_field.GetValue(obj);
|
|
left_field.SetValue(obj, right_field.GetValue(obj));
|
|
right_field.SetValue(obj, temp);
|
|
return;
|
|
}
|
|
Object lval = LateBinding.GetValueAtIndex(obj, left);
|
|
Object rval = LateBinding.GetValueAtIndex(obj, right);
|
|
if (lval is Missing)
|
|
LateBinding.DeleteValueAtIndex(obj, right);
|
|
else
|
|
LateBinding.SetValueAtIndex(obj, right, lval);
|
|
if (rval is Missing)
|
|
LateBinding.DeleteValueAtIndex(obj, left);
|
|
else
|
|
LateBinding.SetValueAtIndex(obj, left, rval);
|
|
}
|
|
|
|
private static int[] ToIndices(Object[] arguments){
|
|
int n = arguments.Length;
|
|
int[] result = new int[n];
|
|
for (int i = 0; i < n; i++)
|
|
result[i] = Convert.ToInt32(arguments[i]);
|
|
return result;
|
|
}
|
|
}
|
|
}
|