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.
300 lines
12 KiB
C#
300 lines
12 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;
|
|
|
|
public sealed class NumericUnary : UnaryOp{
|
|
private Object metaData = null;
|
|
private JSToken operatorTok;
|
|
private MethodInfo operatorMeth;
|
|
private Type type;
|
|
|
|
internal NumericUnary(Context context, AST operand, JSToken operatorTok)
|
|
: base(context, operand){
|
|
this.operatorTok = operatorTok;
|
|
this.operatorMeth = null;
|
|
this.type = null;
|
|
}
|
|
|
|
public NumericUnary(int operatorTok)
|
|
: this(null, null, (JSToken)operatorTok){
|
|
}
|
|
|
|
internal override Object Evaluate(){
|
|
return this.EvaluateUnary(this.operand.Evaluate());
|
|
}
|
|
|
|
#if !DEBUG
|
|
[DebuggerStepThroughAttribute]
|
|
[DebuggerHiddenAttribute]
|
|
#endif
|
|
public Object EvaluateUnary(Object v){
|
|
IConvertible ic = Convert.GetIConvertible(v);
|
|
switch(Convert.GetTypeCode(v, ic)){
|
|
case TypeCode.Empty: return this.EvaluateUnary(Double.NaN);
|
|
case TypeCode.DBNull: return this.EvaluateUnary(0);
|
|
case TypeCode.Boolean: return this.EvaluateUnary(ic.ToBoolean(null) ? 1 : 0);
|
|
case TypeCode.Char: return this.EvaluateUnary((int)ic.ToChar(null));
|
|
|
|
case TypeCode.SByte:
|
|
case TypeCode.Byte:
|
|
case TypeCode.Int16:
|
|
case TypeCode.UInt16:
|
|
case TypeCode.Int32:
|
|
int i = ic.ToInt32(null);
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
return ~i;
|
|
case JSToken.LogicalNot:
|
|
return i == 0;
|
|
case JSToken.Minus:
|
|
if (i == 0) return -(double)i;
|
|
return -i;
|
|
case JSToken.Plus:
|
|
return i;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
|
|
case TypeCode.UInt32:
|
|
uint ui = ic.ToUInt32(null);
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
return ~ui;
|
|
case JSToken.LogicalNot:
|
|
return ui == 0;
|
|
case JSToken.Minus:
|
|
if (ui != 0 && ui <= Int32.MaxValue)
|
|
return -(int)ui;
|
|
else
|
|
return -(double)ui;
|
|
case JSToken.Plus:
|
|
return ui;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
|
|
case TypeCode.Int64:
|
|
long l = ic.ToInt64(null);
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
return ~l;
|
|
case JSToken.LogicalNot:
|
|
return l == 0;
|
|
case JSToken.Minus:
|
|
if (l == 0) return -(double)l;
|
|
return -l;
|
|
case JSToken.Plus:
|
|
return l;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
|
|
case TypeCode.UInt64:
|
|
ulong ul = ic.ToUInt64(null);
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
return ~ul;
|
|
case JSToken.LogicalNot:
|
|
return ul == 0;
|
|
case JSToken.Minus:
|
|
if (ul != -0 && ul < Int64.MaxValue)
|
|
return -(long)ul;
|
|
else
|
|
return -(double)ul;
|
|
case JSToken.Plus:
|
|
return ul;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
|
|
case TypeCode.Single:
|
|
case TypeCode.Double:
|
|
double d = ic.ToDouble(null);
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
return ~(int)d;
|
|
case JSToken.LogicalNot:
|
|
return !Convert.ToBoolean(d);
|
|
case JSToken.Minus:
|
|
return -d;
|
|
case JSToken.Plus:
|
|
return d;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
|
|
case TypeCode.String:
|
|
goto no_overload_case;
|
|
}
|
|
|
|
MethodInfo oper = this.GetOperator(v.GetType());
|
|
if (oper != null)
|
|
return oper.Invoke(null, (BindingFlags)0, JSBinder.ob, new Object[]{v}, null);
|
|
no_overload_case:
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
return ~Convert.ToInt32(v, ic);
|
|
case JSToken.LogicalNot:
|
|
return !Convert.ToBoolean(v, ic);
|
|
case JSToken.Minus:
|
|
return -Convert.ToNumber(v, ic);
|
|
case JSToken.Plus:
|
|
return Convert.ToNumber(v, ic);
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
}
|
|
|
|
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 JSToken.BitwiseNot:
|
|
this.operatorMeth = t.GetMethod("op_OnesComplement", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
|
|
case JSToken.LogicalNot:
|
|
this.operatorMeth = t.GetMethod("op_LogicalNot", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
|
|
case JSToken.Minus:
|
|
this.operatorMeth = t.GetMethod("op_UnaryNegation", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
|
|
case JSToken.Plus:
|
|
this.operatorMeth = t.GetMethod("op_UnaryPlus", BindingFlags.Public|BindingFlags.Static, JSBinder.ob, new Type[]{t}, null); break;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
if (this.operatorMeth == null ||
|
|
(operatorMeth.Attributes & MethodAttributes.SpecialName) == 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 (this.operatorTok == JSToken.LogicalNot) return Typeob.Boolean;
|
|
switch (Type.GetTypeCode(this.type)){
|
|
case TypeCode.Empty: return this.operatorTok == JSToken.BitwiseNot ? Typeob.Int32 : Typeob.Double;
|
|
case TypeCode.Object: return Typeob.Object;
|
|
case TypeCode.DBNull: return Typeob.Int32;
|
|
case TypeCode.Boolean: return Typeob.Int32;
|
|
case TypeCode.SByte: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.SByte : Typeob.Int32;
|
|
case TypeCode.Char: return Typeob.Int32;
|
|
case TypeCode.Byte: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.Byte : Typeob.Int32;
|
|
case TypeCode.Int16: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.Int16 : Typeob.Int32;
|
|
case TypeCode.UInt16: return (this.operatorTok == JSToken.BitwiseNot) ? Typeob.UInt16 : Typeob.Int32;
|
|
case TypeCode.Int32: return Typeob.Int32;
|
|
case TypeCode.UInt32: return this.operatorTok == JSToken.Minus ? Typeob.Double : Typeob.UInt32;
|
|
case TypeCode.Int64: return Typeob.Int64;
|
|
case TypeCode.UInt64: return this.operatorTok == JSToken.Minus ? Typeob.Double : Typeob.UInt64;
|
|
case TypeCode.Single:
|
|
case TypeCode.Double:
|
|
case TypeCode.String: return this.operatorTok == JSToken.BitwiseNot ? Typeob.Int32 : Typeob.Double;
|
|
}
|
|
if (Typeob.JSObject.IsAssignableFrom(this.type))
|
|
return Typeob.Double;
|
|
else
|
|
return Typeob.Object;
|
|
}
|
|
|
|
internal override void TranslateToConditionalBranch(ILGenerator il, bool branchIfTrue, Label label, bool shortForm){
|
|
if (this.operatorTok == JSToken.LogicalNot)
|
|
this.operand.TranslateToConditionalBranch(il, !branchIfTrue, label, shortForm);
|
|
else
|
|
base.TranslateToConditionalBranch(il, branchIfTrue, label, shortForm);
|
|
}
|
|
|
|
internal override void TranslateToIL(ILGenerator il, Type rtype){
|
|
if (this.metaData == null){
|
|
Type rt = this.operatorTok == JSToken.LogicalNot ? Typeob.Boolean : Typeob.Double;
|
|
if (Convert.IsPrimitiveNumericType(rtype) && Convert.IsPromotableTo(this.type, rtype))
|
|
rt = rtype;
|
|
if (this.operatorTok == JSToken.BitwiseNot && !Convert.IsPrimitiveIntegerType(rt)){
|
|
rt = this.type;
|
|
if (!Convert.IsPrimitiveIntegerType(rt))
|
|
rt = Typeob.Int32;
|
|
}
|
|
this.operand.TranslateToIL(il, this.type);
|
|
Convert.Emit(this, il, this.type, rt, true);
|
|
switch (this.operatorTok){
|
|
case JSToken.BitwiseNot:
|
|
il.Emit(OpCodes.Not);
|
|
break;
|
|
case JSToken.LogicalNot:
|
|
Convert.Emit(this, il, rt, Typeob.Boolean);
|
|
rt = Typeob.Boolean;
|
|
il.Emit(OpCodes.Ldc_I4_0);
|
|
il.Emit(OpCodes.Ceq);
|
|
break;
|
|
case JSToken.Minus:
|
|
il.Emit(OpCodes.Neg);
|
|
break;
|
|
case JSToken.Plus:
|
|
break;
|
|
default:
|
|
throw new JScriptException(JSError.InternalError, this.context);
|
|
}
|
|
Convert.Emit(this, il, rt, rtype);
|
|
return;
|
|
}
|
|
if (this.metaData is MethodInfo){
|
|
MethodInfo oper = (MethodInfo)this.metaData;
|
|
ParameterInfo[] pars = oper.GetParameters();
|
|
this.operand.TranslateToIL(il, pars[0].ParameterType);
|
|
il.Emit(OpCodes.Call, oper);
|
|
Convert.Emit(this, il, oper.ReturnType, rtype);
|
|
return;
|
|
}
|
|
//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
|
|
il.Emit(OpCodes.Ldloc, (LocalBuilder)this.metaData);
|
|
this.operand.TranslateToIL(il, Typeob.Object);
|
|
il.Emit(OpCodes.Call, CompilerGlobals.evaluateUnaryMethod);
|
|
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(NumericUnary));
|
|
ConstantWrapper.TranslateToILInt(il, (int)this.operatorTok);
|
|
il.Emit(OpCodes.Newobj, CompilerGlobals.numericUnaryConstructor);
|
|
il.Emit(OpCodes.Stloc, (LocalBuilder)this.metaData);
|
|
}
|
|
|
|
}
|
|
}
|