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.
412 lines
16 KiB
C#
412 lines
16 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.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Diagnostics;
|
|
|
|
internal enum PostOrPrefix{PostfixDecrement, PostfixIncrement, PrefixDecrement, PrefixIncrement};
|
|
|
|
public class PostOrPrefixOperator : UnaryOp{
|
|
private MethodInfo operatorMeth;
|
|
private PostOrPrefix operatorTok;
|
|
private Object metaData;
|
|
private Type type;
|
|
|
|
internal PostOrPrefixOperator(Context context, AST operand)
|
|
: base(context, operand) {
|
|
}
|
|
|
|
internal PostOrPrefixOperator(Context context, AST operand, PostOrPrefix operatorTok)
|
|
: base(context, operand) {
|
|
this.operatorMeth = null;
|
|
this.operatorTok = operatorTok;
|
|
this.metaData = null;
|
|
this.type = null;
|
|
}
|
|
|
|
public PostOrPrefixOperator(int operatorTok)
|
|
: this(null, null, (PostOrPrefix)operatorTok){
|
|
}
|
|
|
|
private Object DoOp(int i){
|
|
try{
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixIncrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
checked {return i+1;}
|
|
default:
|
|
checked {return i-1;}
|
|
}
|
|
}catch{
|
|
return this.DoOp((double)i);
|
|
}
|
|
}
|
|
|
|
private Object DoOp(uint i){
|
|
try{
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixIncrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
checked {return i+1;}
|
|
default:
|
|
checked {return i-1;}
|
|
}
|
|
}catch{
|
|
return this.DoOp((double)i);
|
|
}
|
|
}
|
|
|
|
private Object DoOp(long i){
|
|
try{
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixIncrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
checked {return i+1;}
|
|
default:
|
|
checked {return i-1;}
|
|
}
|
|
}catch{
|
|
return this.DoOp((double)i);
|
|
}
|
|
}
|
|
|
|
private Object DoOp(ulong i){
|
|
try{
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixIncrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
checked {return i+1;}
|
|
default:
|
|
checked {return i-1;}
|
|
}
|
|
}catch{
|
|
return this.DoOp((double)i);
|
|
}
|
|
}
|
|
|
|
private Object DoOp(double d){
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixIncrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
return d+1;
|
|
default:
|
|
return d-1;
|
|
}
|
|
}
|
|
|
|
internal override Object Evaluate(){
|
|
try{
|
|
Object oldval = this.operand.Evaluate();
|
|
Object newval = this.EvaluatePostOrPrefix(ref oldval);
|
|
this.operand.SetValue(newval);
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixDecrement:
|
|
case PostOrPrefix.PostfixIncrement:
|
|
return oldval;
|
|
case PostOrPrefix.PrefixDecrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
return newval;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
}catch(JScriptException e){
|
|
if (e.context == null)
|
|
e.context = this.context;
|
|
throw e;
|
|
}catch(Exception e){
|
|
throw new JScriptException(e, this.context);
|
|
}
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public Object EvaluatePostOrPrefix(ref Object v){
|
|
int i; uint ui; long l; ulong ul; double d;
|
|
IConvertible ic = Convert.GetIConvertible(v);
|
|
switch (Convert.GetTypeCode(v, ic)){
|
|
case TypeCode.Empty: v = Double.NaN; return v;
|
|
case TypeCode.DBNull: v = 0; return this.DoOp(0);
|
|
case TypeCode.Boolean:
|
|
case TypeCode.SByte:
|
|
case TypeCode.Byte:
|
|
case TypeCode.Int16:
|
|
case TypeCode.UInt16:
|
|
case TypeCode.Int32: v = i = ic.ToInt32(null); return this.DoOp(i);
|
|
case TypeCode.Char: i = ic.ToInt32(null); return ((IConvertible)this.DoOp(i)).ToChar(null);
|
|
case TypeCode.UInt32: v = ui = ic.ToUInt32(null); return this.DoOp(ui);
|
|
case TypeCode.Int64: v = l = ic.ToInt64(null); return this.DoOp(l);
|
|
case TypeCode.UInt64: v = ul = ic.ToUInt64(null); return this.DoOp(ul);
|
|
case TypeCode.Single:
|
|
case TypeCode.Double: v = d = ic.ToDouble(null); return this.DoOp(d);
|
|
}
|
|
MethodInfo oper = this.GetOperator(v.GetType());
|
|
if (oper != null)
|
|
return oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v}, null);
|
|
else{
|
|
v = d = Convert.ToNumber(v, ic);
|
|
return this.DoOp(d);
|
|
}
|
|
}
|
|
|
|
private MethodInfo GetOperator(IReflect ir){
|
|
Type t = ir is Type ? (Type)ir : Typeob.Object;
|
|
if (this.type == t)
|
|
return this.operatorMeth;
|
|
this.type = t;
|
|
if (Convert.IsPrimitiveNumericType(t) || Typeob.JSObject.IsAssignableFrom(t)){
|
|
this.operatorMeth = null;
|
|
return null;
|
|
}
|
|
switch (this.operatorTok){
|
|
case PostOrPrefix.PostfixDecrement:
|
|
case PostOrPrefix.PrefixDecrement:
|
|
this.operatorMeth = t.GetMethod("op_Decrement", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
|
|
case PostOrPrefix.PostfixIncrement:
|
|
case PostOrPrefix.PrefixIncrement:
|
|
this.operatorMeth = t.GetMethod("op_Increment", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
if (this.operatorMeth != null &&
|
|
(!this.operatorMeth.IsStatic || (operatorMeth.Attributes & MethodAttributes.SpecialName) == (MethodAttributes)0 || operatorMeth.GetParameters().Length != 1))
|
|
this.operatorMeth = null;
|
|
if (this.operatorMeth != null)
|
|
this.operatorMeth = new JSMethodInfo(this.operatorMeth);
|
|
return this.operatorMeth;
|
|
}
|
|
|
|
internal override IReflect InferType(JSField inference_target){
|
|
MethodInfo oper;
|
|
if (this.type == null || inference_target != null){
|
|
oper = this.GetOperator(this.operand.InferType(inference_target));
|
|
}else
|
|
oper = this.GetOperator(this.type);
|
|
if (oper != null){
|
|
this.metaData = oper;
|
|
return oper.ReturnType;
|
|
}
|
|
if (Convert.IsPrimitiveNumericType(this.type))
|
|
return this.type;
|
|
else if (this.type == Typeob.Char)
|
|
return this.type;
|
|
else if (Typeob.JSObject.IsAssignableFrom(this.type))
|
|
return Typeob.Double;
|
|
else
|
|
return Typeob.Object;
|
|
}
|
|
|
|
internal override AST PartiallyEvaluate(){
|
|
this.operand = this.operand.PartiallyEvaluateAsReference();
|
|
this.operand.SetPartialValue(this);
|
|
return this;
|
|
}
|
|
|
|
private void TranslateToILForNoOverloadCase(ILGenerator il, Type rtype){
|
|
Type type = Convert.ToType(this.operand.InferType(null));
|
|
this.operand.TranslateToILPreSetPlusGet(il);
|
|
if (rtype == Typeob.Void){
|
|
Type rt = Typeob.Double;
|
|
if (Convert.IsPrimitiveNumericType(type))
|
|
if (type == Typeob.SByte || type == Typeob.Int16)
|
|
rt = Typeob.Int32;
|
|
else if (type == Typeob.Byte || type == Typeob.UInt16 || type == Typeob.Char)
|
|
rt = Typeob.UInt32;
|
|
else
|
|
rt = type;
|
|
Convert.Emit(this, il, type, rt);
|
|
il.Emit(OpCodes.Ldc_I4_1);
|
|
Convert.Emit(this, il, Typeob.Int32, rt);
|
|
if (rt == Typeob.Double || rt == Typeob.Single){
|
|
if (this.operatorTok == PostOrPrefix.PostfixDecrement || this.operatorTok == PostOrPrefix.PrefixDecrement){
|
|
il.Emit(OpCodes.Sub);
|
|
}else{
|
|
il.Emit(OpCodes.Add);
|
|
}
|
|
}else if (rt == Typeob.Int32 || rt == Typeob.Int64){
|
|
if (this.operatorTok == PostOrPrefix.PostfixDecrement || this.operatorTok == PostOrPrefix.PrefixDecrement){
|
|
il.Emit(OpCodes.Sub_Ovf);
|
|
}else{
|
|
il.Emit(OpCodes.Add_Ovf);
|
|
}
|
|
}else{
|
|
if (this.operatorTok == PostOrPrefix.PostfixDecrement || this.operatorTok == PostOrPrefix.PrefixDecrement){
|
|
il.Emit(OpCodes.Sub_Ovf_Un);
|
|
}else{
|
|
il.Emit(OpCodes.Add_Ovf_Un);
|
|
}
|
|
}
|
|
Convert.Emit(this, il, rt, type);
|
|
this.operand.TranslateToILSet(il);
|
|
}else{
|
|
//set rt to be the smallest type that is precise enough for the result and the variable
|
|
Type rt = Typeob.Double;
|
|
if (Convert.IsPrimitiveNumericType(rtype) && Convert.IsPromotableTo(type, rtype))
|
|
rt = rtype;
|
|
else if (Convert.IsPrimitiveNumericType(type) && Convert.IsPromotableTo(rtype, type))
|
|
rt = type;
|
|
if (rt == Typeob.SByte || rt == Typeob.Int16)
|
|
rt = Typeob.Int32;
|
|
else if (rt == Typeob.Byte || rt == Typeob.UInt16 || rt == Typeob.Char)
|
|
rt = Typeob.UInt32;
|
|
LocalBuilder result = il.DeclareLocal(rtype);
|
|
Convert.Emit(this, il, type, rt);
|
|
if (this.operatorTok == PostOrPrefix.PostfixDecrement){
|
|
il.Emit(OpCodes.Dup);
|
|
if (type == Typeob.Char){
|
|
Convert.Emit(this, il, rt, Typeob.Char);
|
|
Convert.Emit(this, il, Typeob.Char, rtype);
|
|
}else
|
|
Convert.Emit(this, il, rt, rtype);
|
|
il.Emit(OpCodes.Stloc, result);
|
|
il.Emit(OpCodes.Ldc_I4_1);
|
|
Convert.Emit(this, il, Typeob.Int32, rt);
|
|
if (rt == Typeob.Double || rt == Typeob.Single)
|
|
il.Emit(OpCodes.Sub);
|
|
else if (rt == Typeob.Int32 || rt == Typeob.Int64)
|
|
il.Emit(OpCodes.Sub_Ovf);
|
|
else
|
|
il.Emit(OpCodes.Sub_Ovf_Un);
|
|
}else if (this.operatorTok == PostOrPrefix.PostfixIncrement){
|
|
il.Emit(OpCodes.Dup);
|
|
if (type == Typeob.Char){
|
|
Convert.Emit(this, il, rt, Typeob.Char);
|
|
Convert.Emit(this, il, Typeob.Char, rtype);
|
|
}else
|
|
Convert.Emit(this, il, rt, rtype);
|
|
il.Emit(OpCodes.Stloc, result);
|
|
il.Emit(OpCodes.Ldc_I4_1);
|
|
Convert.Emit(this, il, Typeob.Int32, rt);
|
|
if (rt == Typeob.Double || rt == Typeob.Single)
|
|
il.Emit(OpCodes.Add);
|
|
else if (rt == Typeob.Int32 || rt == Typeob.Int64)
|
|
il.Emit(OpCodes.Add_Ovf);
|
|
else
|
|
il.Emit(OpCodes.Add_Ovf_Un);
|
|
}else if (this.operatorTok == PostOrPrefix.PrefixDecrement){
|
|
il.Emit(OpCodes.Ldc_I4_1);
|
|
Convert.Emit(this, il, Typeob.Int32, rt);
|
|
if (rt == Typeob.Double || rt == Typeob.Single)
|
|
il.Emit(OpCodes.Sub);
|
|
else if (rt == Typeob.Int32 || rt == Typeob.Int64)
|
|
il.Emit(OpCodes.Sub_Ovf);
|
|
else
|
|
il.Emit(OpCodes.Sub_Ovf_Un);
|
|
il.Emit(OpCodes.Dup);
|
|
if (type == Typeob.Char){
|
|
Convert.Emit(this, il, rt, Typeob.Char);
|
|
Convert.Emit(this, il, Typeob.Char, rtype);
|
|
} else
|
|
Convert.Emit(this, il, rt, rtype);
|
|
il.Emit(OpCodes.Stloc, result);
|
|
}else{ //if (this.operatorTok == PostOrPrefix.PrefixIncrement)
|
|
il.Emit(OpCodes.Ldc_I4_1);
|
|
Convert.Emit(this, il, Typeob.Int32, rt);
|
|
if (rt == Typeob.Double || rt == Typeob.Single)
|
|
il.Emit(OpCodes.Add);
|
|
else if (rt == Typeob.Int32 || rt == Typeob.Int64)
|
|
il.Emit(OpCodes.Add_Ovf);
|
|
else
|
|
il.Emit(OpCodes.Add_Ovf_Un);
|
|
il.Emit(OpCodes.Dup);
|
|
if (type == Typeob.Char){
|
|
Convert.Emit(this, il, rt, Typeob.Char);
|
|
Convert.Emit(this, il, Typeob.Char, rtype);
|
|
} else
|
|
Convert.Emit(this, il, rt, rtype);
|
|
il.Emit(OpCodes.Stloc, result);
|
|
}
|
|
Convert.Emit(this, il, rt, type);
|
|
this.operand.TranslateToILSet(il);
|
|
il.Emit(OpCodes.Ldloc, result);
|
|
}
|
|
}
|
|
|
|
internal override void TranslateToIL(ILGenerator il, Type rtype){
|
|
if (this.metaData == null){
|
|
TranslateToILForNoOverloadCase(il, rtype);
|
|
return;
|
|
}
|
|
if (this.metaData is MethodInfo){
|
|
Object result = null;
|
|
Type type = Convert.ToType(this.operand.InferType(null));
|
|
this.operand.TranslateToILPreSetPlusGet(il);
|
|
if (rtype != Typeob.Void){
|
|
result = il.DeclareLocal(rtype);
|
|
if (this.operatorTok == PostOrPrefix.PostfixDecrement || this.operatorTok == PostOrPrefix.PostfixIncrement){
|
|
il.Emit(OpCodes.Dup);
|
|
Convert.Emit(this, il, type, rtype);
|
|
il.Emit(OpCodes.Stloc, (LocalBuilder)result);
|
|
}
|
|
}
|
|
MethodInfo oper = (MethodInfo)this.metaData;
|
|
ParameterInfo[] pars = oper.GetParameters();
|
|
Convert.Emit(this, il, type, pars[0].ParameterType);
|
|
il.Emit(OpCodes.Call, oper);
|
|
if (rtype != Typeob.Void){
|
|
if (this.operatorTok == PostOrPrefix.PrefixDecrement || this.operatorTok == PostOrPrefix.PrefixIncrement){
|
|
il.Emit(OpCodes.Dup);
|
|
Convert.Emit(this, il, type, rtype);
|
|
il.Emit(OpCodes.Stloc, (LocalBuilder)result);
|
|
}
|
|
}
|
|
Convert.Emit(this, il, oper.ReturnType, type);
|
|
this.operand.TranslateToILSet(il);
|
|
if (rtype != Typeob.Void)
|
|
il.Emit(OpCodes.Ldloc, (LocalBuilder)result);
|
|
}else{
|
|
//Getting here is just too bad. We do not know until the code runs whether or not to call an overloaded operator method.
|
|
//Compile operands to objects and devolve the decision making to run time thunks
|
|
Type type = Convert.ToType(this.operand.InferType(null));
|
|
LocalBuilder result = il.DeclareLocal(Typeob.Object);
|
|
this.operand.TranslateToILPreSetPlusGet(il);
|
|
Convert.Emit(this, il, type, Typeob.Object);
|
|
il.Emit(OpCodes.Stloc, result);
|
|
il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
|
|
il.Emit(OpCodes.Ldloca, result);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.evaluatePostOrPrefixOperatorMethod);
|
|
if (rtype != Typeob.Void){
|
|
if (this.operatorTok == PostOrPrefix.PrefixDecrement || this.operatorTok == PostOrPrefix.PrefixIncrement){
|
|
il.Emit(OpCodes.Dup);
|
|
il.Emit(OpCodes.Stloc, result);
|
|
}
|
|
}
|
|
Convert.Emit(this, il, Typeob.Object, type);
|
|
this.operand.TranslateToILSet(il);
|
|
if (rtype != Typeob.Void){
|
|
il.Emit(OpCodes.Ldloc, result);
|
|
Convert.Emit(this, il, Typeob.Object, rtype);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void TranslateToILInitializer(ILGenerator il){
|
|
IReflect rtype = this.InferType(null);
|
|
this.operand.TranslateToILInitializer(il);
|
|
if (rtype != Typeob.Object)
|
|
return;
|
|
this.metaData = il.DeclareLocal(typeof(PostOrPrefixOperator));
|
|
ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
|
|
il.Emit(OpCodes.Newobj, CompilerGlobals.postOrPrefixConstructor);
|
|
il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
|
|
}
|
|
}
|
|
}
|