private MSAst.Expression ReduceWorker(bool optimizeDynamicConvert) { MSAst.Expression result; if (_tests.Length > 100) { // generate: // if(x) { // body // goto end // } else { // } // elseBody // end: // // to avoid deeply recursive trees which can stack overflow. BlockBuilder builder = new BlockBuilder(); var label = Ast.Label(); for (int i = 0; i < _tests.Length; i++) { IfStatementTest ist = _tests[i]; builder.Add( Ast.Condition( optimizeDynamicConvert ? TransformAndDynamicConvert(ist.Test, typeof(bool)) : GlobalParent.Convert(typeof(bool), Microsoft.Scripting.Actions.ConversionResultKind.ExplicitCast, ist.Test), Ast.Block( TransformMaybeSingleLineSuite(ist.Body, GlobalParent.IndexToLocation(ist.Test.StartIndex)), Ast.Goto(label) ), Utils.Empty() ) ); } if (_else != null) { builder.Add(_else); } builder.Add(Ast.Label(label)); result = builder.ToExpression(); } else { // Now build from the inside out if (_else != null) { result = _else; } else { result = AstUtils.Empty(); } int i = _tests.Length; while (i-- > 0) { IfStatementTest ist = _tests[i]; result = GlobalParent.AddDebugInfoAndVoid( Ast.Condition( optimizeDynamicConvert ? TransformAndDynamicConvert(ist.Test, typeof(bool)) : GlobalParent.Convert(typeof(bool), Microsoft.Scripting.Actions.ConversionResultKind.ExplicitCast, ist.Test), TransformMaybeSingleLineSuite(ist.Body, GlobalParent.IndexToLocation(ist.Test.StartIndex)), result ), new SourceSpan(GlobalParent.IndexToLocation(ist.StartIndex), GlobalParent.IndexToLocation(ist.HeaderIndex)) ); } } return(result); }
protected internal static Expression InlineCall(CodeBlock parent, CodeBlockExpression cbe, bool istailpostion, params Expression[] pp) { // all var names are unique. CodeBlock cb = cbe.Block; if (parent.IsGlobal) { return(CallNormal(cbe, pp)); } List <Statement> assigns = new List <Statement>(); int i = 0; cb.Inlined = true; if (parent.Filename == null && cb.Filename != null) { parent.Filename = cb.Filename; } var parentvars = new List <Variable>(parent.Variables); foreach (Variable p in cb.Parameters) { SymbolId origname = p.Name; p.Name = (SymbolId)Builtins.GenSym(p.Name); p.Block = parent; p.Kind = Variable.VariableKind.Local; parent.AddVariable(p); Expression val = Unwrap(pp[i]); if (val.Type != typeof(SymbolId) && !Generator.assigns.ContainsKey(origname)) { if (p.Type == typeof(object)) { p.Type = val.Type; assigns.Add(Ast.Write(p, val)); } else { assigns.Add(Ast.Write(p, Ast.ConvertHelper(val, p.Type))); } } else { if (p.Type == typeof(object)) { assigns.Add(Ast.Write(p, pp[i])); } else { assigns.Add(Ast.Write(p, Ast.ConvertHelper(pp[i], p.Type))); } } if (p.Lift) { parent.HasEnvironment = true; } i++; } foreach (Variable l in cb.Variables) { if (l.DefaultValue == null && l.Kind != Variable.VariableKind.Global) { l.Name = (SymbolId)Builtins.GenSym(l.Name); } l.Block = parent; parent.AddVariable(l); if (l.Lift) { parent.HasEnvironment = true; } } Expression body = RewriteReturn(cb.Body); if (assigns.Count > 0) { return(Ast.Comma(Ast.Void(Ast.Block(assigns)), body)); } else { return(body); } }
private MSAst.LambdaExpression CreateOuterLambda(Type lambdaType, MSAst.Expression debuggableBody) { List <MSAst.Expression> bodyExpressions = new List <MSAst.Expression>(); List <MSAst.Expression> tryExpressions = new List <MSAst.Expression>(); List <MSAst.Expression> finallyExpressions = new List <MSAst.Expression>(); Type returnType = lambdaType.GetMethod("Invoke").ReturnType; MSAst.LabelTarget returnLabelTarget = Ast.Label(returnType); // Init $funcInfo tryExpressions.Add( Ast.Assign( _funcInfo, Ast.Convert(_functionInfo, typeof(FunctionInfo)) ) ); // Init $traceLocations // $TODO: only do this if we're in TracePoints mode tryExpressions.Add( Ast.Assign( _traceLocations, Ast.Call(typeof(RuntimeOps).GetMethod("GetTraceLocations"), _funcInfo) ) ); // Init sourceFile locals foreach (var entry in _sourceFilesMap) { tryExpressions.Add( Ast.Assign( entry.Value, Ast.Constant(entry.Key, typeof(DebugSourceFile)) ) ); } if (_noPushFrameOptimization) { tryExpressions.Add(_pushFrame); } tryExpressions.Add(Ast.Call( typeof(RuntimeOps).GetMethod("OnFrameEnterTraceEvent"), _thread )); var frameExit = AstUtils.If( Ast.Equal( _debugMarkerLocationMap.Length > 0 ? Ast.Property(_sourceFilesMap[_debugMarkerLocationMap[0].SourceFile], "Mode") : _globalDebugMode, AstUtils.Constant((int)DebugMode.FullyEnabled) ), Ast.Call( typeof(RuntimeOps).GetMethod("OnFrameExitTraceEvent"), _thread, _debugMarker, _retVal != null ? (MSAst.Expression)Ast.Convert(_retVal, typeof(object)) : Ast.Constant(null) ) ); // normal exit tryExpressions.Add( Ast.Block( _retVal != null ? Ast.Assign(_retVal, debuggableBody) : debuggableBody, Ast.Assign(_frameExitException, Ast.Constant(true)), frameExit) ); tryExpressions.Add( _retVal != null ? (MSAst.Expression)Ast.Return(returnLabelTarget, _retVal) : Ast.Empty() ); MSAst.Expression[] popFrame = new MSAst.Expression[] { AstUtils.If( // Fire thead-exit event if PopFrame returns true Ast.AndAlso( Ast.Equal(Ast.Call(typeof(RuntimeOps).GetMethod("PopFrame"), _thread), Ast.Constant(true)), Ast.Equal(_globalDebugMode, AstUtils.Constant((int)DebugMode.FullyEnabled)) ), Ast.Call( typeof(RuntimeOps).GetMethod("OnThreadExitEvent"), _thread ) ) }; if (_noPushFrameOptimization) { finallyExpressions.AddRange(popFrame); } else { finallyExpressions.Add( AstUtils.If( Ast.Equal(_framePushed, Ast.Constant(true)), popFrame ) ); } MSAst.ParameterExpression caughtException; // Run the function body bodyExpressions.Add(Ast.TryCatchFinally( Ast.TryCatch( Ast.Block( ArrayUtils.Append(tryExpressions.ToArray(), Ast.Default(returnType)) ), Ast.Catch( caughtException = Ast.Variable(typeof(Exception), "$caughtException"), Ast.Block( // The expressions below will always throw. // If the exception needs to be cancelled then OnTraceEvent will throw ForceToGeneratorLoopException. // If the exception is not being cancelled then we'll just rethrow at the end of the catch block. AstUtils.If( Ast.Not( Ast.TypeIs( caughtException, typeof(ForceToGeneratorLoopException) ) ), AstUtils.If( Ast.NotEqual(_globalDebugMode, AstUtils.Constant((int)DebugMode.Disabled)), _noPushFrameOptimization ? Ast.Empty() : _conditionalPushFrame, Ast.Call( typeof(RuntimeOps).GetMethod("OnTraceEventUnwind"), _thread, _debugMarker, caughtException ) ), // exception exit AstUtils.If( Ast.Not(_frameExitException), frameExit ) ), Ast.Rethrow(), // Ensuring that the catch block is of the same type as the try block Ast.Default(returnType) ) ) ), Ast.Block(finallyExpressions), Ast.Catch( typeof(ForceToGeneratorLoopException), Ast.TryFinally( // Handle ForceToGeneratorLoopException Ast.Block( returnType != typeof(void) ? Ast.Block( Ast.Assign( _retValFromGeneratorLoop, Ast.Call( typeof(RuntimeOps).GetMethod("GeneratorLoopProc"), _thread ) ), AstUtils.If( Ast.NotEqual( _retValFromGeneratorLoop, Ast.Constant(null) ), Ast.Assign(_retVal, Ast.Convert(_retValFromGeneratorLoop, returnType)), Ast.Return( returnLabelTarget, Ast.Convert(_retValFromGeneratorLoop, returnType) ) ).Else( Ast.Assign(_retVal, Ast.Default(returnType)), Ast.Return( returnLabelTarget, Ast.Default(returnType) ) ) ) : Ast.Block( Ast.Call( typeof(RuntimeOps).GetMethod("GeneratorLoopProc"), _thread ), Ast.Return(returnLabelTarget) ) , // Ensuring that the catch block is of the same type as the try block Ast.Default(returnType) ), // Make sure that the debugMarker is up-to-date after the generator loop Ast.Assign( _debugMarker, Ast.Call( typeof(RuntimeOps).GetMethod("GetCurrentSequencePointForLeafGeneratorFrame"), _thread ) ) ) ) )); MSAst.Expression body = Ast.Block(bodyExpressions); if (body.Type == typeof(void) && returnType != typeof(void)) { body = Ast.Block(body, Ast.Default(returnType)); } return(Ast.Lambda( lambdaType, Ast.Block( _lambdaVars, Ast.Label(returnLabelTarget, body) ), _alias, _lambdaParams)); }
Statement Rewrite(Statement s) { if (s is BlockStatement) { var stmts = ((BlockStatement)s).Statements; List <Statement> newbody = new List <Statement>(); foreach (var stmt in stmts) { var nstmt = Rewrite(stmt); if (nstmt != null) { if (nstmt is BlockStatement) { newbody.AddRange(((BlockStatement)nstmt).Statements); } else { newbody.Add(nstmt); } } } return(Ast.Block(newbody)); } if (s is WriteStatement) { var ws = (WriteStatement)s; if (!references.ContainsKey(ws.Variable)) { if (ws.Value is MemberExpression) { var me = ws.Value as MemberExpression; if (me.Member == Compiler.Generator.Unspecified && !ScriptDomainManager.Options.LightweightDebugging) { return(null); } } if (ws.Value is BoundExpression) { return(null); } return(Ast.Statement(ws.Value)); } } if (s is ExpressionStatement) { var es = (ExpressionStatement)s; if (es.Expression is VoidExpression) { var ve = (VoidExpression)es.Expression; return(ve.Statement); } } if (s is IfStatement) { var ifs = (IfStatement)s; if (ifs.ElseStatement == null) { return(Ast.If(ifs.Tests[0].Test, Rewrite(ifs.Tests[0].Body)).ToStatement()); } else { return(Ast.If(ifs.Tests[0].Test, Rewrite(ifs.Tests[0].Body)).Else(Rewrite(ifs.ElseStatement))); } } return(s); }
public override MSAst.Expression Reduce() { MSAst.Expression destination = _dest; if (_expressions.Length == 0) { MSAst.Expression result; if (destination != null) { result = Ast.Call( AstMethods.PrintNewlineWithDest, Parent.LocalContext, destination ); } else { result = Ast.Call( AstMethods.PrintNewline, Parent.LocalContext ); } return(GlobalParent.AddDebugInfo(result, Span)); } else { // Create list for the individual statements ReadOnlyCollectionBuilder <MSAst.Expression> statements = new ReadOnlyCollectionBuilder <MSAst.Expression>(); // Store destination in a temp, if we have one MSAst.ParameterExpression temp = null; if (destination != null) { temp = Ast.Variable(typeof(object), "destination"); statements.Add(MakeAssignment(temp, destination)); destination = temp; } for (int i = 0; i < _expressions.Length; i++) { bool withComma = (i < _expressions.Length - 1 || _trailingComma);// ? "PrintComma" : "Print"; Expression current = _expressions[i]; MSAst.MethodCallExpression mce; if (destination != null) { mce = Ast.Call( withComma ? AstMethods.PrintCommaWithDest : AstMethods.PrintWithDest, Parent.LocalContext, destination, AstUtils.Convert(current, typeof(object)) ); } else { mce = Ast.Call( withComma ? AstMethods.PrintComma : AstMethods.Print, Parent.LocalContext, AstUtils.Convert(current, typeof(object)) ); } statements.Add(mce); } statements.Add(AstUtils.Empty()); MSAst.Expression res; if (temp != null) { res = Ast.Block(new[] { temp }, statements.ToReadOnlyCollection()); } else { res = Ast.Block(statements.ToReadOnlyCollection()); } return(GlobalParent.AddDebugInfo(res, Span)); } }
private DynamicMetaObject /*!*/ MakeMemberAccess(DynamicMetaObjectBinder /*!*/ member, string name, MemberAccess access, params DynamicMetaObject /*!*/[] /*!*/ args) { DynamicMetaObject self = Restrict(typeof(OldInstance)); CustomInstanceDictionaryStorage dict; int key = GetCustomStorageSlot(name, out dict); if (key == -1) { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldInstance " + access + " NoOptimized"); return(MakeDynamicMemberAccess(member, name, access, args)); } ParameterExpression tmp = Ast.Variable(typeof(object), "dict"); Expression target; ValidationInfo test = new ValidationInfo( Ast.NotEqual( Ast.Assign( tmp, Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceGetOptimizedDictionary)), self.Expression, AstUtils.Constant(dict.KeyVersion) ) ), AstUtils.Constant(null) ) ); PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldInstance " + access + " Optimized"); switch (access) { case MemberAccess.Invoke: ParameterExpression value = Ast.Variable(typeof(object), "value"); target = Ast.Block( new[] { value }, Ast.Condition( Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.TryOldInstanceDictionaryGetValueHelper)), tmp, Ast.Constant(key), AstUtils.Convert(Expression, typeof(object)), value ), AstUtils.Convert( ((InvokeMemberBinder)member).FallbackInvoke(new DynamicMetaObject(value, BindingRestrictions.Empty), args, null).Expression, typeof(object) ), AstUtils.Convert( ((InvokeMemberBinder)member).FallbackInvokeMember(self, args).Expression, typeof(object) ) ) ); break; case MemberAccess.Get: // BUG: There's a missing Fallback path here that's always been present even // in the version that used rules. target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDictionaryGetValueHelper)), tmp, AstUtils.Constant(key), AstUtils.Convert(Expression, typeof(object)) ); break; case MemberAccess.Set: target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDictionarySetExtraValue)), tmp, AstUtils.Constant(key), AstUtils.Convert(args[1].Expression, typeof(object)) ); break; case MemberAccess.Delete: target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDeleteCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), AstUtils.Convert(Expression, typeof(OldInstance)), AstUtils.Constant(name) ); break; default: throw new InvalidOperationException(); } return(BindingHelpers.AddDynamicTestAndDefer( member, new DynamicMetaObject( target, BindingRestrictions.Combine(args).Merge(self.Restrictions) ), args, test, tmp )); }
public static MSA.Expression /*!*/ Infinite(MSA.LabelTarget @break, MSA.LabelTarget @continue, params MSA.Expression[] /*!*/ body) { return(AstUtils.Infinite(Ast.Void(Ast.Block(body)), @break, @continue)); }
internal protected override Expression ToExpression(OverloadResolver resolver, RestrictedArguments args, bool[] hasBeenUsed) { var actualArgs = resolver.GetActualArguments(); int splatIndex = actualArgs.SplatIndex; int collapsedCount = actualArgs.CollapsedCount; int firstSplatted = actualArgs.FirstSplattedArg; var result = new Expression[2 + _expandedCount + (collapsedCount > 0 ? 2 : 0)]; var arrayVariable = resolver.GetTemporary(_elementType.MakeArrayType(), "a"); int e = 0; result[e++] = Ast.Assign(arrayVariable, Ast.NewArrayBounds(_elementType, Ast.Constant(_expandedCount + collapsedCount))); int itemIndex = 0; int i = _start; while (true) { // inject loop copying collapsed items: if (i == splatIndex) { var indexVariable = resolver.GetTemporary(typeof(int), "t"); // for (int t = 0; t <= {collapsedCount}; t++) { // a[{itemIndex} + t] = CONVERT<ElementType>(list.get_Item({splatIndex - firstSplatted} + t)) // } result[e++] = Ast.Assign(indexVariable, AstUtils.Constant(0)); result[e++] = AstUtils.Loop( Ast.LessThan(indexVariable, Ast.Constant(collapsedCount)), // TODO: not implemented in the old interpreter // Ast.PostIncrementAssign(indexVariable), Ast.Assign(indexVariable, Ast.Add(indexVariable, AstUtils.Constant(1))), Ast.Assign( Ast.ArrayAccess(arrayVariable, Ast.Add(AstUtils.Constant(itemIndex), indexVariable)), resolver.Convert( new DynamicMetaObject( resolver.GetSplattedItemExpression(Ast.Add(AstUtils.Constant(splatIndex - firstSplatted), indexVariable)), BindingRestrictions.Empty ), null, ParameterInfo, _elementType ) ), null ); itemIndex += collapsedCount; } if (i >= _start + _expandedCount) { break; } Debug.Assert(!hasBeenUsed[i]); hasBeenUsed[i] = true; result[e++] = Ast.Assign( Ast.ArrayAccess(arrayVariable, AstUtils.Constant(itemIndex++)), resolver.Convert(args.GetObject(i), args.GetType(i), ParameterInfo, _elementType) ); i++; } result[e++] = arrayVariable; Debug.Assert(e == result.Length); return(Ast.Block(result)); }
/// <summary> /// Creating a Python type involves calling __new__ and __init__. We resolve them /// and generate calls to either the builtin funcions directly or embed sites which /// call the slots at runtime. /// </summary> private DynamicMetaObject /*!*/ MakePythonTypeCall(DynamicMetaObjectBinder /*!*/ call, Expression /*!*/ codeContext, DynamicMetaObject /*!*/[] /*!*/ args) { ValidationInfo valInfo = MakeVersionCheck(); DynamicMetaObject self = new RestrictedMetaObject( AstUtils.Convert(Expression, LimitType), BindingRestrictionsHelpers.GetRuntimeTypeRestriction(Expression, LimitType), Value ); CallSignature sig = BindingHelpers.GetCallSignature(call); ArgumentValues ai = new ArgumentValues(sig, self, args); NewAdapter newAdapter; InitAdapter initAdapter; if (TooManyArgsForDefaultNew(call, args)) { return(MakeIncorrectArgumentsForCallError(call, ai, valInfo)); } else if (Value.UnderlyingSystemType.IsGenericTypeDefinition()) { return(MakeGenericTypeDefinitionError(call, ai, valInfo)); } else if (Value.HasAbstractMethods(PythonContext.GetPythonContext(call).SharedContext)) { return(MakeAbstractInstantiationError(call, ai, valInfo)); } DynamicMetaObject translated = BuiltinFunction.TranslateArguments(call, codeContext, self, args, false, Value.Name); if (translated != null) { return(translated); } GetAdapters(ai, call, codeContext, out newAdapter, out initAdapter); PythonContext state = PythonContext.GetPythonContext(call); // get the expression for calling __new__ DynamicMetaObject createExpr = newAdapter.GetExpression(state.Binder); if (createExpr.Expression.Type == typeof(void)) { return(BindingHelpers.AddDynamicTestAndDefer( call, createExpr, args, valInfo )); } Expression res; BindingRestrictions additionalRestrictions = BindingRestrictions.Empty; if (!Value.IsSystemType && (!(newAdapter is DefaultNewAdapter) || HasFinalizer(call))) { // we need to dynamically check the return value to see if it's a subtype of // the type that we are calling. If it is then we need to call __init__/__del__ // for the actual returned type. res = DynamicExpression.Dynamic( Value.GetLateBoundInitBinder(sig), typeof(object), ArrayUtils.Insert( codeContext, Expression.Convert(createExpr.Expression, typeof(object)), DynamicUtils.GetExpressions(args) ) ); additionalRestrictions = createExpr.Restrictions; } else { // just call the __init__ method, built-in types currently have // no wacky return values which don't return the derived type. // then get the statement for calling __init__ ParameterExpression allocatedInst = Ast.Variable(createExpr.GetLimitType(), "newInst"); Expression tmpRead = allocatedInst; DynamicMetaObject initCall = initAdapter.MakeInitCall( state.Binder, new RestrictedMetaObject( AstUtils.Convert(allocatedInst, Value.UnderlyingSystemType), createExpr.Restrictions ) ); List <Expression> body = new List <Expression>(); Debug.Assert(!HasFinalizer(call)); // add the call to init if we need to if (initCall.Expression != tmpRead) { // init can fail but if __new__ returns a different type // no exception is raised. DynamicMetaObject initStmt = initCall; if (body.Count == 0) { body.Add( Ast.Assign(allocatedInst, createExpr.Expression) ); } if (!Value.UnderlyingSystemType.IsAssignableFrom(createExpr.Expression.Type)) { // return type of object, we need to check the return type before calling __init__. body.Add( AstUtils.IfThen( Ast.TypeIs(allocatedInst, Value.UnderlyingSystemType), initStmt.Expression ) ); } else { // just call the __init__ method, no type check necessary (TODO: need null check?) body.Add(initStmt.Expression); } } // and build the target from everything we have if (body.Count == 0) { res = createExpr.Expression; } else { body.Add(allocatedInst); res = Ast.Block(body); } res = Ast.Block(new ParameterExpression[] { allocatedInst }, res); additionalRestrictions = initCall.Restrictions; } return(BindingHelpers.AddDynamicTestAndDefer( call, new DynamicMetaObject( res, self.Restrictions.Merge(additionalRestrictions) ), ArrayUtils.Insert(this, args), valInfo )); }
private DynamicMetaObject InvokeWorker(DynamicMetaObjectBinder /*!*/ callAction, DynamicMetaObject /*!*/[] args) { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "Method Invoke " + args.Length); PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "Method"); CallSignature signature = BindingHelpers.GetCallSignature(callAction); DynamicMetaObject self = Restrict(typeof(Method)); BindingRestrictions restrictions = self.Restrictions; DynamicMetaObject func = GetMetaFunction(self); DynamicMetaObject call; if (Value.im_self == null) { // restrict to null self (Method is immutable so this is an invariant test) restrictions = restrictions.Merge( BindingRestrictions.GetExpressionRestriction( Ast.Equal( GetSelfExpression(self), AstUtils.Constant(null) ) ) ); if (args.Length == 0) { // this is an error, we pass null which will throw the normal error call = new DynamicMetaObject( Ast.Call( typeof(PythonOps).GetMethod("MethodCheckSelf"), PythonContext.GetCodeContext(callAction), self.Expression, AstUtils.Constant(null) ), restrictions ); } else { // this may or may not be an error call = new DynamicMetaObject( Ast.Block( MakeCheckSelf(callAction, signature, args), Ast.Dynamic( PythonContext.GetPythonContext(callAction).Invoke( BindingHelpers.GetCallSignature(callAction) ).GetLightExceptionBinder(callAction.SupportsLightThrow()), typeof(object), ArrayUtils.Insert(PythonContext.GetCodeContext(callAction), DynamicUtils.GetExpressions(ArrayUtils.Insert(func, args))) ) ), BindingRestrictions.Empty ); /*call = func.Invoke(callAction, ArrayUtils.Insert(func, args)); * call = new MetaObject( * Ast.Comma( * Ast.Call( * typeof(PythonOps).GetMethod("MethodCheckSelf"), * self.Expression, * args[0].Expression * ), * call.Expression * ), * call.Restrictions * );*/ } } else { // restrict to non-null self (Method is immutable so this is an invariant test) restrictions = restrictions.Merge( BindingRestrictions.GetExpressionRestriction( Ast.NotEqual( GetSelfExpression(self), AstUtils.Constant(null) ) ) ); DynamicMetaObject im_self = GetMetaSelf(self); DynamicMetaObject[] newArgs = ArrayUtils.Insert(func, im_self, args); CallSignature newSig = new CallSignature(ArrayUtils.Insert(new Argument(ArgumentType.Simple), signature.GetArgumentInfos())); call = new DynamicMetaObject( Ast.Dynamic( PythonContext.GetPythonContext(callAction).Invoke( newSig ).GetLightExceptionBinder(callAction.SupportsLightThrow()), typeof(object), ArrayUtils.Insert(PythonContext.GetCodeContext(callAction), DynamicUtils.GetExpressions(newArgs)) ), BindingRestrictions.Empty ); /* * call = func.Invoke( * new CallBinder( * PythonContext.GetBinderState(callAction), * newSig * ), * newArgs * );*/ } if (call.HasValue) { return(new DynamicMetaObject( call.Expression, restrictions.Merge(call.Restrictions), call.Value )); } else { return(new DynamicMetaObject( call.Expression, restrictions.Merge(call.Restrictions) )); } }
private MSAst.Expression AddFinally(MSAst.Expression /*!*/ body) { if (Finally != null) { MSAst.ParameterExpression tryThrows = Ast.Variable(typeof(Exception), "$tryThrows"); MSAst.ParameterExpression locException = Ast.Variable(typeof(Exception), "$localException"); MSAst.Expression? @finally = Finally; // lots is going on here. We need to consider: // 1. Exceptions propagating out of try/except/finally. Here we need to save the line # // from the exception block and not save the # from the finally block later. // 2. Exceptions propagating out of the finally block. Here we need to report the line number // from the finally block and leave the existing stack traces cleared. // 3. Returning from the try block: Here we need to run the finally block and not update the // line numbers. body = AstUtils.Try( // we use a fault to know when we have an exception and when control leaves normally (via // either a return or the body completing successfully). AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(HeaderIndex))), Ast.Assign(tryThrows, AstUtils.Constant(null, typeof(Exception))), body, AstUtils.Empty() ).Catch( locException, Expression.Block( // If there was no except block, or the except block threw, then the // exception has not yet been properly set, so we need to set the // currently handled exception when we catch it Ast.Call(AstMethods.SetCurrentException, Parent.LocalContext, locException), Ast.Assign(tryThrows, locException), Expression.Rethrow() ) ) ).FinallyWithJumps( // if we had an exception save the line # that was last executing during the try AstUtils.If( Expression.NotEqual(tryThrows, Expression.Default(typeof(Exception))), Parent.GetSaveLineNumberExpression(tryThrows, false) ), // clear the frames incase thae finally throws, and allow line number // updates to proceed UpdateLineUpdated(false), // run the finally code // if the finally block reraises the same exception we have been handling, // mark it as already updated AstUtils.Try( @finally ).Catch( locException, AstUtils.If( Expression.Equal(locException, tryThrows), UpdateLineUpdated(true) ), Expression.Rethrow() ), // if we took an exception in the try block we have saved the line number. Otherwise // we have no line number saved and will need to continue saving them if // other exceptions are thrown. AstUtils.If( Expression.NotEqual(tryThrows, Expression.Default(typeof(Exception))), UpdateLineUpdated(true) ) ); body = Ast.Block(new[] { tryThrows }, body); } return(body); }
private Microsoft.Scripting.Ast.LightExpression <Func <CodeContext, CodeContext> > MakeClassBody() { // we always need to create a nested context for class defs var init = new List <MSAst.Expression>(); var locals = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression>(); locals.Add(LocalCodeContextVariable); locals.Add(PythonAst._globalContext); init.Add(Ast.Assign(PythonAst._globalContext, new GetGlobalContextExpression(_parentContextParam))); GlobalParent.PrepareScope(locals, init); CreateVariables(locals, init); var createLocal = CreateLocalContext(_parentContextParam); init.Add(Ast.Assign(LocalCodeContextVariable, createLocal)); List <MSAst.Expression> statements = new List <MSAst.Expression>(); // Create the body MSAst.Expression bodyStmt = _body; // __module__ = __name__ MSAst.Expression modStmt = AssignValue(GetVariableExpression(_modVariable), GetVariableExpression(_modNameVariable)); string doc = GetDocumentation(_body); if (doc != null) { statements.Add( AssignValue( GetVariableExpression(_docVariable), AstUtils.Constant(doc) ) ); } if (_body.CanThrow && GlobalParent.PyContext.PythonOptions.Frames) { bodyStmt = AddFrame(LocalContext, FuncCodeExpr, bodyStmt); locals.Add(FunctionStackVariable); } bodyStmt = WrapScopeStatements( Ast.Block( Ast.Block(init), statements.Count == 0 ? EmptyBlock : Ast.Block(new ReadOnlyCollection <MSAst.Expression>(statements)), modStmt, bodyStmt, LocalContext ), _body.CanThrow ); var lambda = AstUtils.LightLambda <Func <CodeContext, CodeContext> >( typeof(CodeContext), Ast.Block( locals, bodyStmt ), Name + "$" + Interlocked.Increment(ref _classId), new[] { _parentContextParam } ); return(lambda); }
static Statement Rewrite(Statement body) { if (body is LabeledStatement) { var ls = (LabeledStatement)body; return(ls.Mark(Rewrite(ls.Statement))); } if (body is BlockStatement) { var node = body as BlockStatement; var newbody = new List <Statement>(); foreach (var stmt in node.Statements) { var ns = Rewrite(stmt); if (ns is BlockStatement) { newbody.AddRange(((BlockStatement)ns).Statements); } else { newbody.Add(ns); } } return(Ast.Block(newbody)); } if (body is WriteStatement) { var ws = (WriteStatement)body; if (ws.Value is CommaExpression) { var ce = ws.Value as CommaExpression; var block = RewriteExpressions(ce.Expressions, x => Ast.Write(ws.Variable, x)); return(Rewrite(Ast.Block(block))); } else if (ws.Value == null) { var cb = ws.Variable.Block; if (cb != null) { cb.RemoveVariables(new List <Variable>(new[] { ws.Variable })); cb.Bind(); } return(Ast.Empty()); } else if (ws.Variable.Block == null) { //var cb = ws.Variable.Block; //cb.RemoveVariables(new List<Variable>(new[] { ws.Variable })); //cb.Bind(); return(Ast.Empty()); } else if (ws.HasNoRef) { //var cb = ws.Variable.Block; //cb.RemoveVariables(new List<Variable>(new[] { ws.Variable })); //cb.Bind(); //return Ast.Empty(); } } if (body is ReturnStatement) { var rs = body as ReturnStatement; if (rs.Expression is UnaryExpression && rs.Expression.NodeType == AstNodeType.Convert) { var ux = (UnaryExpression)rs.Expression; var op = ux.Operand; if (op is VoidExpression) { return(Rewrite(op as VoidExpression)); } if (op is CommaExpression) { var ce = op as CommaExpression; var block = RewriteExpressions(ce.Expressions, x => Ast.Return(Ast.ConvertHelper(x, ux.Type))); return(Rewrite(Ast.Block(block))); } } if (rs.Expression is CommaExpression) { var ce = rs.Expression as CommaExpression; var le = ce.Expressions[ce.Expressions.Count - 1]; if (le is VoidExpression && ((VoidExpression)le).Statement is ContinueStatement) { var block = RewriteExpressions(ce.Expressions, x => Ast.Continue()); return(Rewrite(Ast.Block(block))); } else { var block = RewriteExpressions(ce.Expressions, Ast.Return); return(Rewrite(Ast.Block(block))); } } if (rs.Expression is VoidExpression) { var ve = rs.Expression as VoidExpression; return(Rewrite(ve)); } if (rs.Expression is MethodCallExpression) { var mce = rs.Expression as MethodCallExpression; var uce = Unwrap(mce.Instance); if (uce is CommaExpression) { var ce = uce as CommaExpression; var block = RewriteExpressions(ce.Expressions, x => { if (mce.Arguments.Count == 1 && mce.Arguments[0].Type == typeof(object[])) { var mc = Ast.SimpleCallHelper(x, mce.Method, mce.Arguments.ToArray()); mc.TailCall = mce.TailCall; return(Ast.Return(mc)); } else { //var args = Array.ConvertAll(mce.Arguments.ToArray(), y => Ast.ConvertHelper(y, typeof(object))); var mc = Ast.SimpleCallHelper(x, mce.Method, mce.Arguments.ToArray()); mc.TailCall = mce.TailCall; return(Ast.Return(mc)); } }); return(Rewrite(Ast.Block(block))); } } } if (body is ExpressionStatement) { var es = (ExpressionStatement)body; var trs = TryRewriteExpression(es.Expression); if (trs != null) { return(trs); } } if (body is IfStatement) { var ifs = (IfStatement)body; if (ifs.ElseStatement == null) { return(Ast.If(ifs.Tests[0].Test, Rewrite(ifs.Tests[0].Body)).ToStatement()); } else { return(Ast.If(ifs.Tests[0].Test, Rewrite(ifs.Tests[0].Body)).Else(Rewrite(ifs.ElseStatement))); } } return(body); }
private void MakePropertyRule(SetOrDeleteMemberInfo memInfo, DynamicMetaObject instance, DynamicMetaObject target, Type targetType, MemberGroup properties) { PropertyTracker info = (PropertyTracker)properties[0]; MethodInfo setter = info.GetSetMethod(true); // Allow access to protected getters TODO: this should go, it supports IronPython semantics. if (setter != null && !setter.IsPublic && !setter.IsProtected()) { if (!PrivateBinding) { setter = null; } } if (setter != null) { setter = CompilerHelpers.GetCallableMethod(setter, PrivateBinding); if (info.IsStatic != (instance == null)) { memInfo.Body.FinishCondition( MakeError( MakeStaticPropertyInstanceAccessError( info, true, instance, target ), typeof(object) ) ); } else if (info.IsStatic && info.DeclaringType != targetType) { memInfo.Body.FinishCondition( MakeError( MakeStaticAssignFromDerivedTypeError(targetType, instance, info, target, memInfo.ResolutionFactory), typeof(object) ) ); } else if (setter.ContainsGenericParameters) { memInfo.Body.FinishCondition( MakeGenericPropertyExpression(memInfo) ); } else if (setter.IsPublic && !setter.DeclaringType.IsValueType) { if (instance == null) { memInfo.Body.FinishCondition( Ast.Block( AstUtils.SimpleCallHelper( setter, ConvertExpression( target.Expression, setter.GetParameters()[0].ParameterType, ConversionResultKind.ExplicitCast, memInfo.ResolutionFactory ) ), Ast.Constant(null) ) ); } else { memInfo.Body.FinishCondition( MakeReturnValue( MakeCallExpression(memInfo.ResolutionFactory, setter, instance, target), target ) ); } } else { // TODO: Should be able to do better w/ value types. memInfo.Body.FinishCondition( MakeReturnValue( Ast.Call( AstUtils.Constant(((ReflectedPropertyTracker)info).Property), // TODO: Private binding on extension properties typeof(PropertyInfo).GetMethod("SetValue", new Type[] { typeof(object), typeof(object), typeof(object[]) }), instance == null ? AstUtils.Constant(null) : AstUtils.Convert(instance.Expression, typeof(object)), AstUtils.Convert( ConvertExpression( target.Expression, setter.GetParameters()[0].ParameterType, ConversionResultKind.ExplicitCast, memInfo.ResolutionFactory ), typeof(object) ), Ast.NewArrayInit(typeof(object)) ), target ) ); } } else { memInfo.Body.FinishCondition( MakeError( MakeMissingMemberErrorForAssignReadOnlyProperty(targetType, instance, memInfo.Name), typeof(object) ) ); } }
public override MSAst.Expression Reduce() { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression lineUpdated = null; MSAst.ParameterExpression runElse = null; if (_else != null || (_handlers != null && _handlers.Length > 0)) { lineUpdated = Ast.Variable(typeof(bool), "$lineUpdated_try"); if (_else != null) { runElse = Ast.Variable(typeof(bool), "run_else"); } } // don't allocate locals below here... MSAst.Expression body = _body; MSAst.Expression @else = _else; MSAst.Expression @catch, result; MSAst.ParameterExpression exception; if (_handlers != null && _handlers.Length > 0) { exception = Ast.Variable(typeof(Exception), "$exception"); @catch = TransformHandlers(exception); } else { exception = null; @catch = null; } // We have else clause, must generate guard around it if (@else != null) { Debug.Assert(@catch != null); // run_else = true; // try { // try_body // } catch ( ... ) { // run_else = false; // catch_body // } // if (run_else) { // else_body // } result = Ast.Block( Ast.Assign(runElse, AstUtils.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. PushLineUpdated(false, lineUpdated), LightExceptions.RewriteExternal( AstUtils.Try( Parent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(_headerIndex))), body, AstUtils.Constant(null) ).Catch(exception, Ast.Assign(runElse, AstUtils.Constant(false)), @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ), AstUtils.IfThen(runElse, @else ), AstUtils.Empty() ); } else if (@catch != null) // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // { result = LightExceptions.RewriteExternal( AstUtils.Try( GlobalParent.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, GlobalParent.IndexToLocation(_headerIndex))), // save existing line updated PushLineUpdated(false, lineUpdated), body, AstUtils.Constant(null) ).Catch(exception, @catch, // restore existing line updated after exception handler completes PopLineUpdated(lineUpdated), Ast.Assign(exception, Ast.Constant(null, typeof(Exception))), AstUtils.Constant(null) ) ); } else { result = body; } return(Ast.Block( GetVariables(lineUpdated, runElse), AddFinally(result), AstUtils.Default(typeof(void)) )); }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { return(Ast.Block(Transform(gen), AstUtils.Constant(null))); }
private DynamicMetaObject /*!*/ InvokeWorker(DynamicMetaObjectBinder /*!*/ invoke, Expression /*!*/ codeContext, DynamicMetaObject /*!*/[] args) { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass Invoke"); DynamicMetaObject self = Restrict(typeof(OldInstance)); Expression[] exprArgs = new Expression[args.Length + 1]; for (int i = 0; i < args.Length; i++) { exprArgs[i + 1] = args[i].Expression; } ParameterExpression tmp = Ast.Variable(typeof(object), "callFunc"); exprArgs[0] = tmp; return(new DynamicMetaObject( // we could get better throughput w/ a more specific rule against our current custom old class but // this favors less code generation. Ast.Block( new ParameterExpression[] { tmp }, Ast.Condition( Expression.Not( Expression.TypeIs( Expression.Assign( tmp, Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceTryGetBoundCustomMember)), codeContext, self.Expression, AstUtils.Constant("__call__") ) ), typeof(OperationFailed) ) ), Ast.Block( Utils.Try( Ast.Call(typeof(PythonOps).GetMethod(nameof(PythonOps.FunctionPushFrameCodeContext)), codeContext), Ast.Assign( tmp, DynamicExpression.Dynamic( PythonContext.GetPythonContext(invoke).Invoke( BindingHelpers.GetCallSignature(invoke) ), typeof(object), ArrayUtils.Insert(codeContext, exprArgs) ) ) ).Finally( Ast.Call(typeof(PythonOps).GetMethod(nameof(PythonOps.FunctionPopFrame))) ), tmp ), Utils.Convert( BindingHelpers.InvokeFallback(invoke, codeContext, this, args).Expression, typeof(object) ) ) ), self.Restrictions.Merge(BindingRestrictions.Combine(args)) )); }
// arguments: complex arguments (expressions, maplets, splat, block) // singleArgument: siple argument (complex are not used) // assignmentRhsArgument: rhs of the assignment: target.method=(rhs) internal static MSA.Expression /*!*/ TransformRead(Expression /*!*/ node, AstGenerator /*!*/ gen, bool hasImplicitSelf, string /*!*/ methodName, MSA.Expression /*!*/ transformedTarget, Arguments arguments, Block block, MSA.Expression singleArgument, MSA.Expression assignmentRhsArgument) { Debug.Assert(assignmentRhsArgument == null || block == null, "Block not allowed in assignment"); Debug.Assert(singleArgument == null || arguments == null && assignmentRhsArgument == null); Assert.NotNull(gen, transformedTarget); Assert.NotEmpty(methodName); // Pass args in this order: // 1. instance // 2. block (if present) // 3. passed args: normal args, maplets, array // 4. RHS of assignment (if present) MSA.Expression blockArgVariable; MSA.Expression transformedBlock; if (block != null) { blockArgVariable = gen.CurrentScope.DefineHiddenVariable("#block-def", typeof(Proc)); transformedBlock = block.Transform(gen); } else { blockArgVariable = transformedBlock = null; } var siteBuilder = new CallSiteBuilder(gen, transformedTarget, blockArgVariable); if (arguments != null) { arguments.TransformToCall(gen, siteBuilder); } else if (singleArgument != null) { siteBuilder.Add(singleArgument); } MSA.Expression rhsVariable = null; if (assignmentRhsArgument != null) { rhsVariable = gen.CurrentScope.DefineHiddenVariable("#rhs", assignmentRhsArgument.Type); siteBuilder.RhsArgument = Ast.Assign(rhsVariable, assignmentRhsArgument); } var dynamicSite = siteBuilder.MakeCallAction(methodName, hasImplicitSelf); #if FEATURE_CALL_SITE_TRACER if (gen.Context.CallSiteCreated != null) { gen.Context.CallSiteCreated(node, dynamicSite); } #endif MSA.Expression result = gen.DebugMark(dynamicSite, methodName); if (block != null) { result = gen.DebugMark(MakeCallWithBlockRetryable(gen, result, blockArgVariable, transformedBlock, block.IsDefinition), "#RB: method call with a block ('" + methodName + "')"); } if (assignmentRhsArgument != null) { result = Ast.Block(result, rhsVariable); } return(result); }
private DynamicMetaObject /*!*/ MakeDynamicMemberAccess(DynamicMetaObjectBinder /*!*/ member, string /*!*/ name, MemberAccess access, DynamicMetaObject /*!*/[] /*!*/ args) { DynamicMetaObject self = Restrict(typeof(OldInstance)); Expression target; ParameterExpression tmp = Ast.Variable(typeof(object), "result"); switch (access) { case MemberAccess.Invoke: target = Ast.Block( new ParameterExpression[] { tmp }, Ast.Condition( Expression.Not( Expression.TypeIs( Expression.Assign( tmp, Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceTryGetBoundCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(name) ) ), typeof(OperationFailed) ) ), ((InvokeMemberBinder)member).FallbackInvoke(new DynamicMetaObject(tmp, BindingRestrictions.Empty), args, null).Expression, AstUtils.Convert( ((InvokeMemberBinder)member).FallbackInvokeMember(this, args).Expression, typeof(object) ) ) ); break; case MemberAccess.Get: target = Ast.Block( new ParameterExpression[] { tmp }, Ast.Condition( Expression.Not( Expression.TypeIs( Expression.Assign( tmp, Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceTryGetBoundCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(name) ) ), typeof(OperationFailed) ) ), tmp, AstUtils.Convert( FallbackGet(member, args), typeof(object) ) ) ); break; case MemberAccess.Set: target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceSetCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(name), AstUtils.Convert(args[1].Expression, typeof(object)) ); break; case MemberAccess.Delete: target = Ast.Call( typeof(PythonOps).GetMethod(nameof(PythonOps.OldInstanceDeleteCustomMember)), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(name) ); break; default: throw new InvalidOperationException(); } return(new DynamicMetaObject( target, self.Restrictions.Merge(BindingRestrictions.Combine(args)) )); }
/// <summary> /// Creates the LambdaExpression which implements the body of the function. /// /// The functions signature is either "object Function(PythonFunction, ...)" /// where there is one object parameter for each user defined parameter or /// object Function(PythonFunction, object[]) for functions which take more /// than PythonCallTargets.MaxArgs arguments. /// </summary> private LightLambdaExpression CreateFunctionLambda() { bool needsWrapperMethod = _parameters.Length > PythonCallTargets.MaxArgs; Delegate originalDelegate; Type delegateType = GetDelegateType(_parameters, needsWrapperMethod, out originalDelegate); MSAst.ParameterExpression localContext = null; ReadOnlyCollectionBuilder <MSAst.ParameterExpression> locals = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression>(); if (NeedsLocalsDictionary || ContainsNestedFreeVariables) { localContext = LocalCodeContextVariable; locals.Add(localContext); } MSAst.ParameterExpression[] parameters = CreateParameters(needsWrapperMethod, locals); List <MSAst.Expression> init = new List <MSAst.Expression>(); foreach (var param in _parameters) { IPythonVariableExpression pyVar = GetVariableExpression(param.PythonVariable) as IPythonVariableExpression; if (pyVar != null) { var varInit = pyVar.Create(); if (varInit != null) { init.Add(varInit); } } } // Transform the parameters. init.Add(Ast.ClearDebugInfo(GlobalParent.Document)); locals.Add(PythonAst._globalContext); init.Add(Ast.Assign(PythonAst._globalContext, new GetGlobalContextExpression(_parentContext))); GlobalParent.PrepareScope(locals, init); // Create variables and references. Since references refer to // parameters, do this after parameters have been created. CreateFunctionVariables(locals, init); // Initialize parameters - unpack tuples. // Since tuples unpack into locals, this must be done after locals have been created. InitializeParameters(init, needsWrapperMethod, parameters); List <MSAst.Expression> statements = new List <MSAst.Expression>(); // add beginning sequence point var start = GlobalParent.IndexToLocation(StartIndex); statements.Add(GlobalParent.AddDebugInfo( AstUtils.Empty(), new SourceSpan(new SourceLocation(0, start.Line, start.Column), new SourceLocation(0, start.Line, Int32.MaxValue)))); // For generators, we need to do a check before the first statement for Generator.Throw() / Generator.Close(). // The exception traceback needs to come from the generator's method body, and so we must do the check and throw // from inside the generator. if (IsGenerator) { MSAst.Expression s1 = YieldExpression.CreateCheckThrowExpression(SourceSpan.None); statements.Add(s1); } MSAst.ParameterExpression extracted = null; if (!IsGenerator && _canSetSysExcInfo) { // need to allocate the exception here so we don't share w/ exceptions made & freed // during the body. extracted = Ast.Parameter(typeof(Exception), "$ex"); locals.Add(extracted); } if (_body.CanThrow && !(_body is SuiteStatement) && _body.StartIndex != -1) { statements.Add(UpdateLineNumber(GlobalParent.IndexToLocation(_body.StartIndex).Line)); } statements.Add(Body); MSAst.Expression body = Ast.Block(statements); // If this function can modify sys.exc_info() (_canSetSysExcInfo), then it must restore the result on finish. // // Wrap in // $temp = PythonOps.SaveCurrentException() // <body> // PythonOps.RestoreCurrentException($temp) // Skip this if we're a generator. For generators, the try finally is handled by the PythonGenerator class // before it's invoked. This is because the restoration must occur at every place the function returns from // a yield point. That's different than the finally semantics in a generator. if (extracted != null) { MSAst.Expression s = AstUtils.Try( Ast.Assign( extracted, Ast.Call(AstMethods.SaveCurrentException) ), body ).Finally( Ast.Call( AstMethods.RestoreCurrentException, extracted ) ); body = s; } if (_body.CanThrow && GlobalParent.PyContext.PythonOptions.Frames) { body = AddFrame(LocalContext, Ast.Property(_functionParam, typeof(PythonFunction).GetProperty("__code__")), body); locals.Add(FunctionStackVariable); } body = AddProfiling(body); body = WrapScopeStatements(body, _body.CanThrow); body = Ast.Block(body, AstUtils.Empty()); body = AddReturnTarget(body); MSAst.Expression bodyStmt = body; if (localContext != null) { var createLocal = CreateLocalContext(_parentContext); init.Add( Ast.Assign( localContext, createLocal ) ); } init.Add(bodyStmt); bodyStmt = Ast.Block(init); // wrap a scope if needed bodyStmt = Ast.Block(locals.ToReadOnlyCollection(), bodyStmt); return(AstUtils.LightLambda( typeof(object), delegateType, AddDefaultReturn(bodyStmt, typeof(object)), Name + "$" + Interlocked.Increment(ref _lambdaId), parameters )); }
internal static DynamicMetaObject Call(DynamicMetaObjectBinder /*!*/ call, DynamicMetaObject target, DynamicMetaObject /*!*/[] /*!*/ args) { Assert.NotNull(call, args); Assert.NotNullItems(args); if (target.NeedsDeferral()) { return(call.Defer(ArrayUtils.Insert(target, args))); } foreach (DynamicMetaObject mo in args) { if (mo.NeedsDeferral()) { RestrictTypes(args); return(call.Defer( ArrayUtils.Insert(target, args) )); } } DynamicMetaObject self = target.Restrict(target.GetLimitType()); ValidationInfo valInfo = BindingHelpers.GetValidationInfo(target); PythonType pt = DynamicHelpers.GetPythonType(target.Value); PythonContext pyContext = PythonContext.GetPythonContext(call); // look for __call__, if it's present dispatch to it. Otherwise fall back to the // default binder PythonTypeSlot callSlot; if (!typeof(Delegate).IsAssignableFrom(target.GetLimitType()) && pt.TryResolveSlot(pyContext.SharedContext, "__call__", out callSlot)) { ConditionalBuilder cb = new ConditionalBuilder(call); Expression body; callSlot.MakeGetExpression( pyContext.Binder, PythonContext.GetCodeContext(call), self, GetPythonType(self), cb ); if (!cb.IsFinal) { cb.FinishCondition(GetCallError(call, self)); } Expression[] callArgs = ArrayUtils.Insert( PythonContext.GetCodeContext(call), cb.GetMetaObject().Expression, DynamicUtils.GetExpressions(args) ); body = DynamicExpression.Dynamic( PythonContext.GetPythonContext(call).Invoke( BindingHelpers.GetCallSignature(call) ), typeof(object), callArgs ); body = Ast.TryFinally( Ast.Block( Ast.Call(typeof(PythonOps).GetMethod("FunctionPushFrame"), Ast.Constant(pyContext)), body ), Ast.Call(typeof(PythonOps).GetMethod("FunctionPopFrame")) ); return(BindingHelpers.AddDynamicTestAndDefer( call, new DynamicMetaObject(body, self.Restrictions.Merge(BindingRestrictions.Combine(args))), args, valInfo )); } return(null); }
internal static MSAst.Expression TransformFor(ScopeStatement parent, MSAst.ParameterExpression enumerator, Expression list, Expression left, MSAst.Expression body, Statement else_, SourceSpan span, SourceLocation header, MSAst.LabelTarget breakLabel, MSAst.LabelTarget continueLabel, bool isStatement) { // enumerator, isDisposable = Dynamic(GetEnumeratorBinder, list) MSAst.Expression init = Ast.Assign( enumerator, new PythonDynamicExpression1 <KeyValuePair <IEnumerator, IDisposable> >( Binders.UnaryOperationBinder( parent.GlobalParent.PyContext, PythonOperationKind.GetEnumeratorForIteration ), parent.GlobalParent.CompilationMode, AstUtils.Convert(list, typeof(object)) ) ); // while enumerator.MoveNext(): // left = enumerator.Current // body // else: // else MSAst.Expression ls = AstUtils.Loop( parent.GlobalParent.AddDebugInfo( Ast.Call( Ast.Property( enumerator, typeof(KeyValuePair <IEnumerator, IDisposable>).GetProperty("Key") ), typeof(IEnumerator).GetMethod("MoveNext") ), left.Span ), null, Ast.Block( left.TransformSet( SourceSpan.None, Ast.Call( Ast.Property( enumerator, typeof(KeyValuePair <IEnumerator, IDisposable>).GetProperty("Key") ), typeof(IEnumerator).GetProperty("Current").GetGetMethod() ), PythonOperationKind.None ), body, isStatement ? UpdateLineNumber(parent.GlobalParent.IndexToLocation(list.StartIndex).Line) : AstUtils.Empty(), AstUtils.Empty() ), else_, breakLabel, continueLabel ); return(Ast.Block( init, Ast.TryFinally( ls, Ast.Call(AstMethods.ForLoopDispose, enumerator) ) )); }
private DynamicMetaObject /*!*/ MakeSetMember(SetMemberBinder /*!*/ member, DynamicMetaObject /*!*/ value) { PythonContext state = PythonContext.GetPythonContext(member); DynamicMetaObject self = Restrict(Value.GetType()); if (Value.GetType() != typeof(PythonType) && DynamicHelpers.GetPythonType(Value).IsSystemType) { // built-in subclass of .NET type. Usually __setattr__ is handled by MetaUserObject // but we can have a built-in subtype that's not a user type. PythonTypeSlot pts; if (Value.TryGetCustomSetAttr(state.SharedContext, out pts)) { Debug.Assert(pts.GetAlwaysSucceeds); ParameterExpression tmp = Ast.Variable(typeof(object), "boundVal"); return(BindingHelpers.AddDynamicTestAndDefer( member, new DynamicMetaObject( Ast.Block( new[] { tmp }, DynamicExpression.Dynamic( state.Invoke(new CallSignature(2)), typeof(object), AstUtils.Constant(state.SharedContext), Ast.Block( Ast.Call( typeof(PythonOps).GetMethod("SlotTryGetValue"), AstUtils.Constant(state.SharedContext), AstUtils.Convert(AstUtils.WeakConstant(pts), typeof(PythonTypeSlot)), AstUtils.Convert(Expression, typeof(object)), AstUtils.Convert(AstUtils.WeakConstant(DynamicHelpers.GetPythonType(Value)), typeof(PythonType)), tmp ), tmp ), Ast.Constant(member.Name), value.Expression ) ), self.Restrictions ), new DynamicMetaObject[] { this, value }, TestUserType() )); } } return(BindingHelpers.AddDynamicTestAndDefer( member, new DynamicMetaObject( Ast.Call( typeof(PythonOps).GetMethod("PythonTypeSetCustomMember"), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(member.Name), AstUtils.Convert( value.Expression, typeof(object) ) ), self.Restrictions.Merge(value.Restrictions) ), new DynamicMetaObject[] { this, value }, TestUserType() )); }
internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expression right, PythonOperationKind op) { // if we just have a simple named multi-assignment (e.g. a, b = 1,2) // then go ahead and step over the entire statement at once. If we have a // more complex statement (e.g. a.b, c.d = 1, 2) then we'll step over the // sets individually as they could be property sets the user wants to step // into. TODO: Enable stepping of the right hand side? bool emitIndividualSets = false; foreach (Expression e in _items) { if (IsComplexAssignment(e)) { emitIndividualSets = true; break; } } SourceSpan rightSpan = SourceSpan.None; SourceSpan leftSpan = (Span.Start.IsValid && span.IsValid) ? new SourceSpan(Span.Start, span.End) : SourceSpan.None; SourceSpan totalSpan = SourceSpan.None; if (emitIndividualSets) { rightSpan = span; leftSpan = SourceSpan.None; totalSpan = (Span.Start.IsValid && span.IsValid) ? new SourceSpan(Span.Start, span.End) : SourceSpan.None; } // 1. Evaluate the expression and assign the value to the temp. MSAst.ParameterExpression right_temp = Ast.Variable(typeof(object), "unpacking"); // 2. Add the assignment "right_temp = right" into the suite/block MSAst.Expression assignStmt1 = MakeAssignment(right_temp, right); int expected = _items.Length; int argcntafter = -1; for (var i = 0; i < _items.Length; i++) { var item = _items[i]; if (item is StarredExpression) { expected = i; argcntafter = _items.Length - i - 1; break; } } // 3. Call GetEnumeratorValues on the right side (stored in temp) MSAst.Expression enumeratorValues = Expression.Convert(LightExceptions.CheckAndThrow( Expression.Call( // method argcntafter != -1 ? AstMethods.UnpackIterable : emitIndividualSets ? AstMethods.GetEnumeratorValues : AstMethods.GetEnumeratorValuesNoComplexSets, // arguments Parent.LocalContext, right_temp, AstUtils.Constant(expected), AstUtils.Constant(argcntafter) ) ), typeof(object[])); // 4. Create temporary variable for the array MSAst.ParameterExpression array_temp = Ast.Variable(typeof(object[]), "array"); // 5. Assign the value of the method call (mce) into the array temp // And add the assignment "array_temp = Ops.GetEnumeratorValues(...)" into the block MSAst.Expression assignStmt2 = MakeAssignment( array_temp, enumeratorValues, rightSpan ); ReadOnlyCollectionBuilder <MSAst.Expression> sets = new ReadOnlyCollectionBuilder <MSAst.Expression>(_items.Length + 1); for (int i = 0; i < _items.Length; i++) { // target = array_temp[i] Expression target = _items[i]; if (target == null) { continue; } // 6. array_temp[i] MSAst.Expression element = Ast.ArrayAccess( array_temp, // array expression AstUtils.Constant(i) // index ); // 7. target = array_temp[i], and add the transformed assignment into the list of sets MSAst.Expression set = target.TransformSet( emitIndividualSets ? // span target.Span : SourceSpan.None, element, PythonOperationKind.None ); sets.Add(set); } // 9. add the sets as their own block so they can be marked as a single span, if necessary. sets.Add(AstUtils.Empty()); MSAst.Expression itemSet = GlobalParent.AddDebugInfo(Ast.Block(sets.ToReadOnlyCollection()), leftSpan); // 10. Return the suite statement (block) return(GlobalParent.AddDebugInfo(Ast.Block(new[] { array_temp, right_temp }, assignStmt1, assignStmt2, itemSet, AstUtils.Empty()), totalSpan)); }
static Expression RewriteReturn(Statement statement) { if (statement is BlockStatement) { BlockStatement bs = (BlockStatement)statement; List <Statement> newbody = new List <Statement>(bs.Statements); Statement last = newbody[newbody.Count - 1]; newbody.RemoveAt(newbody.Count - 1); Statement fb = FlattenStatement(Ast.Block(newbody)); Expression eb = Ast.Void(fb); if (fb is ExpressionStatement) { eb = ((ExpressionStatement)fb).Expression; } return(Ast.Comma(eb, Unwrap(RewriteReturn(last)))); } if (statement is ReturnStatement) { Expression e = ((ReturnStatement)statement).Expression; if (e is MethodCallExpression) { ((MethodCallExpression)e).TailCall = false; } if (e.Type != typeof(object)) { return(e); } else { return(Unwrap(e)); } } if (statement is IfStatement) { IfStatement ifs = (IfStatement)statement; Debug.Assert(ifs.Tests.Count == 1); var a = Unwrap(RewriteReturn(ifs.Tests[0].Body)); var b = Unwrap(RewriteReturn(ifs.ElseStatement)); if (a.Type != b.Type) { a = Ast.ConvertHelper(a, typeof(object)); b = Ast.ConvertHelper(b, typeof(object)); } return(Ast.Condition(ifs.Tests[0].Test, a, b)); } if (statement is LabeledStatement) { var ls = statement as LabeledStatement; return(Ast.Void(ls)); } throw new ArgumentException("Unexpected"); }
private DynamicMetaObject /*!*/ MakeGetMember(DynamicMetaObjectBinder /*!*/ member, DynamicMetaObject codeContext) { PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "OldClass GetMember"); PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "OldClass GetMember"); DynamicMetaObject self = Restrict(typeof(OldClass)); Expression target; string memberName = GetGetMemberName(member); switch (memberName) { case "__dict__": target = Ast.Block( Ast.Call( typeof(PythonOps).GetMethod("OldClassDictionaryIsPublic"), self.Expression ), Ast.Call( typeof(PythonOps).GetMethod("OldClassGetDictionary"), self.Expression ) ); break; case "__bases__": target = Ast.Call( typeof(PythonOps).GetMethod("OldClassGetBaseClasses"), self.Expression ); break; case "__name__": target = Ast.Call( typeof(PythonOps).GetMethod("OldClassGetName"), self.Expression ); break; default: ParameterExpression tmp = Ast.Variable(typeof(object), "lookupVal"); return(new DynamicMetaObject( Ast.Block( new ParameterExpression[] { tmp }, Ast.Condition( Expression.Not( Expression.TypeIs( Expression.Assign( tmp, Ast.Call( typeof(PythonOps).GetMethod("OldClassTryLookupValue"), AstUtils.Constant(PythonContext.GetPythonContext(member).SharedContext), self.Expression, AstUtils.Constant(memberName) ) ), typeof(OperationFailed) ) ), tmp, AstUtils.Convert( GetMemberFallback(this, member, codeContext).Expression, typeof(object) ) ) ), self.Restrictions )); } return(new DynamicMetaObject( target, self.Restrictions )); }
internal MSA.Expression <T> /*!*/ Transform <T>(AstGenerator /*!*/ gen) { Debug.Assert(gen != null); ScopeBuilder scope = DefineLocals(); MSA.ParameterExpression[] parameters; MSA.ParameterExpression selfVariable; MSA.ParameterExpression runtimeScopeVariable; MSA.ParameterExpression blockParameter; if (gen.CompilerOptions.FactoryKind == TopScopeFactoryKind.None || gen.CompilerOptions.FactoryKind == TopScopeFactoryKind.ModuleEval) { parameters = new MSA.ParameterExpression[4]; runtimeScopeVariable = parameters[0] = Ast.Parameter(typeof(RubyScope), "#scope"); selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self"); parameters[2] = Ast.Parameter(typeof(RubyModule), "#module"); blockParameter = parameters[3] = Ast.Parameter(typeof(Proc), "#block"); } else { parameters = new MSA.ParameterExpression[2]; runtimeScopeVariable = parameters[0] = Ast.Parameter(typeof(RubyScope), "#scope"); selfVariable = parameters[1] = Ast.Parameter(typeof(object), "#self"); blockParameter = null; } gen.EnterSourceUnit( scope, selfVariable, runtimeScopeVariable, blockParameter, gen.CompilerOptions.TopLevelMethodName, // method name for blocks null // parameters for super calls ); MSA.Expression body; if (_statements.Count > 0) { if (gen.PrintInteractiveResult) { var resultVariable = scope.DefineHiddenVariable("#result", typeof(object)); var epilogue = Methods.PrintInteractiveResult.OpCall(runtimeScopeVariable, AstUtils.LightDynamic(ConvertToSAction.Make(gen.Context), typeof(MutableString), CallSiteBuilder.InvokeMethod(gen.Context, "inspect", RubyCallSignature.WithScope(0), gen.CurrentScopeVariable, resultVariable ) ) ); body = gen.TransformStatements(null, _statements, epilogue, ResultOperation.Store(resultVariable)); } else { body = gen.TransformStatements(_statements, ResultOperation.Return); } // TODO: var exceptionVariable = Ast.Parameter(typeof(Exception), "#exception"); body = AstUtils.Try( body ).Filter(exceptionVariable, Methods.TraceTopLevelCodeFrame.OpCall(runtimeScopeVariable, exceptionVariable), Ast.Empty() ); } else { body = AstUtils.Constant(null); } // scope initialization: MSA.Expression prologue; switch (gen.CompilerOptions.FactoryKind) { case TopScopeFactoryKind.None: case TopScopeFactoryKind.ModuleEval: prologue = Methods.InitializeScopeNoLocals.OpCall(runtimeScopeVariable, EnterInterpretedFrameExpression.Instance); break; case TopScopeFactoryKind.Hosted: case TopScopeFactoryKind.File: case TopScopeFactoryKind.WrappedFile: prologue = Methods.InitializeScope.OpCall( runtimeScopeVariable, scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), EnterInterpretedFrameExpression.Instance ); break; case TopScopeFactoryKind.Main: prologue = Methods.InitializeScope.OpCall( runtimeScopeVariable, scope.MakeLocalsStorage(), scope.GetVariableNamesExpression(), EnterInterpretedFrameExpression.Instance ); if (_dataOffset >= 0) { prologue = Ast.Block( prologue, Methods.SetDataConstant.OpCall( runtimeScopeVariable, gen.SourcePathConstant, AstUtils.Constant(_dataOffset) ) ); } break; default: throw Assert.Unreachable; } // BEGIN blocks: if (gen.FileInitializers != null) { var b = new AstBlock(); b.Add(prologue); b.Add(gen.FileInitializers); b.Add(body); body = b; } body = gen.AddReturnTarget(scope.CreateScope(body)); gen.LeaveSourceUnit(); return(Ast.Lambda <T>(body, GetEncodedName(gen), parameters)); }
/// <summary> /// Transform multiple python except handlers for a try block into a single catch body. /// </summary> /// <param name="exception">The variable for the exception in the catch block.</param> /// <returns>Null if there are no except handlers. Else the statement to go inside the catch handler</returns> private MSAst.Expression TransformHandlers(MSAst.ParameterExpression exception) { Assert.NotEmpty(_handlers); MSAst.ParameterExpression extracted = Ast.Variable(typeof(object), "$extracted"); var tests = new List <Microsoft.Scripting.Ast.IfStatementTest>(_handlers.Length); MSAst.ParameterExpression converted = null; MSAst.Expression catchAll = null; for (int index = 0; index < _handlers.Length; index++) { TryStatementHandler tsh = _handlers[index]; if (tsh.Test != null) { Microsoft.Scripting.Ast.IfStatementTest ist; // translating: // except Test ... // // generate following AST for the Test (common part): // CheckException(exception, Test) MSAst.Expression test = Ast.Call( AstMethods.CheckException, Parent.LocalContext, extracted, AstUtils.Convert(tsh.Test, typeof(object)) ); if (tsh.Target != null) { // translating: // except Test as Target: // <body> // into: // if ((converted = CheckException(exception, Test)) != null) { // try { // Target = converted; // traceback-header // <body> // } // finally { // del Target // } // } if (converted == null) { converted = Ast.Variable(typeof(object), "$converted"); } ist = AstUtils.IfCondition( Ast.NotEqual( Ast.Assign(converted, test), AstUtils.Constant(null) ), Ast.TryFinally( Ast.Block( tsh.Target.TransformSet(SourceSpan.None, converted, PythonOperationKind.None), GlobalParent.AddDebugInfo( GetTracebackHeader( this, exception, tsh.Body ), new SourceSpan(GlobalParent.IndexToLocation(tsh.StartIndex), GlobalParent.IndexToLocation(tsh.HeaderIndex)) ), AstUtils.Empty() ), Ast.Block( tsh.Target.TransformSet(SourceSpan.None, AstUtils.Constant(null), PythonOperationKind.None), tsh.Target.TransformDelete() ) ) ); } else { // translating: // except Test: // <body> // into: // if (CheckException(exception, Test) != null) { // traceback-header // <body> // } ist = AstUtils.IfCondition( Ast.NotEqual( test, AstUtils.Constant(null) ), GlobalParent.AddDebugInfo( GetTracebackHeader( this, exception, tsh.Body ), new SourceSpan(GlobalParent.IndexToLocation(tsh.StartIndex), GlobalParent.IndexToLocation(tsh.HeaderIndex)) ) ); } // Add the test to the if statement test cascade tests.Add(ist); } else { Debug.Assert(index == _handlers.Length - 1); Debug.Assert(catchAll == null); // translating: // except: // <body> // into: // { // traceback-header // <body> // } catchAll = GlobalParent.AddDebugInfo( GetTracebackHeader(this, exception, tsh.Body), new SourceSpan(GlobalParent.IndexToLocation(tsh.StartIndex), GlobalParent.IndexToLocation(tsh.HeaderIndex)) ); } } MSAst.Expression body = null; if (tests.Count > 0) { // rethrow the exception if we have no catch-all block if (catchAll == null) { catchAll = Ast.Block( Parent.GetSaveLineNumberExpression(exception, true), Ast.Throw( Ast.Call( typeof(ExceptionHelpers).GetMethod("UpdateForRethrow"), exception ) ) ); } body = AstUtils.If( tests.ToArray(), catchAll ); } else { Debug.Assert(catchAll != null); body = catchAll; } IList <MSAst.ParameterExpression> args; if (converted != null) { args = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression> { converted, extracted }; } else { args = new ReadOnlyCollectionBuilder <MSAst.ParameterExpression> { extracted }; } // Codegen becomes: // extracted = PythonOps.SetCurrentException(exception) // < dynamic exception analysis > return(Ast.Block( args, Ast.Assign( extracted, Ast.Call( AstMethods.SetCurrentException, Parent.LocalContext, exception ) ), body, Ast.Assign(extracted, Ast.Constant(null)), AstUtils.Empty() )); }
internal MSAst.Expression ReduceWorker() { if (_body is ReturnStatement retStmt && (_languageFeatures == ModuleOptions.None || _languageFeatures == (ModuleOptions.ExecOrEvalCode | ModuleOptions.Interpret) || _languageFeatures == (ModuleOptions.ExecOrEvalCode | ModuleOptions.Interpret | ModuleOptions.LightThrow))) { // for simple eval's we can construct a simple tree which just // leaves the value on the stack. Return's can't exist in modules // so this is always safe. Debug.Assert(!IsModule); var ret = (ReturnStatement)_body; Ast simpleBody; if ((_languageFeatures & ModuleOptions.LightThrow) != 0) { simpleBody = LightExceptions.Rewrite(retStmt.Expression.Reduce()); } else { simpleBody = retStmt.Expression.Reduce(); } var start = IndexToLocation(ret.Expression.StartIndex); var end = IndexToLocation(ret.Expression.EndIndex); return(Ast.Block( Ast.DebugInfo( _document, start.Line, start.Column, end.Line, end.Column ), AstUtils.Convert(simpleBody, typeof(object)) )); } ReadOnlyCollectionBuilder <MSAst.Expression> block = new ReadOnlyCollectionBuilder <MSAst.Expression>(); AddInitialization(block); string doc = GetDocumentation(_body); if (doc != null || IsModule) { block.Add(AssignValue(GetVariableExpression(DocVariable), Ast.Constant(doc))); } if (!(_body is SuiteStatement) && _body.CanThrow) { // we only initialize line numbers in suite statements but if we don't generate a SuiteStatement // at the top level we can miss some line number updates. block.Add(UpdateLineNumber(_body.Start.Line)); } block.Add(_body); MSAst.Expression body = Ast.Block(block.ToReadOnlyCollection()); body = WrapScopeStatements(body, Body.CanThrow); // new ComboActionRewriter().VisitNode(Transform(ag)) body = AddModulePublishing(body); body = AddProfiling(body); if ((((PythonCompilerOptions)_compilerContext.Options).Module & ModuleOptions.LightThrow) != 0) { body = LightExceptions.Rewrite(body); } body = Ast.Label(FunctionDefinition._returnLabel, AstUtils.Convert(body, typeof(object))); if (body.Type == typeof(void)) { body = Ast.Block(body, Ast.Constant(null)); } return(body); }
// see Ruby Language.doc/Runtime/Control Flow Implementation/While-Until internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#loop-result", typeof(object)); MSA.Expression redoVariable = gen.CurrentScope.DefineHiddenVariable("#skip-condition", typeof(bool)); MSA.ParameterExpression unwinder; bool isInnerLoop = gen.CurrentLoop != null; MSA.LabelTarget breakLabel = Ast.Label(); MSA.LabelTarget continueLabel = Ast.Label(); gen.EnterLoop(redoVariable, resultVariable, breakLabel, continueLabel); MSA.Expression transformedBody = gen.TransformStatements(_statements, ResultOperation.Ignore); MSA.Expression transformedCondition = _condition.TransformCondition(gen, true); gen.LeaveLoop(); MSA.Expression conditionPositiveStmt, conditionNegativeStmt; if (_isWhileLoop) { conditionPositiveStmt = AstUtils.Empty(); conditionNegativeStmt = Ast.Break(breakLabel); } else { conditionPositiveStmt = Ast.Break(breakLabel); conditionNegativeStmt = AstUtils.Empty(); } // make the loop first: MSA.Expression loop = new AstBlock { gen.ClearDebugInfo(), Ast.Assign(redoVariable, AstUtils.Constant(_isPostTest)), AstFactory.Infinite(breakLabel, continueLabel, AstUtils.Try( AstUtils.If(redoVariable, Ast.Assign(redoVariable, AstUtils.Constant(false)) ).ElseIf(transformedCondition, conditionPositiveStmt ).Else( conditionNegativeStmt ), transformedBody, AstUtils.Empty() ).Catch(unwinder = Ast.Parameter(typeof(BlockUnwinder), "#u"), // redo = u.IsRedo Ast.Assign(redoVariable, Ast.Field(unwinder, BlockUnwinder.IsRedoField)), AstUtils.Empty() ).Filter(unwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"), Ast.Equal(Ast.Field(unwinder, EvalUnwinder.ReasonField), AstFactory.BlockReturnReasonBreak), // result = unwinder.ReturnValue Ast.Assign(resultVariable, Ast.Field(unwinder, EvalUnwinder.ReturnValueField)), Ast.Break(breakLabel) ) ), gen.ClearDebugInfo(), AstUtils.Empty(), }; // wrap it to try finally that updates RFC state: if (!isInnerLoop) { loop = AstUtils.Try( Methods.EnterLoop.OpCall(gen.CurrentScopeVariable), loop ).Finally( Methods.LeaveLoop.OpCall(gen.CurrentScopeVariable) ); } return(Ast.Block(loop, resultVariable)); }