private JST.Expression TranslateExpression(MethodCompilerEnvironment methCompEnv, ISeq<JST.Statement> optBody, JST.Expression optLvalue, bool ignoreResult, CST.Expression expr) { switch (expr.Flavor) { case CST.ExpressionFlavor.Null: return new JST.NullExpression(); case CST.ExpressionFlavor.TypeHandle: { // The type structure var the = (CST.TypeHandleConstantExpression)expr; return methCompEnv.ResolveType(the.RuntimeType); } case CST.ExpressionFlavor.FieldHandle: { // A System.Reflection.FieldInfo instance, via exported constructor var fhe = (CST.FieldHandleConstantExpression)expr; var fieldEnv = fhe.RuntimeField.Enter(methCompEnv); var slot = env.GlobalMapping.ResolveFieldDefToSlot (fieldEnv.Assembly, fieldEnv.Type, fieldEnv.Field); var fieldDefType = methCompEnv.ResolveType(fhe.RuntimeField.DefiningType); var fieldType = methCompEnv.ResolveType(fieldEnv.SubstituteType(fieldEnv.Field.FieldType)); var init = default(JST.Expression); var rawInit = fieldEnv.Field.Init as CST.RawFieldInit; if (rawInit != null) init = new JST.ArrayLiteral (rawInit.Data.Select(b => (JST.Expression)new JST.NumericLiteral(b)).ToSeq()); else init = new JST.NullExpression(); return JST.Expression.DotCall (rootId.ToE(), Constants.RootReflectionFieldInfo, new JST.StringLiteral(slot), fieldDefType, new JST.BooleanLiteral(fieldEnv.Field.IsStatic), new JST.BooleanLiteral(!fieldEnv.Field.IsStatic), new JST.StringLiteral(fieldEnv.Field.Name), new JST.NullExpression(), fieldType, init); } case CST.ExpressionFlavor.MethodHandle: { // A System.Reflection.MethodInfo instance, via exported constructor var mhe = (CST.MethodHandleConstantExpression)expr; var runtimeMethEnv = mhe.RuntimeMethod.Enter(methCompEnv); var slot = env.GlobalMapping.ResolveMethodDefToSlot (runtimeMethEnv.Assembly, runtimeMethEnv.Type, runtimeMethEnv.Method); var methodDef = runtimeMethEnv.Method; var methodDefType = methCompEnv.ResolveType(mhe.RuntimeMethod.DefiningType); var paramTypes = methodDef.ValueParameters.Where((t, i) => i > 0 || methodDef.IsStatic).Select (t => methCompEnv.ResolveType(runtimeMethEnv.SubstituteType(t.Type))).ToSeq(); var result = methodDef.Result == null ? null : methCompEnv.ResolveType(runtimeMethEnv.SubstituteType(methodDef.Result.Type)); var isStatic = env.InteropManager.IsStatic (runtimeMethEnv.Assembly, runtimeMethEnv.Type, runtimeMethEnv.Method); return JST.Expression.DotCall (rootId.ToE(), Constants.RootReflectionMethodInfo, new JST.StringLiteral(slot), methodDefType, new JST.BooleanLiteral(isStatic), new JST.BooleanLiteral(!isStatic), new JST.StringLiteral(methodDef.Name), new JST.NullExpression(), new JST.BooleanLiteral(runtimeMethEnv.Method.IsVirtualOrAbstract), new JST.ArrayLiteral(paramTypes), new JST.BooleanLiteral(true), result); } case CST.ExpressionFlavor.CodePointer: { // Produces a <codePtr> structure, which is only 'observable' by delegate constructors. // (We can't just produce the function itself because of issues with method slot updating // and delegate structural equality) var cpe = (CST.CodePointerExpression)expr; // If object is non-null then we assume the same object is passed to delegate constructor var isVirtual = cpe.Object != null; var bindings = new OrdMap<JST.Identifier, JST.Expression>(); if (cpe.Method.MethodTypeArguments.Count > 0) { var methodBoundTypeArgs = cpe.Method.MethodTypeArguments.Select(t => methCompEnv.ResolveType(t)).ToSeq(); bindings.Add(Constants.CodePtrArguments, new JST.ArrayLiteral(methodBoundTypeArgs)); } bindings.Add (Constants.CodePtrType, isVirtual ? new JST.NullExpression() : methCompEnv.ResolveType(cpe.Method.DefiningType)); bindings.Add (Constants.CodePtrArity, new JST.NumericLiteral(cpe.Method.ValueParameters.Count - (cpe.Method.IsStatic ? 0 : 1))); bindings.Add (Constants.CodePtrSlot, env.JSTHelpers.MethodSlotName(methCompEnv, cpe.Method, isVirtual)); return new JST.ObjectLiteral(bindings); } case CST.ExpressionFlavor.Int32: { var ie = (CST.Int32ConstantExpression)expr; return new JST.NumericLiteral(ie.Value); } case CST.ExpressionFlavor.Int64: { var ie = (CST.Int64ConstantExpression)expr; var n = (double)ie.Value; var o = (long)n; if (o != ie.Value) env.Log (new LossOfPrecisionMessage(CST.MessageContextBuilders.Expression(messageCtxt, expr), "int64 literal")); return new JST.NumericLiteral(n); } case CST.ExpressionFlavor.Single: { var se = (CST.SingleConstantExpression)expr; return new JST.NumericLiteral(se.Value); } case CST.ExpressionFlavor.Double: { var de = (CST.DoubleConstantExpression)expr; return new JST.NumericLiteral(de.Value); } case CST.ExpressionFlavor.String: { var se = (CST.StringConstantExpression)expr; return methCompEnv.ResolveString(se.Value); } case CST.ExpressionFlavor.Unary: { var ue = (CST.UnaryExpression)expr; if (ue.WithOverflow) env.Log (new LossOfPrecisionMessage (CST.MessageContextBuilders.Expression(messageCtxt, expr), "overflow detection")); if (ue.IsUnsigned) env.Log (new LossOfPrecisionMessage (CST.MessageContextBuilders.Expression(messageCtxt, expr), "unsigned arithmetic")); switch (ue.Op) { case CST.UnaryOp.CheckFinite: { var ve = TranslateExpression(methCompEnv, optBody, null, false, ue.Value); return JST.Expression.DotCall(rootId.ToE(), Constants.RootCheckFinite, ve); } case CST.UnaryOp.Length: { var ve = TranslateExpression(methCompEnv, optBody, null, false, ue.Value); return JST.Expression.Dot(ve, Constants.length); } case CST.UnaryOp.Neg: { var ve = TranslateExpression(methCompEnv, optBody, null, false, ue.Value); return new JST.UnaryExpression(JST.UnaryOp.UnaryMinus, ve); } case CST.UnaryOp.BitNot: { var ve = TranslateExpression(methCompEnv, optBody, null, false, ue.Value); return new JST.UnaryExpression(JST.UnaryOp.BitwiseNot, ve); } case CST.UnaryOp.LogNot: { var ve = TranslateConditionalExpression(methCompEnv, optBody, ue.Value); return new JST.UnaryExpression(JST.UnaryOp.LogicalNot, ve); } case CST.UnaryOp.IsZero: { var ve = TranslateConditionalExpression(methCompEnv, optBody, ue.Value); return new JST.UnaryExpression(JST.UnaryOp.LogicalNot, ve); } case CST.UnaryOp.IsNonZero: { var ve = TranslateConditionalExpression(methCompEnv, optBody, ue.Value); return ve; } case CST.UnaryOp.IsNull: { var ve = TranslateConditionalExpression(methCompEnv, optBody, ue.Value); return new JST.UnaryExpression(JST.UnaryOp.LogicalNot, ve); } case CST.UnaryOp.IsNonNull: { var ve = TranslateConditionalExpression(methCompEnv, optBody, ue.Value); // Must force result to be a boolean return new JST.UnaryExpression(JST.UnaryOp.LogicalNot, new JST.UnaryExpression(JST.UnaryOp.LogicalNot, ve)); } default: throw new ArgumentOutOfRangeException(); } } case CST.ExpressionFlavor.Binary: { var be = (CST.BinaryExpression)expr; if (be.WithOverflow) env.Log (new LossOfPrecisionMessage (CST.MessageContextBuilders.Expression(messageCtxt, expr), "overflow detection")); if (be.IsUnsigned && be.Op != CST.BinaryOp.Shr) env.Log (new LossOfPrecisionMessage (CST.MessageContextBuilders.Expression(messageCtxt, expr), "unsigned arithmetic")); if (be.Op == CST.BinaryOp.LogAnd) { var le = TranslateConditionalExpression(methCompEnv, optBody, be.LeftValue); // NOTE: Not safe to hoist side-effects into body since rhs may not be evaluated var re = TranslateConditionalExpression(methCompEnv, null, be.RightValue); return new JST.BinaryExpression(le, JST.BinaryOp.LogicalAND, re); } else if (be.Op == CST.BinaryOp.LogOr) { var le = TranslateConditionalExpression(methCompEnv, optBody, be.LeftValue); // NOTE: Not safe to hoist side-effects into body since rhs may not be evaluated var re = TranslateConditionalExpression(methCompEnv, null, be.RightValue); return new JST.BinaryExpression(le, JST.BinaryOp.LogicalOR, re); } else { var le = TranslateExpression(methCompEnv, optBody, null, false, be.LeftValue); var re = TranslateExpression(methCompEnv, optBody, null, false, be.RightValue); switch (be.Op) { case CST.BinaryOp.Eq: return new JST.BinaryExpression(le, JST.BinaryOp.Equals, re); case CST.BinaryOp.Ne: return new JST.BinaryExpression(le, JST.BinaryOp.NotEquals, re); case CST.BinaryOp.Lt: return new JST.BinaryExpression(le, JST.BinaryOp.LessThan, re); case CST.BinaryOp.Le: return new JST.BinaryExpression(le, JST.BinaryOp.LessThanOrEqual, re); case CST.BinaryOp.Gt: return new JST.BinaryExpression(le, JST.BinaryOp.GreaterThan, re); case CST.BinaryOp.Ge: return new JST.BinaryExpression(le, JST.BinaryOp.GreaterThanOrEqual, re); case CST.BinaryOp.Add: return new JST.BinaryExpression(le, JST.BinaryOp.Plus, re); case CST.BinaryOp.Sub: return new JST.BinaryExpression(le, JST.BinaryOp.Minus, re); case CST.BinaryOp.Mul: return new JST.BinaryExpression(le, JST.BinaryOp.Times, re); case CST.BinaryOp.Div: { var de = new JST.BinaryExpression(le, JST.BinaryOp.Div, re); if (be.Type(methCompEnv).Style(methCompEnv) is CST.IntegerTypeStyle) return new JST.BinaryExpression (de, JST.BinaryOp.LeftShift, new JST.NumericLiteral(0)); else return de; } case CST.BinaryOp.Rem: return new JST.BinaryExpression(le, JST.BinaryOp.Mod, re); case CST.BinaryOp.LogAnd: case CST.BinaryOp.LogOr: throw new InvalidOperationException(); case CST.BinaryOp.BitAnd: return new JST.BinaryExpression(le, JST.BinaryOp.BitwiseAND, re); case CST.BinaryOp.BitOr: return new JST.BinaryExpression(le, JST.BinaryOp.BitwiseOR, re); case CST.BinaryOp.BitXor: return new JST.BinaryExpression(le, JST.BinaryOp.BitwiseXOR, re); case CST.BinaryOp.Shl: return new JST.BinaryExpression(le, JST.BinaryOp.LeftShift, re); case CST.BinaryOp.Shr: if (be.IsUnsigned) return new JST.BinaryExpression(le, JST.BinaryOp.UnsignedRightShift, re); else return new JST.BinaryExpression(le, JST.BinaryOp.RightShift, re); default: throw new ArgumentOutOfRangeException(); } } } case CST.ExpressionFlavor.Convert: { var ce = (CST.ConvertExpression)expr; var ve = TranslateExpression(methCompEnv, optBody, null, false, ce.Value); var sourceStyle = ce.Value.Type(methCompEnv).Style(methCompEnv); var resultStyle = ce.ResultType.Style(methCompEnv); env.Log(new LossOfPrecisionMessage(CST.MessageContextBuilders.Expression(messageCtxt, expr), "conversion")); if (resultStyle is CST.Int32TypeStyle && sourceStyle is CST.FloatTypeStyle) return new JST.BinaryExpression(ve, JST.BinaryOp.LeftShift, new JST.NumericLiteral(0)); else return ve; } case CST.ExpressionFlavor.Read: { var re = (CST.ReadExpression)expr; if (re.Address.Flavor == CST.ExpressionFlavor.AddressOf) { var aoe = (CST.AddressOfExpression)re.Address; return TranslateCellReadWrite(methCompEnv, optBody, false, aoe.Cell, null); } else { var ptre = TranslateExpression(methCompEnv, optBody, null, false, re.Address); return JST.Expression.DotCall(ptre, Constants.PointerRead); } } case CST.ExpressionFlavor.Write: { var we = (CST.WriteExpression)expr; if (we.Address.Flavor == CST.ExpressionFlavor.AddressOf) { var aoe = (CST.AddressOfExpression)we.Address; return TranslateCellReadWrite (methCompEnv, optBody, ignoreResult, aoe.Cell, lvalue => TranslateExpression(methCompEnv, optBody, lvalue, false, we.Value)); } else { var ptre = TranslateExpression(methCompEnv, optBody, null, false, we.Address); var ve = TranslateExpression(methCompEnv, optBody, null, false, we.Value); return JST.Expression.DotCall(ptre, Constants.PointerWrite, ve); } } case CST.ExpressionFlavor.AddressOf: { var ao = (CST.AddressOfExpression)expr; return TranslateCellAsPointer(methCompEnv, optBody, ao.Cell); } case CST.ExpressionFlavor.ConditionalDeref: { var cde = (CST.ConditionalDerefExpression)expr; var obj = TranslateExpression(methCompEnv, optBody, null, false, cde.Address); return env.JSTHelpers.ConditionalDerefExpressionForType(methCompEnv, cde.ConstrainedType, obj); } case CST.ExpressionFlavor.Call: { var ce = (CST.CallExpression)expr; return TranslateCall(methCompEnv, optBody, ce.CallFlavor, ce.Method, ce.Arguments); } case CST.ExpressionFlavor.NewObject: { var noe = (CST.NewObjectExpression)expr; var args = noe.Arguments.Select(e => TranslateExpression(methCompEnv, optBody, null, false, e)).ToSeq(); // Construction will try to build object directly into optLvalue, if any return env.JSTHelpers.ConstructorExpression (methCompEnv, nameSupply, optBody, optLvalue, noe.Method, args); } case CST.ExpressionFlavor.NewArray: { var nae = (CST.NewArrayExpression)expr; var len = TranslateExpression(methCompEnv, optBody, null, false, nae.Length); var elemType = methCompEnv.ResolveType(nae.ElementType); return JST.Expression.DotCall(rootId.ToE(), Constants.RootNewArray, elemType, len); } case CST.ExpressionFlavor.NewBox: { var nbe = (CST.NewBoxExpression)expr; var value = TranslateExpression(methCompEnv, optBody, null, false, nbe.Value); return env.JSTHelpers.BoxExpressionForType(methCompEnv, nbe.ValueType, value); } case CST.ExpressionFlavor.Cast: { var ce = (CST.CastExpression)expr; var value = TranslateExpression(methCompEnv, optBody, null, false, ce.Value); var resultType = methCompEnv.ResolveType(ce.ResultType); return JST.Expression.DotCall(rootId.ToE(), Constants.RootCastClass, resultType, value); } case CST.ExpressionFlavor.Clone: { var ce = (CST.CloneExpression)expr; var ve = TranslateExpression(methCompEnv, optBody, null, false, ce.Value); return env.JSTHelpers.CloneExpressionForType(methCompEnv, ce.ResultType, ve); } case CST.ExpressionFlavor.IsInst: { var iie = (CST.IsInstExpression)expr; var value = TranslateExpression(methCompEnv, optBody, null, false, iie.Value); var testType = methCompEnv.ResolveType(iie.TestType); return JST.Expression.DotCall(rootId.ToE(), Constants.RootIsInst, value, testType); } case CST.ExpressionFlavor.IfThenElse: { var itee = (CST.IfThenElseExpression)expr; var cond = TranslateConditionalExpression(methCompEnv, optBody, itee.Condition); // NOTE: Not safe to hoist side-effects into body var then = TranslateExpression(methCompEnv, null, null, false, itee.Then); var els = TranslateExpression(methCompEnv, null, null, false, itee.Else); return new JST.ConditionalExpression(cond, then, els); } case CST.ExpressionFlavor.ImportExport: { var iee = (CST.ImportExportExpression)expr; var ve = TranslateExpression(methCompEnv, optBody, null, false, iee.Value); if (iee.IsImport) return env.JSTHelpers.ImportExpressionForType(methCompEnv, iee.ManagedType, ve); else return env.JSTHelpers.ExportExpressionForType(methCompEnv, iee.ManagedType, ve); } case CST.ExpressionFlavor.CallImportedPseudo: { var cie = (CST.CallImportedExpression)expr; var calleeMemEnv = cie.Method.Enter(methCompEnv); var localBody = optBody ?? new Seq<JST.Statement>(); var args = cie.Arguments.Select(e => TranslateExpression(methCompEnv, optBody, null, false, e)).ToSeq(); var call = env.InteropManager.AppendImport (nameSupply, rootId, calleeMemEnv.Assembly, calleeMemEnv.Type, calleeMemEnv.Method, localBody, args); if (optBody == null && localBody.Count > 0) return new JST.StatementsPseudoExpression(new JST.Statements(localBody), call); else return call; } case CST.ExpressionFlavor.StatementsPseudo: { // Statements are in same scope as surrounding context var se = (CST.StatementsPseudoExpression)expr; var body = TranslateStatements(methCompEnv, se.Body); var value = se.Value == null ? null : TranslateExpression(methCompEnv, body, null, false, se.Value); return new JST.StatementsPseudoExpression(new JST.Statements(body), value); } case CST.ExpressionFlavor.InitialStatePseudo: return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.StatePC, new JST.NumericLiteral(0) }, { Constants.StateTryStack, new JST.ArrayLiteral() }, { Constants.StateContStack, new JST.ArrayLiteral() } }); default: throw new ArgumentOutOfRangeException(); } }
private JST.Expression TranslateConditionalExpression(MethodCompilerEnvironment methCompEnv, ISeq<JST.Statement> optBody, CST.Expression expr) { var e = TranslateExpression(methCompEnv, optBody, null, false, expr); var type = expr.Type(methCompEnv); var s = type.Style(methCompEnv); if (s is CST.ObjectTypeStyle || s is CST.StringTypeStyle || s is CST.ParameterTypeStyle || s is CST.NullTypeStyle) { if (e.IsDuplicatable) return StringIsTrue(e); else { var body = optBody ?? new Seq<JST.Statement>(); var id = nameSupply.GenSym(); body.Add(JST.Statement.Var(id, e)); if (optBody == null) return new JST.StatementsPseudoExpression(new JST.Statements(body), StringIsTrue(id.ToE())); else return StringIsTrue(id.ToE()); } } else return e; }
private JST.Expression TranslateCall(MethodCompilerEnvironment methCompEnv, ISeq<JST.Statement> optBody, CST.CallFlavor callFlavor, CST.MethodRef methodRef, IImSeq<CST.Expression> arguments) { var s = methodRef.DefiningType.Style(methCompEnv); if (s is CST.NullableTypeStyle && methodRef.Name.Equals("get_HasValue", StringComparison.Ordinal) && arguments[0].Flavor == CST.ExpressionFlavor.AddressOf) { // SPECIAL CASE: Nullable`1::get_HasValue(&cell) becomes cell != null var ao = (CST.AddressOfExpression)arguments[0]; return JST.Expression.IsNotNull(TranslateCellReadWrite(methCompEnv, optBody, false, ao.Cell, null)); } else if (s is CST.NullableTypeStyle && methodRef.Name.Equals("get_Value", StringComparison.Ordinal) && arguments[0].Flavor == CST.ExpressionFlavor.AddressOf) { // SPECIAL CASE: Nullable`1::get_Value(&cell) becomes AssertNonNullInvalidOperation(cell) var ao = (CST.AddressOfExpression)arguments[0]; return JST.Expression.DotCall (rootId.ToE(), Constants.RootAssertNonNullInvalidOperation, TranslateCellReadWrite(methCompEnv, optBody, false, ao.Cell, null)); } else { var args = arguments.Select(e => TranslateExpression(methCompEnv, optBody, null, false, e)).ToSeq (); switch (callFlavor) { case CST.CallFlavor.Normal: return methCompEnv.MethodCallExpression(methodRef, nameSupply, false, args); case CST.CallFlavor.Factory: return methCompEnv.MethodCallExpression(methodRef, nameSupply, true, args); case CST.CallFlavor.Virtual: return methCompEnv.VirtualMethodCallExpression(methodRef, optBody, args); default: throw new ArgumentOutOfRangeException("callFlavor"); } } }
private JST.Expression TranslateCellAsPointer(MethodCompilerEnvironment methCompEnv, ISeq<JST.Statement> optBody, CST.Cell cell) { switch (cell.Flavor) { case CST.CellFlavor.Variable: { var alc = (CST.VariableCell)cell; return methCompEnv.ResolveVariablePointer(alc.Id); } case CST.CellFlavor.Field: { var fc = (CST.FieldCell)cell; var fieldEnv = fc.Field.Enter(methCompEnv); if (fieldEnv.Field.IsStatic) { if (fc.Object != null) throw new InvalidOperationException("static field has object"); return env.JSTHelpers.ResolveStaticFieldToPointer(methCompEnv, fc.Field); } else { if (fc.Object == null) throw new InvalidOperationException("instance field does not have object"); var oe = TranslateExpression(methCompEnv, optBody, null, false, fc.Object); return env.JSTHelpers.ResolveInstanceFieldToPointer(methCompEnv, oe, fc.Field); } } case CST.CellFlavor.Element: { var ee = (CST.ElementCell)cell; var ae = TranslateExpression(methCompEnv, optBody, null, false, ee.Array); var ie = TranslateExpression(methCompEnv, optBody, null, false, ee.Index); return env.JSTHelpers.PointerToArrayElementExpression(methCompEnv, ae, ie, ee.Type(methCompEnv)); } case CST.CellFlavor.Box: { var bc = (CST.BoxCell)cell; var valueType = methCompEnv.ResolveType(bc.ValueType); var be = TranslateExpression(methCompEnv, optBody, null, false, bc.Box); return JST.Expression.DotCall(valueType, Constants.TypeUnbox, be); } case CST.CellFlavor.StatePCPseudo: { throw new InvalidOperationException("cannot take address of PC"); } default: throw new ArgumentOutOfRangeException(); } }
// ---------------------------------------------------------------------- // Translating CST cells/expressions/statements // ---------------------------------------------------------------------- private JST.Expression TranslateCellReadWrite(MethodCompilerEnvironment methCompEnv, ISeq<JST.Statement> optBody, bool ignoreResult, CST.Cell cell, Func<JST.Expression, JST.Expression> mkRexp) { var lvalue = default(JST.Expression); switch (cell.Flavor) { case CST.CellFlavor.Variable: { var alc = (CST.VariableCell)cell; lvalue = alc.Id.ToE(); break; } case CST.CellFlavor.Field: { var fc = (CST.FieldCell)cell; var fieldEnv = fc.Field.Enter(methCompEnv); if (fieldEnv.Field.IsStatic) { if (fc.Object != null) throw new InvalidOperationException("static field has object"); lvalue = env.JSTHelpers.ResolveStaticField(methCompEnv, fc.Field); } else { if (fc.Object == null) throw new InvalidOperationException("instance field does not have object"); var oe = TranslateExpression(methCompEnv, optBody, null, false, fc.Object); lvalue = env.JSTHelpers.ResolveInstanceField(methCompEnv, oe, fc.Field); } break; } case CST.CellFlavor.Element: { var ee = (CST.ElementCell)cell; var ae = TranslateExpression(methCompEnv, optBody, null, false, ee.Array); var ie = TranslateExpression(methCompEnv, optBody, null, false, ee.Index); if (env.CLRArraySemantics) { // Must go via runtime read/write methods if (mkRexp == null) return JST.Expression.DotCall(rootId.ToE(), Constants.RootGetArrayValue, ae, ie); else return JST.Expression.DotCall (rootId.ToE(), Constants.RootSetArrayValueInstruction, ae, ie, mkRexp(null)); } else { lvalue = new JST.IndexExpression(ae, ie); break; } } case CST.CellFlavor.Box: { var bc = (CST.BoxCell)cell; var be = TranslateExpression(methCompEnv, optBody, null, false, bc.Box); if (mkRexp == null) { var valueType = methCompEnv.ResolveType(bc.ValueType); return JST.Expression.DotCall(valueType, Constants.TypeUnboxAny, be); } else throw new InvalidOperationException("cannot mutate boxed objects"); } case CST.CellFlavor.StatePCPseudo: { var sc = (CST.StatePCPseudoCell)cell; lvalue = JST.Expression.Dot(sc.StateId.ToE(), Constants.StatePC); } break; default: throw new ArgumentOutOfRangeException(); } if (mkRexp == null) return lvalue; else if (lvalue.IsIdentifier != null) { // Try to build r.h.s. diretly into knows l.h.s. lvalue var rvalue = mkRexp(lvalue); if (rvalue == null) { // Great! No need for assignmet. return ignoreResult ? null : lvalue; } else // No luck, must assign explicitly return new JST.BinaryExpression(lvalue, JST.BinaryOp.Assignment, rvalue); } else return new JST.BinaryExpression(lvalue, JST.BinaryOp.Assignment, mkRexp(null)); }
private Seq<JST.Statement> TranslateStatements(MethodCompilerEnvironment methCompEnv, CST.Statements cstStatements) { var jstStatements = new Seq<JST.Statement>(); var subMethCompEnv = methCompEnv.EnterBlock(); subMethCompEnv.BindUsage(jstStatements, cstStatements.Usage(methCompEnv)); foreach (var stmnt in cstStatements.Body) TranslateStatement(subMethCompEnv, jstStatements, stmnt); return jstStatements; }
private void TranslateStatement(MethodCompilerEnvironment methCompEnv, Seq<JST.Statement> body, CST.Statement stmnt) { switch (stmnt.Flavor) { case CST.StatementFlavor.Expression: { var es = (CST.ExpressionStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement(es.ToString())); var exp = TranslateExpression(methCompEnv, body, null, true, es.Value); if (exp != null) body.Add(new JST.ExpressionStatement(exp)); break; } case CST.StatementFlavor.Break: { var bs = (CST.BreakStatement)stmnt; body.Add(new JST.BreakStatement(bs.Label)); break; } case CST.StatementFlavor.Continue: { var cs = (CST.ContinueStatement)stmnt; body.Add(new JST.ContinueStatement(cs.Label)); break; } case CST.StatementFlavor.Throw: { var ts = (CST.ThrowStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement(ts.ToString())); var e = TranslateExpression(methCompEnv, body, null, false, ts.Exception); body.Add(new JST.ThrowStatement(e)); break; } case CST.StatementFlavor.Rethrow: { var rs = (CST.RethrowStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement(rs.ToString())); var e = TranslateExpression(methCompEnv, body, null, false, rs.Exception); body.Add(new JST.ThrowStatement(e)); break; } case CST.StatementFlavor.Return: { var rs = (CST.ReturnStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement(rs.ToString())); var e = rs.Value == null ? null : TranslateExpression(methCompEnv, body, null, false, rs.Value); body.Add(new JST.ReturnStatement(e)); break; } case CST.StatementFlavor.IfThenElse: { var ites = (CST.IfThenElseStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement("condition: " + ites.Condition.ToString())); var cond = TranslateConditionalExpression(methCompEnv, body, ites.Condition); var then = new JST.Statements(TranslateStatements(methCompEnv, ites.Then)); var els = ites.Else == null ? default(JST.Statements) : new JST.Statements(TranslateStatements(methCompEnv, ites.Else)); body.Add(new JST.IfStatement(cond, then, els)); break; } case CST.StatementFlavor.Switch: { var ss = (CST.SwitchStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement("switch: " + ss.Value.ToString())); var v = TranslateExpression(methCompEnv, body, null, false, ss.Value); var cases = new Seq<JST.CaseClause>(); var def = default(JST.DefaultClause); foreach (var ssc in ss.Cases) { var actValues = ssc.Values.Where(i => i >= 0).ToList(); if (actValues.Count < ssc.Values.Count) { if (def != null) throw new InvalidOperationException("duplicate default cases"); var caseBody = TranslateStatements(methCompEnv, ssc.Body); def = new JST.DefaultClause(new JST.Statements(caseBody), -1); } if (actValues.Count > 0) { for (var i = 0; i < actValues.Count - 1; i++) cases.Add (new JST.CaseClause (new JST.NumericLiteral(ssc.Values[i]), new JST.Statements())); var caseBody = TranslateStatements(methCompEnv, ssc.Body); cases.Add (new JST.CaseClause (new JST.NumericLiteral(ssc.Values[ssc.Values.Count - 1]), new JST.Statements(caseBody))); } } body.Add(new JST.SwitchStatement(v, cases, def)); break; } case CST.StatementFlavor.DoWhile: { var dws = (CST.DoWhileStatement)stmnt; var whileBody = TranslateStatements(methCompEnv, dws.Body); // NOTE: Not safe to hoist side-effects to body var cond = TranslateConditionalExpression(methCompEnv, null, dws.Condition); body.Add(new JST.DoStatement(new JST.Statements(whileBody), cond)); if (env.DebugMode) body.Add(new JST.CommentStatement("condition: " + dws.Condition.ToString())); break; } case CST.StatementFlavor.WhileDo: { var wds = (CST.WhileDoStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement("condition: " + wds.Condition.ToString())); // NOTE: Not safe to hoist side-effects into body var cond = TranslateConditionalExpression(methCompEnv, null, wds.Condition); var doBody = TranslateStatements(methCompEnv, wds.Body); body.Add(new JST.WhileStatement(cond, new JST.Statements(doBody))); break; } case CST.StatementFlavor.InitializeObject: { var ios = (CST.InitializeObjectStatement)stmnt; if (env.DebugMode) body.Add(new JST.CommentStatement(ios.ToString())); var ptrType = ios.Address.Type(methCompEnv); if (ios.Address.Flavor == CST.ExpressionFlavor.AddressOf) { var aoe = (CST.AddressOfExpression)ios.Address; var exp = TranslateCellReadWrite (methCompEnv, body, true, aoe.Cell, lvalue => env.JSTHelpers.DefaultExpressionForType(methCompEnv, ptrType.Arguments[0])); if (exp != null) body.Add(new JST.ExpressionStatement(exp)); } else { var ptr = TranslateExpression(methCompEnv, body, null, false, ios.Address); var defval = env.JSTHelpers.DefaultExpressionForType(methCompEnv, ptrType.Arguments[0]); body.Add (new JST.ExpressionStatement(JST.Expression.DotCall(ptr, Constants.PointerWrite, defval))); } break; } case CST.StatementFlavor.Try: { var ts = (CST.TryStatement)stmnt; var tryBody = TranslateStatements(methCompEnv, ts.Body); var finallyClause = default(JST.FinallyClause); var exceptionId = default(JST.Identifier); var catchTests = default(Seq<JST.Expression>); var catchBodies = default(Seq<Seq<JST.Statement>>); var nCatches = ts.Handlers.Where(h => h.Flavor == CST.HandlerFlavor.Catch).Count(); foreach (var h in ts.Handlers) { switch (h.Flavor) { case CST.HandlerFlavor.Catch: { var ch = (CST.TryStatementCatchHandler)h; var thisBody = TranslateStatements(methCompEnv, ch.Body); if (exceptionId == null) exceptionId = ch.ExceptionId; var type = methCompEnv.ResolveType(ch.Type); var thisTest = JST.Expression.IsNotNull (JST.Expression.DotCall (rootId.ToE(), Constants.RootIsInst, exceptionId.ToE(), type)); if (catchTests == null) { catchTests = new Seq<JST.Expression>(); catchBodies = new Seq<Seq<JST.Statement>>(); } catchTests.Add(thisTest); if (!ch.ExceptionId.Equals(exceptionId)) { var newBody = new Seq<JST.Statement>(); newBody.Add(JST.Statement.Var(ch.ExceptionId, exceptionId.ToE())); foreach (var s in thisBody) newBody.Add(s); catchBodies.Add(newBody); } else catchBodies.Add(thisBody); break; } case CST.HandlerFlavor.Finally: { var finallyBody = TranslateStatements(methCompEnv, h.Body); if (finallyClause != null) throw new InvalidOperationException("more than one finally clause"); finallyClause = new JST.FinallyClause(new JST.Statements(finallyBody)); break; } case CST.HandlerFlavor.Filter: throw new InvalidOperationException("filter handler not supported"); case CST.HandlerFlavor.Fault: throw new InvalidOperationException("fault handler not supported"); default: throw new ArgumentOutOfRangeException(); } } var catchClause = default(JST.CatchClause); if (exceptionId != null) { var catchIf = default(JST.Statement); for (var i = catchTests.Count - 1; i >= 0; i--) { if (catchIf == null) catchIf = new JST.IfStatement (catchTests[i], new JST.Statements(catchBodies[i]), new JST.Statements(new JST.ThrowStatement(exceptionId.ToE()))); else catchIf = new JST.IfStatement(catchTests[i], new JST.Statements(catchBodies[i]), new JST.Statements(catchIf)); } catchClause = new JST.CatchClause(exceptionId, new JST.Statements(catchIf)); } body.Add(new JST.TryStatement(new JST.Statements(tryBody), catchClause, finallyClause)); break; } case CST.StatementFlavor.HandlePseudo: { var hs = (CST.HandlePseudoStatement)stmnt; body.Add (JST.Statement.DotCall (rootId.ToE(), Constants.RootHandle, hs.StateId.ToE(), hs.ExceptionId.ToE())); break; } case CST.StatementFlavor.PushTryPseudo: { var pts = (CST.PushTryPseudoStatement)stmnt; body.Add (JST.Statement.Call (JST.Expression.Dot(pts.StateId.ToE(), Constants.StateTryStack, Constants.push), new JST.ObjectLiteral(new OrdMap<JST.Identifier, JST.Expression> { { Constants.TryHandlers, new JST.ArrayLiteral(pts.Handlers.Select(h => HandlerLiteral(methCompEnv, h)).ToSeq()) } }))); break; } case CST.StatementFlavor.LeavePseudo: { var ls = (CST.LeavePseudoStatement)stmnt; body.Add (JST.Statement.DotCall (rootId.ToE(), Constants.RootLeaveTryCatch, ls.StateId.ToE(), new JST.NumericLiteral(ls.PopCount), new JST.NumericLiteral(ls.TargetId))); break; } case CST.StatementFlavor.EndPseudo: { var es = (CST.EndPseudoStatement)stmnt; body.Add (JST.Statement.DotCall(rootId.ToE(), Constants.RootEndFaultFinally, es.StateId.ToE())); break; } case CST.StatementFlavor.GotoPseudo: { var gs = (CST.GotoPseudoStatement)stmnt; body.Add (JST.Statement.DotAssignment(gs.StateId.ToE(), Constants.StatePC, new JST.NumericLiteral(gs.TargetId))); break; } default: throw new ArgumentOutOfRangeException(); } }
private JST.Expression HandlerLiteral(MethodCompilerEnvironment methCompEnv, CST.TryPseudoStatementHandler handler) { switch (handler.Flavor) { case CST.HandlerFlavor.Catch: { var catchh = (CST.CatchTryPseudoStatementHandler)handler; var exid = nameSupply.GenSym(); var type = methCompEnv.ResolveType(catchh.ExceptionType); var match = JST.Expression.IsNotNull (JST.Expression.DotCall(rootId.ToE(), Constants.RootIsInst, exid.ToE(), type)); var pred = new JST.FunctionExpression (new Seq<JST.Identifier> { exid }, new JST.Statements (new JST.IfStatement (match, new JST.Statements (JST.Statement.Assignment(catchh.ExceptionId.ToE(), exid.ToE()), new JST.ReturnStatement(new JST.BooleanLiteral(true))), new JST.Statements(new JST.ReturnStatement(new JST.BooleanLiteral(false)))))); return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.HandlerStyle, new JST.NumericLiteral(0) }, { Constants.HandlerTarget, new JST.NumericLiteral(catchh.HandlerId) }, { Constants.HandlerPred, pred } }); } case CST.HandlerFlavor.Fault: return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.HandlerStyle, new JST.NumericLiteral(1) }, { Constants.HandlerTarget, new JST.NumericLiteral(handler.HandlerId) } }); case CST.HandlerFlavor.Finally: return new JST.ObjectLiteral (new OrdMap<JST.Identifier, JST.Expression> { { Constants.HandlerStyle, new JST.NumericLiteral(2) }, { Constants.HandlerTarget, new JST.NumericLiteral(handler.HandlerId) } }); case CST.HandlerFlavor.Filter: throw new InvalidOperationException("filter blocks not supported"); default: throw new ArgumentOutOfRangeException(); } }