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

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