public CSharpBuilderException(JSToCSharpExceptionType type, RelinqScriptExpression root, RelinqScriptExpression expression, CompilationContext ctx, Exception innerException) : base(innerException) { Type = type; Root = root; Expression = expression; Context = ctx; }
public CannotResolveQuasiTypeFromContextException(RelinqScriptExpression root, RelinqScriptExpression expression, CompilationContext ctx) : base(JSToCSharpExceptionType.CannotResolveQuasiTypeFromContext, root, expression, ctx) { }
private LinqExpression CompileCast(Cast cast, RelinqScriptExpression e, CompilationContext ctx) { // todo. int -> enum and string -> char casts need special treatment // todo. remove this hack that's necessary only for redirectto+concat to work if (cast == null && e.Parent is OperatorExpression && ((OperatorExpression)e.Parent).Type == OperatorType.Add) { return Compile(e, ctx); } if (cast.Destination is ClrType) { if (cast.UserDefined == null) { if (cast.Source is ClrType) { var ta = ((ClrType)cast.Source).Type; var tp = ((ClrType)cast.Destination).Type; if (tp.LookupInheritanceChain().Contains(typeof(LinqExpression))) { return LinqExpression.Quote(Compile(e, ctx)); } else { if (ta.LookupInheritanceChain().Contains(tp)) { if (ta.IsValueType && tp.IsReferenceType()) { return LinqExpression.Convert(Compile(e, ctx), tp); } else { return Compile(e, ctx); } } else { return LinqExpression.Convert(Compile(e, ctx), tp); } } } else if (cast.Source is Lambda) { var tp = ((ClrType)cast.Destination).Type; if (tp.IsDelegate()) { return Compile(e, ctx); } if (tp.LookupInheritanceChain().Contains(typeof(LinqExpression))) { return LinqExpression.Quote(Compile(e, ctx)); } } else if (cast.Source is MethodGroup) { throw new NotImplementedException(String.Format( "Don't know how to compile the cast: '{0}'", cast)); } else { if (cast.Source is Null || cast.Source is UnknownConstant) { return Compile(e, ctx); } } } else { var ta = ((ClrType)cast.Source).Type; var tp = ((ClrType)cast.Destination).Type; // As a sidenote: C# expression trees fail to support spec-compliant u/d conversions // that consist of three step: implicit -> u/d -> implicit, // so all involved implicit conversions need to be created manually. throw new NotImplementedException(String.Format( "Don't know how to compile the cast: '{0}'", cast)); } } throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, e, ctx); }
private IEnumerable<LinqExpression> CompileCallArguments( MethodInfo sig, IEnumerable<Cast> casts, IEnumerable<RelinqScriptExpression> args, CompilationContext ctx) { var sigp = sig.GetParameters(); var instaCasts = sig.IsExtension() ? casts : casts.Skip(1); for (var i = 0; i < sigp.Length; ++i) { var cast = instaCasts.ElementAt(i); if (i != sigp.Length - 1 || !sig.IsVarargs()) { yield return CompileCast(cast, args.ElementAt(i), ctx); } else { // cba to check whether the cast actually features a nice CLR type var argType = ((ClrType)cast.Destination).Type; yield return LinqExpression.NewArrayInit( argType, args.Skip(sigp.Length - 1).Select(arg => CompileCast(cast, arg, ctx))); } } }
private IEnumerable<LinqExpression> CompileCallArguments( ResolvedInvocation invocation, IEnumerable<RelinqScriptExpression> args, CompilationContext ctx) { return CompileCallArguments(invocation.Signature, invocation.Casts, args, ctx); }
private Type AcquireExpectedTypeFromContext(RelinqScriptExpression e, CompilationContext ctx) { if (!e.Parent.IsCall()) { if (e.Parent is ConditionalExpression) { // conditional expression must have a CLR type inferred // it is checked during its compilation which happens // before its test or branches are compiled // so here we can safely assume that fact return e.ChildIndex == 0 ? typeof(bool) : ((ClrType)ctx.Types[e.Parent]).Type; } else { throw new CannotResolveQuasiTypeFromContextException(Ast, e, ctx); } } else { if (e.ChildIndex == 0 && // even if we analyze a target of an extension method invocation, // it still needs to be treated in a special way *elsewhere* (e.Parent is IndexerExpression || e.Parent is InvokeExpression)) { throw new CannotResolveQuasiTypeFromContextException(Ast, e, ctx); } var sig = ctx.Invocations[e.Parent].Signature; var sigp = sig.GetParameters(); var argIndex = sig.IsExtension() || sig.IsOperator() ? e.ChildIndex : e.ChildIndex - 1; argIndex = Math.Min(argIndex, sigp.Length - 1); // varargs var argType = sigp[argIndex].ParameterType; if (argIndex == sigp.Length - 1 && sig.IsVarargs()) // varargs argType = argType.GetElementType(); return argType; } }
private LinqExpression Compile(RelinqScriptExpression e, CompilationContext ctx) { try { if (!ctx.Types.ContainsKey(e)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, e, ctx); } if (!(ctx.Types[e] is ClrType || ctx.Types[e] is Null || ctx.Types[e] is UnknownConstant || ctx.Types[e] is Lambda)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, e, ctx); } if (e.IsCall() && !ctx.Invocations.ContainsKey(e)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, e, ctx); } if (e is LambdaExpression && !(ctx.Types[e] is Lambda)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, e, ctx); } switch (e.NodeType) { case ExpressionType.Keyword: return CompileKeyword((KeywordExpression)e, ctx); case ExpressionType.Variable: return CompileVariable((VariableExpression)e, ctx); case ExpressionType.Constant: return CompileConstant((ConstantExpression)e, ctx); case ExpressionType.New: return CompileNew((NewExpression)e, ctx); case ExpressionType.Lambda: return CompileLambda((LambdaExpression)e, ctx); case ExpressionType.MemberAccess: return CompileMemberAccess((MemberAccessExpression)e, ctx); case ExpressionType.Invoke: return CompileInvoke((InvokeExpression)e, ctx); case ExpressionType.Indexer: return CompileIndexer((IndexerExpression)e, ctx); case ExpressionType.Operator: return CompileOperator((OperatorExpression)e, ctx); case ExpressionType.Conditional: return CompileConditional((ConditionalExpression)e, ctx); } } catch (CSharpBuilderException) { throw; } catch (Exception ex) { throw new CSharpBuilderException( JSToCSharpExceptionType.Unexpected, Ast, e, ctx, ex); } throw new NotSupportedException(e.ToString()); }
private LinqExpression CompileVariable(VariableExpression ve, CompilationContext ctx) { if (!ctx.Closure.ContainsKey(ve.Name)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ve, ctx); } return ctx.Closure[ve.Name]; }
private LinqExpression CompileOperator(OperatorExpression oe, CompilationContext ctx) { // not checking anything here. see comment in CompileConditional method's body // do not rely on oe.Type to detect the LINQ expression type, since // lookup/resolution might yield an operator with a different type // todo. validate this (LogicalNot -> OnesComplement, but not others) var sig = ctx.Invocations[oe].Signature; var sigTypes = sig.GetOperatorTypes(); var actualOpType = sigTypes.Count() > 1 ? oe.Type : sigTypes.Single(); if (!sig.IsRedirected()) { var compiledOperands = CompileCallArguments(ctx.Invocations[oe], oe.Operands, ctx); var ctorArgs = compiledOperands.Cast<Object>(); if (sig.IsUserDefinedOperator()) ctorArgs = ctorArgs.Concat(sig.AsArray()); return actualOpType.LinqExpressionFactory()(ctorArgs.ToArray()); } else { if (sig.IsLifted()) { // todo. implement this throw new NotImplementedException(String.Format( "Don't know how to compile the lifted operator: '{0}'", sig)); } else { var rebuiltSig = sig.RedirectionRoot(); var rebuiltCasts = ctx.Invocations[oe].Casts.Zip( sig.RedirectionRoot().ToXArgs(oe.XArgsCount()), (cast, param) => Cast.Lookup(cast.Source, param)); // todo. the stuff above sucks since there's no cast from string to object var compiledOperands = CompileCallArguments( rebuiltSig, rebuiltCasts, oe.Operands, ctx); var ctorArgs = compiledOperands.Cast<Object>(); ctorArgs = ctorArgs.Concat(rebuiltSig.AsArray()); return actualOpType.LinqExpressionFactory()(ctorArgs.ToArray()); } } }
private LinqExpression CompileIndexer(IndexerExpression ie, CompilationContext ctx) { var typeofTarget = ctx.Types[ie.Target]; if (typeofTarget is ClrType) { var clrType = ((ClrType)typeofTarget).Type; if (clrType.IsArray && clrType.GetArrayRank() == 1) { // not checking whether the indexer expression has one and only operand return LinqExpression.ArrayIndex( Compile(ie.Target, ctx), CompileCallArguments(ctx.Invocations[ie], ie.Operands, ctx)); } else { // not checking for a void-returning signature // neither we check whether the sig is actually an indexer return LinqExpression.Call( Compile(ie.Target, ctx), ctx.Invocations[ie].Signature, CompileCallArguments(ctx.Invocations[ie], ie.Operands, ctx)); } } else { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ie, ctx); } }
private LinqExpression CompileInvoke(InvokeExpression ie, CompilationContext ctx) { var typeofTarget = ctx.Types[ie.Target]; if (typeofTarget is ClrType) { var clrType = ((ClrType)typeofTarget).Type; if (clrType.IsDelegate()) { // not checking for a void-returning signature return LinqExpression.Invoke( Compile(ie.Target, ctx), CompileCallArguments(ctx.Invocations[ie], ie.Args, ctx)); } else { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ie, ctx); } } else if (typeofTarget is MethodGroup) { var target = ie.Target as MemberAccessExpression; if (target == null) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ie, ctx); } var invocation = ctx.Invocations[ie]; if (!invocation.Signature.IsExtension()) { // not checking for a void-returning signature return LinqExpression.Call( Compile(target.Target, ctx), invocation.Signature, CompileCallArguments(invocation, ie.Args, ctx)); } else { // not checking for a void-returning signature var callArguments = target.Target.AsArray().Concat(ie.Args); return LinqExpression.Call( invocation.Signature, CompileCallArguments(invocation, callArguments, ctx).ToArray()); } } else { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ie, ctx); } }
private LinqExpression CompileMemberAccess(MemberAccessExpression mae, CompilationContext ctx) { if (ctx.Types[mae] is ClrType) { var clrType = ((ClrType)ctx.Types[mae.Target]).Type; if (clrType.IsArray && mae.Name == "Length") { return LinqExpression.ArrayLength(Compile(mae.Target, ctx)); } else { var fop = clrType.GetFieldOrProperty(mae.Name); //no validation for fop != null and being only of 2 possible types - I'm sick & tired! return fop is FieldInfo ? LinqExpression.Field(Compile(mae.Target, ctx), (FieldInfo)fop) : LinqExpression.Property(Compile(mae.Target, ctx), (PropertyInfo)fop); } } else if (ctx.Types[mae] is MethodGroup) { // we have three variants here: // 1) mae is a target of an Invoke -> note. processed, but not here // 2) mae is an argument of a call -> we need to emit a CreateDelegate stuff // 3) mae is a branch of a conditional -> same as 2 var expected = AcquireExpectedTypeFromContext(mae, ctx); if (!expected.IsDelegate()) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, mae, ctx); } var createDelegate = typeof(Delegate).GetMethod("CreateDelegate", new []{ typeof(Type), typeof(Object), typeof(MethodInfo)}); return LinqExpression.Call(createDelegate, new []{ LinqExpression.Constant(expected, typeof(Type)), Compile(mae.Target, ctx), LinqExpression.Constant(ctx.Invocations[mae], typeof(MethodInfo))}); } else { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, mae, ctx); } }
private LinqExpression CompileLambda(LambdaExpression le, CompilationContext ctx) { var lambda = ctx.Types[le] as Lambda; if (lambda == null) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, le, ctx); } var @params = le.Args.Select((arg, i) => LinqExpression.Parameter(lambda.Type.GetFunctionDesc().Args.ElementAt(i), arg)). ToDictionary(param => param.Name); foreach (var name in @params.Keys) { if (Integration.IsRegisteredJS(name)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, le, ctx); } else if (ctx.Closure.ContainsKey(name)) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, le, ctx); } else { ctx.Closure.Add(name, @params[name]); } } try { // todo. also yield cast to convert return types? return LinqExpression.Lambda(Compile(le.Body, ctx), @params.Values.ToArray()); } finally { ctx.Closure.RemoveRange(@params.Keys); } }
private LinqExpression CompileNew(NewExpression ne, CompilationContext ctx) { var clrType = ctx.Types[ne] is ClrType ? ((ClrType)ctx.Types[ne]).Type : null; if (clrType == null || !clrType.IsAnonymous()) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ne, ctx); } return LinqExpression.New( clrType.GetConstructors()[0], ne.Props.Values.Select(propExpr => Compile(propExpr, ctx)), ne.Props.Keys.Select(prop => clrType.GetProperty(prop)).ToArray()); }
private LinqExpression CompileConstant(ConstantExpression ce, CompilationContext ctx) { Func<Type, LinqExpression> constFactory = clrType => LinqExpression.Constant(JsonDeserializer.Deserialize(ce.Data, clrType), clrType); if (ctx.Types[ce] is ClrType) { return constFactory(((ClrType)ctx.Types[ce]).Type); } else if (ctx.Types[ce] is Null || ctx.Types[ce] is UnknownConstant) { return constFactory(AcquireExpectedTypeFromContext(ce, ctx)); } else { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ce, ctx); } }
public CSharpBuilderException(JSToCSharpExceptionType type, RelinqScriptExpression root, RelinqScriptExpression expression, CompilationContext ctx) : this(type, root, expression, ctx, null) { }
private LinqExpression CompileConditional(ConditionalExpression ce, CompilationContext ctx) { // todo. conditional has to have a CLR type inferred. // cba to check this stuff var typeofTest = ctx.Types[ce.Test]; var typeofFalse = ctx.Types[ce.IfFalse]; var typeofTrue = ctx.Types[ce.IfTrue]; var castTest = Cast.Lookup(typeofTest, typeof(bool)); if (typeofTest is ClrType) { if (castTest == null) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ce, ctx); } } else { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ce, ctx); } var f2tCast = Cast.Lookup(typeofFalse, typeofTrue); var t2fCast = Cast.Lookup(typeofTrue, typeofFalse); if (f2tCast == null && t2fCast == null) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ce, ctx); } else if (f2tCast != null && t2fCast != null && typeofFalse != typeofTrue) { throw new CSharpBuilderException( JSToCSharpExceptionType.UnexpectedInferredAst, Ast, ce, ctx); } return LinqExpression.Condition( CompileCast(castTest, ce.Test, ctx), f2tCast == null ? Compile(ce.IfFalse, ctx) : CompileCast(f2tCast, ce.IfFalse, ctx), t2fCast == null ? Compile(ce.IfTrue, ctx) : CompileCast(t2fCast, ce.IfTrue, ctx)); }
private LinqExpression CompileKeyword(KeywordExpression ke, CompilationContext ctx) { return LinqExpression.Constant(Integration.ProduceCSharp(ke.Name)); }