protected override ICode VisitCall(ExprCall e) { var expr = this.HandleCall(e, (obj, args) => new ExprCall(e.Ctx, e.CallMethod, obj, args, e.IsVirtualCall, e.ConstrainedType, e.Type)); var res = JsResolver.ResolveCallSite(expr); if (res != null) { return this.Visit(res); } if (expr.ConstrainedType != null) { if (expr.ConstrainedType.IsValueType) { // Map constrained virtual call to a method on a value-type, to a non-virtual call. // This is important as it prevents having to box the value-type, which is expensive var impl = expr.ConstrainedType.EnumResolvedMethods().FirstOrDefault(x => x.MatchMethodOnly(expr.CallMethod)); if (impl != null) { var constrainedCall = new ExprCall(expr.Ctx, impl, expr.Obj, expr.Args, false, null, expr.Type); return constrainedCall; } else { throw new Exception(); } } } if (expr.IsVirtualCall) { var ctx = expr.Ctx; var objIsVar = expr.Obj.IsVar(); var temp = objIsVar ? null : new ExprVarLocal(ctx, expr.Obj.Type); var getTypeObj = objIsVar ? expr.Obj : new ExprAssignment(ctx, temp, expr.Obj); var getType = new ExprCall(ctx, typeof(object).GetMethod("GetType"), getTypeObj); var eJsVCall = new ExprJsVirtualCall(ctx, expr.CallMethod, getType, temp ?? expr.Obj, expr.Args); return eJsVCall; } return expr; }
protected override ICode VisitNewArray(ExprNewArray e) { var ctx = e.Ctx; MethodInfo caGenDef; if (e.ElementType.IsValueType) { caGenDef = ((Func<int, int[]>)InternalFunctions.CreateArrayValueType<int>).Method.GetGenericMethodDefinition(); } else { caGenDef = ((Func<int, object[]>)InternalFunctions.CreateArrayRefType<object>).Method.GetGenericMethodDefinition(); } var mCreateArray = ctx.Module.Import(caGenDef).MakeGeneric(e.ElementType); var expr = new ExprCall(ctx, mCreateArray, null, e.ExprNumElements); return expr; }
protected override ICode VisitCall(ExprCall e) { var isDelegate = e.CallMethod.DeclaringType.EnumThisAllBaseTypes().Any(x => x.IsDelegate()); if (isDelegate) { var ctx = e.Ctx; switch (e.CallMethod.Name) { case "Invoke": return new ExprJsDelegateInvoke(ctx, e.Obj, e.Args); default: throw new NotSupportedException("Cannot handle delegate call: " + e.CallMethod.Name); } } else { return base.VisitCall(e); } }
protected override ICode VisitCall(ExprCall e) { if (e.IsVirtualCall) { var objType = e.Obj.Type.FullResolve(e.Ctx); if (objType.Resolve().IsSealed) { // Virtual calls to sealed classes can be rewritten as instance calls var methods = objType.EnumResolvedMethods().ToArray(); var instMethod = methods.FirstOrDefault(x => x.MatchMethodOnly(e.CallMethod)); if (instMethod != null) { var expr = new ExprCall(e.Ctx, instMethod, e.Obj, e.Args, false); return expr; } } } return base.VisitCall(e); }
// TODO: Make these methods share most of the code... protected override ICode VisitCall(ExprCall e) { var ctx = e.Ctx; var byRefs = new List<Tuple<Expr, Expr>>(); var args = e.CallMethod.Parameters.Zip(e.Args, (p, a) => new { p, a }) .Select(x => { if (x.p.ParameterType.IsByReference) { var wrapper = ctx.Local(ctx.IntPtr); byRefs.Add(Tuple.Create(x.a, (Expr)wrapper)); return wrapper; } else { return x.a; } }) .ToArray(); if (!args.SequenceEqual(e.Args)) { var call = new ExprCall(ctx, e.CallMethod, e.Obj, args, e.IsVirtualCall, e.ConstrainedType, e.Type); var resultTemp = ctx.Local(e.Type); return new ExprJsByRefWrapper(e.Ctx, call, resultTemp, byRefs); } return base.VisitCall(e); }
protected override ICode VisitUnary(ExprUnary e) { if (e.Expr.Type.IsInt64() || e.Expr.Type.IsUInt64()) { var ctx = e.Ctx; var signed = e.Expr.Type.IsInt64(); Delegate d; switch (e.Op) { case UnaryOp.Negate: var zero = ctx.Literal(0L, ctx.Int64); var subCall = new ExprBinary(ctx, BinaryOp.Sub, ctx.Int64, zero, e.Expr); return subCall; case UnaryOp.BitwiseNot: d = signed ? (Delegate)(Func<Int64, Int64>)_Int64.BitwiseNot : (Func<UInt64, UInt64>)_UInt64.BitwiseNot; break; default: throw new NotImplementedException("Cannot handle: " + e.Op); } var m = ctx.Module.Import(d.Method); var expr = (Expr)this.Visit(e.Expr); var call = new ExprCall(ctx, m, null, expr); return call; } return base.VisitUnary(e); }
protected override ICode VisitIsInst(ExprIsInst e) { var ctx = e.Ctx; var mIsInst = ctx.Module.Import(((Func<object, Type, object>)InternalFunctions.IsInst).Method); var eType = new ExprJsTypeVarName(ctx, e.Type); var expr = new ExprCall(ctx, e.Type, mIsInst, null, e.Expr, eType); return expr; }
protected override ICode VisitUnboxAny(ExprUnboxAny e) { if (!e.Type.IsValueType) { // On ref-type, unbox-any becomes a castclass var expr = (Expr)this.Visit(e.Expr); var cast = new ExprCast(e.Ctx, expr, e.Type); return cast; } var ctx = e.Ctx; if (e.Type.IsNullable()) { // If obj==null create Nullable with hasValue=false // If obj.Type not assignable to e.InnerType throw InvalidCastEx var innerType = e.Type.GetNullableInnerType(); var unboxMethod = ((Func<object, int?>)InternalFunctions.UnboxAnyNullable<int>).Method.GetGenericMethodDefinition(); var mUnbox = ctx.Module.Import(unboxMethod).MakeGeneric(innerType); var unboxAnyCall = new ExprCall(ctx, mUnbox, null, e.Expr); return unboxAnyCall; } else { // If obj==null throw NullRefEx // If obj.Type not assignable to e.Type throw InvalidCastEx // otherwise unbox var unboxMethod = ((Func<object, int>)InternalFunctions.UnboxAnyNonNullable<int>).Method.GetGenericMethodDefinition(); var mUnbox = ctx.Module.Import(unboxMethod).MakeGeneric(e.Type); var unboxAnyCall = new ExprCall(ctx, mUnbox, null, e.Expr); return unboxAnyCall; } }
public static Stmt GetHashCode(Ctx ctx) { var toString = new ExprJsResolvedMethod(ctx, ctx.String, ctx.This, "toExponential"); var getHashCode = new ExprCall(ctx, typeof(string).GetMethod("GetHashCode"), toString); var stmt = new StmtReturn(ctx, getHashCode); return stmt; }
protected override ICode VisitCall(ExprCall e) { return this.HandleCall(e, (obj, args) => new ExprCall(e.Ctx, e.CallMethod, obj, args, e.IsVirtualCall, e.ConstrainedType, e.Type)); }
protected override ICode VisitCall(ExprCall e) { var mRef = e.CallMethod; var mDef = mRef.Resolve(); if (mDef.DeclaringType.IsInterface) { throw new InvalidOperationException("Interface calls should never occur here"); } if (e.IsVirtualCall) { throw new InvalidOperationException("Virtual calls should never occur here"); } var name = this.resolver.MethodNames[mRef]; this.js.Append(name); this.CallAppendArgs(e); return e; }
public static Stmt FormatInt64(Ctx ctx) { var value = ctx.MethodParameter(0, "value"); var format = ctx.MethodParameter(1, "format"); var neg = ctx.Local(ctx.Boolean, "neg"); var precision = ctx.Local(ctx.Int32.MakeNullable(), "precision"); var fmt0 = ctx.Local(ctx.String, "fmt0"); var i = ctx.Local(ctx.Int32, "i"); var s = ctx.Local(ctx.String, "s"); var ss = ctx.Local(ctx.String, "ss"); var c = ctx.Local(ctx.String, "c"); var inc = ctx.Local(ctx.Boolean, "inc"); var divMod10 = ctx.Local(ctx.Int64.MakeArray(), "divMod10"); var valueDivMod10 = new ExprCall(ctx, (Func<UInt64, UInt64, object>)_Int64UInt64.UInt64DivRem, null, value.Expr, ctx.Literal(10L)).Named("valueDivMod10"); var valueNeg = new ExprCall(ctx, (Func<Int64, Int64, Int64>)_Int64.Subtract, null, ctx.Literal(0L), value.Expr).Named("valueNeg"); var sLen = ctx.Local(ctx.Int32, "sLen"); var newFmtEx = new ExprNewObj(ctx, ctx.Module.Import(typeof(FormatException).GetConstructor(Type.EmptyTypes))).Named("newFmtEx"); var js = @" format = format || 'G'; precision = format.length > 1 ? +format.substr(1) : null; fmt0 = format.charAt(0); if (fmt0 === 'x' || fmt0 === 'X') { s = value[1].toString(16); if (value[0] !== 0) { s = value[0].toString(16) + Array(9 - s.length).join('0') + s; } if (fmt0 === 'X') { s = s.toUpperCase(); } if (precision !== null && precision > s.length) { s = Array(precision - s.length + 1).join('0') + s; } return s; } if (value[0] === 0 && value[1] === 0) { s = '0'; } else if (value[0] === 0x80000000 && value[1] === 0) { s = '9223372036854775808'; neg = true; } else { if (value[0] >= 0x80000000) { value = valueNeg; neg = true; } s = ''; while (value[0] !== 0 || value[1] !== 0) { divMod10 = valueDivMod10; value = divMod10[0]; s = String.fromCharCode(48 + divMod10[1][1]) + s; } } switch (fmt0) { case 'g': case 'G': if (precision !== null && precision < s.length && precision > 0) { sLen = s.length; inc = +(s.charAt(precision)) >= 5; s = s.substr(0, precision); ss = ''; for (i = precision - 1; i >= 0; i--) { c = s.charAt(i); if (inc) { c = (+c) + 1; if (c > 9) { c = 0; } else { inc = false; } ss = String.fromCharCode(48 + c) + ss; } else { ss = c + ss; } } if (inc) { sLen++; ss = '1' + ss.substr(0, ss.length - 1); } if (ss.length > 1) { ss = ss.charAt(0) + '.' + ss.substr(1); } s = ss.replace(/(\d)\.?0+$/, '$1'); s += (fmt0 === 'g' ? 'e' : 'E') + '+' + (sLen - 1).toFixed().replace(/^(\d)$/, '0$1');; } break; case 'd': case 'D': if (precision !== null && precision > s.length) { s = Array(precision - s.length + 1).join('0') + s; } break; case 'n': case 'N': if (precision === null) precision = 2; s = s.replace(/(\d)(?=(\d{3})+$)/g, '$1,'); if (precision > 0) { s += '.' + Array(precision + 1).join('0'); } break; default: throw newFmtEx; } return neg ? '-' + s : s; "; return new StmtJsExplicit(ctx, js, value, format, neg, precision, fmt0, i, s, ss, c, inc, divMod10, valueDivMod10, valueNeg, sLen, newFmtEx); }
private Stmt Call(Instruction inst, bool isVirtualCallInst) { var callingRef = ((MethodReference)inst.Operand).FullResolve(this.ctx); bool isVirtualCall = isVirtualCallInst && callingRef.Resolve().IsVirtual; var numArgs = callingRef.Parameters.Count; var argExprs = new Expr[numArgs]; for (int i = numArgs - 1; i >= 0; i--) { argExprs[i] = this.stack.Pop(); } for (int i = 0; i < numArgs; i++) { var argType = callingRef.Parameters[i].ParameterType.FullResolve(callingRef); if (argType.IsGenericParameter) { throw new InvalidOperationException(); } argExprs[i] = this.InsertConvIfRequired(argExprs[i], argType); } var obj = callingRef.HasThis ? this.stack.Pop() : null; var exprCall = new ExprCall(this.ctx, callingRef, obj, argExprs, isVirtualCall, this.ConstrainedType); if (callingRef.ReturnType.IsVoid()) { return new StmtWrapExpr(this.ctx, exprCall); } else { return this.SsaLocalAssignment(exprCall); } }
public Stmt GetImpl(Ctx ctx) { var a = ctx.MethodParameter(0); var b = ctx.MethodParameter(1); var divMod = new ExprCall(ctx, (Func<UInt64, UInt64, object>)_Int64UInt64.UInt64DivRem, null, a, b).Named("divMod"); var js = "return divMod[1];"; var stmt = new StmtJsExplicit(ctx, js, divMod); return stmt; }
public Stmt GetImpl(Ctx ctx) { var a = ctx.MethodParameter(0, "a"); var b = ctx.MethodParameter(1, "b"); var neg = ctx.Local(ctx.Boolean, "neg"); var aNegate = new ExprUnary(ctx, UnaryOp.Negate, ctx.Int64, a.Expr).Named("aNegate"); var bNegate = new ExprUnary(ctx, UnaryOp.Negate, ctx.Int64, b.Expr).Named("bNegate"); var divMod = new ExprCall(ctx, (Func<UInt64, UInt64, object>)_Int64UInt64.UInt64DivRem, null, a.Expr, b.Expr).Named("divMod"); var r = ctx.Local(ctx.Int64, "r"); var rNegate = new ExprUnary(ctx, UnaryOp.Negate, ctx.Int64, r.Expr).Named("rNegate"); // TODO: Throw ArithmeticException if a == Int64.MinValue and b == -1 // TODO: Handle a or b being Int64.MinValue var js = @" neg = false; if (a[0] >>> 31) { a = aNegate; neg = true; } if (b[0] >>> 31) b = bNegate; r = divMod[1]; return neg ? rNegate : r; "; var stmt = new StmtJsExplicit(ctx, js, a, b, neg, r, aNegate, bNegate, divMod, rNegate); return stmt; }
/// <summary> /// If a call/newobj requires translating to an Expr that is not a call/newobj, then it is done here. /// </summary> /// <param name="call"></param> /// <returns></returns> public static Expr ResolveCallSite(ICall call) { var ctx = call.Ctx; var mRef = call.CallMethod; var tRefDecl = mRef.DeclaringType; var mDef = mRef.Resolve(); // A call to a method in a "JsClass" class - all external methods/properties require translating to JS var tDefDecl = mDef.DeclaringType; var jsClassAttr = tDefDecl.GetCustomAttribute<JsClassAttribute>() ?? tDefDecl.GetCustomAttribute<JsAbstractClassAttribute>(); if (jsClassAttr != null) { if (mDef.IsExternal()) { var jsDetail = mDef.GetCustomAttribute<JsDetailAttribute>(true); var jsDetailName = jsDetail.NullThru(x => (string)x.Properties.FirstOrDefault(y => y.Name == "Name").Argument.Value); var jsDetailIsDomEventProp = jsDetail.NullThru(x => ((bool?)x.Properties.FirstOrDefault(y => y.Name == "IsDomEvent").Argument.Value) ?? false); if (mDef.IsGetter || mDef.IsSetter) { // Property access if (jsDetailIsDomEventProp) { // Special handling of DOM events if (!mDef.IsSetter) { throw new InvalidOperationException("Only setters supported on DOM events"); } if (!mDef.Name.StartsWith("set_On")) { throw new InvalidOperationException("DOM event name must start with 'On'"); } if (call.Args.Count() != 1) { throw new InvalidOperationException("DOM event setter must have exactly one argument"); } var eventName = jsDetailName ?? mDef.Name.Substring(6).ToLowerInvariant(); var eventNameExpr = ctx.Literal(eventName); var safeCallFunction = (Action<object, string, Delegate>)InternalFunctions.SafeAddEventListener; var safeCall = new ExprCall(ctx, safeCallFunction, null, call.Obj, eventNameExpr, call.Args.First()); return safeCall; } else { var propertyName = jsDetailName ?? JsCase(mDef.Name.Substring(4)); if (mDef.Name.Substring(4) == "Item") { propertyName = null; } else if (mDef.IsStatic) { propertyName = JsCase(mDef.DeclaringType.Name) + "." + propertyName; } var jsProperty = new ExprJsResolvedProperty(ctx, call, propertyName); return jsProperty; } } else if (mDef.IsConstructor && !mDef.IsStatic) { // Constructor new object call var typeName = jsDetailName ?? (string)jsClassAttr.ConstructorArguments[0].Value; var expr = new ExprJsResolvedCtor(ctx, typeName, tRefDecl, call.Args); return expr; } else { // Normal method call var methodName = jsDetailName ?? JsCase(mDef.Name); if (mDef.IsStatic) { methodName = JsCase(mDef.DeclaringType.Name) + "." + methodName; } var expr = new ExprJsResolvedMethod(ctx, call.Type, call.Obj, methodName, call.Args); return expr; } } else { return null; } } var jsRedirectAttr = mDef.GetCustomAttribute<JsRedirectAttribute>(true); if (jsRedirectAttr != null) { if (jsRedirectAttr.ConstructorArguments[0].Value == null) { return FindExprReturn(call, call.CallMethod.DeclaringType); } var redirectToTRef = ((TypeReference)jsRedirectAttr.ConstructorArguments[0].Value).FullResolve(mRef); var redirectToMRef = redirectToTRef.EnumResolvedMethods(mRef).First(x => x.MatchMethodOnly(mRef)); switch (call.ExprType) { case Expr.NodeType.NewObj: return new ExprNewObj(ctx, redirectToMRef, call.Args); case Expr.NodeType.Call: return new ExprCall(ctx, redirectToMRef, call.Obj, call.Args, call.IsVirtualCall, null, call.Type); default: throw new NotImplementedException("Cannot handle: " + call.ExprType); } } var exprRet = FindExprReturn(call); if (exprRet != null) { return exprRet; } return null; }
protected override ICode VisitCall(ExprCall e) { var obj = this.Convert(e.Obj, e.CallMethod.DeclaringType); var args = e.Args.Select((x, i) => this.Convert(x, e.CallMethod.Parameters[i].ParameterType.FullResolve(e.CallMethod))).ToArray(); return new ExprCall(e.Ctx, e.CallMethod, obj, args, e.IsVirtualCall, e.ConstrainedType, e.Type); }
protected override ICode VisitCall(ExprCall e) { this.VisitCall((ICall)e, false); return e; }
protected override ICode VisitConv(ExprConv e) { var fromType = e.Expr.Type; var toType = e.Type; var fromTypeMetadataType = fromType.MetadataType; var toTypeMetadataType = toType.MetadataType; if (e.ForceFromUnsigned) { switch (fromTypeMetadataType) { case MetadataType.SByte: fromTypeMetadataType = MetadataType.Byte; break; case MetadataType.Int16: fromTypeMetadataType = MetadataType.UInt16; break; case MetadataType.Int32: fromTypeMetadataType = MetadataType.UInt32; break; case MetadataType.Int64: fromTypeMetadataType = MetadataType.UInt64; break; case MetadataType.IntPtr: fromTypeMetadataType = MetadataType.UIntPtr; break; } } if (fromTypeMetadataType == MetadataType.Char) { fromTypeMetadataType = MetadataType.UInt16; } if (toTypeMetadataType == MetadataType.Char) { toTypeMetadataType = MetadataType.UInt16; } var fromIdx = indexMap.ValueOrDefault(fromTypeMetadataType, -1); var toIdx = indexMap.ValueOrDefault(toTypeMetadataType, -1); if (fromIdx == -1 || toIdx == -1) { return e.Expr; } var ctx = e.Ctx; var js = jss[fromIdx, toIdx]; if (js != null) { // In-place conversion var expr = new ExprJsExplicit(ctx, js, e.Type, e.Expr.Named("e")); return expr; } else { // Call conversion function var convMi = ((Func<int, int>)InternalFunctions.Conv<int, int>).Method.GetGenericMethodDefinition(); var mConv = ctx.Module.Import(convMi).MakeGeneric(e.Expr.Type, e.Type); var convCall = new ExprCall(ctx, mConv, null, e.Expr); return convCall; } }
protected override ICode VisitBinary(ExprBinary e) { if (e.Left.Type.IsInt64() || e.Left.Type.IsUInt64()) { var ctx = e.Ctx; var signed = e.Left.Type.IsInt64(); Delegate d; switch (e.Op) { case BinaryOp.Add: d = signed ? (Delegate)(Func<Int64, Int64, Int64>)_Int64.Add : (Func<UInt64, UInt64, UInt64>)_UInt64.Add; break; case BinaryOp.Sub: d = signed ? (Delegate)(Func<Int64, Int64, Int64>)_Int64.Subtract : (Func<UInt64, UInt64, UInt64>)_UInt64.Subtract; break; case BinaryOp.Mul: d = signed ? (Delegate)(Func<Int64, Int64, Int64>)_Int64.Multiply : (Func<UInt64, UInt64, UInt64>)_UInt64.Multiply; break; case BinaryOp.Div: d = (Func<Int64, Int64, Int64>)_Int64.Divide; break; case BinaryOp.Div_Un: d = (Func<UInt64, UInt64, UInt64>)_UInt64.Divide; break; case BinaryOp.Rem: d = (Func<Int64, Int64, Int64>)_Int64.Remainder; break; case BinaryOp.Rem_Un: d = (Func<UInt64, UInt64, UInt64>)_UInt64.Remainder; break; case BinaryOp.BitwiseAnd: d = signed ? (Delegate)(Func<Int64, Int64, Int64>)_Int64.BitwiseAnd : (Func<UInt64, UInt64, UInt64>)_UInt64.BitwiseAnd; break; case BinaryOp.BitwiseOr: d = signed ? (Delegate)(Func<Int64, Int64, Int64>)_Int64.BitwiseOr : (Func<UInt64, UInt64, UInt64>)_UInt64.BitwiseOr; break; case BinaryOp.BitwiseXor: d = signed ? (Delegate)(Func<Int64, Int64, Int64>)_Int64.BitwiseXor : (Func<UInt64, UInt64, UInt64>)_UInt64.BitwiseXor; break; case BinaryOp.Equal: d = signed ? (Delegate)(Func<Int64, Int64, bool>)_Int64.Equals_ : (Func<UInt64, UInt64, bool>)_UInt64.Equals_; break; case BinaryOp.NotEqual: d = signed ? (Delegate)(Func<Int64, Int64, bool>)_Int64.NotEquals : (Func<UInt64, UInt64, bool>)_UInt64.NotEquals; break; case BinaryOp.LessThan: d = (Func<Int64, Int64, bool>)_Int64.LessThan; break; case BinaryOp.LessThan_Un: d = (Func<UInt64, UInt64, bool>)_UInt64.LessThan; break; case BinaryOp.LessThanOrEqual: d = (Func<Int64, Int64, bool>)_Int64.LessThanOrEqual; break; case BinaryOp.LessThanOrEqual_Un: d = (Func<UInt64, UInt64, bool>)_UInt64.LessThanOrEqual; break; case BinaryOp.GreaterThan: d = (Func<Int64, Int64, bool>)_Int64.GreaterThan; break; case BinaryOp.GreaterThan_Un: d = (Func<UInt64, UInt64, bool>)_UInt64.GreaterThan; break; case BinaryOp.GreaterThanOrEqual: d = (Func<Int64, Int64, bool>)_Int64.GreaterThanOrEqual; break; case BinaryOp.GreaterThanOrEqual_Un: d = (Func<UInt64, UInt64, bool>)_UInt64.GreaterThanOrEqual; break; default: throw new NotImplementedException("Cannot handle: " + e.Op); } var m = ctx.Module.Import(d.Method); var left = (Expr)this.Visit(e.Left); var right = (Expr)this.Visit(e.Right); var call = new ExprCall(ctx, m, null, left, right); return call; } return base.VisitBinary(e); }
public static Stmt ToString(Ctx ctx) { var value = ctx.This; var format = ctx.MethodParameter(0); var provider = ctx.MethodParameter(1); var nfi = ctx.Literal(null, ctx.Module.Import(typeof(NumberFormatInfo))); var expr = new ExprCall(ctx, (Func<Int64, string, NumberFormatInfo, string>)Cil2Js.JsResolvers.Classes.Helpers.Number.FormatInt64, null, value, format, nfi); return new StmtReturn(ctx, expr); }
protected override ICode VisitCall(ExprCall e) { this.calls.Add(e); return base.VisitCall(e); }