internal override MSAst.Expression TransformSet(AstGenerator ag, SourceSpan span, MSAst.Expression right, Operators op) { if (op == Operators.None) { return ag.AddDebugInfoAndVoid( Binders.Set( ag.BinderState, typeof(object), SymbolTable.IdToString(_name), ag.Transform(_target), right ), span ); } else { MSAst.ParameterExpression temp = ag.GetTemporary("inplace"); return ag.AddDebugInfo( Ast.Block( Ast.Assign(temp, ag.Transform(_target)), SetMemberOperator(ag, right, op, temp), Ast.Empty() ), Span.Start, span.End ); } }
internal override MSAst.Expression Transform(AstGenerator ag, Type type) { MSAst.Expression left = ag.Transform(_left); MSAst.Expression right = ag.Transform(_right); Type t = left.Type == right.Type ? left.Type : typeof(object); MSAst.ParameterExpression tmp = ag.GetTemporary("__all__", t); return Ast.Condition( Binders.Convert( ag.BinderState, typeof(bool), ConversionResultKind.ExplicitCast, AstUtils.Assign( tmp, AstUtils.Convert( left, t ) ) ), AstUtils.Convert( right, t ), tmp ); }
internal override MSAst.Expression Transform(AstGenerator ag, Type type) { MSAst.ParameterExpression list = ag.GetTemporary("list_comprehension_list", typeof(List)); // 1. Initialization code - create list and store it in the temp variable MSAst.Expression initialize = Ast.Assign( list, Ast.Call( AstGenerator.GetHelperMethod("MakeList", Type.EmptyTypes) // method ) ); // 2. Create body from _item: list.Append(_item) MSAst.Expression body = ag.AddDebugInfo( Ast.Call( AstGenerator.GetHelperMethod("ListAddForComprehension"), list, ag.TransformAsObject(_item) ), _item.Span ); // 3. Transform all iterators in reverse order, building the true body: int current = _iterators.Length; while (current-- > 0) { ListComprehensionIterator iterator = _iterators[current]; body = iterator.Transform(ag, body); } return Ast.Block( initialize, body, list // result ); }
internal override MSAst.Expression Transform(AstGenerator ag) { MSAst.Expression destination = ag.TransformAsObject(_dest); if (_expressions.Length == 0) { MSAst.Expression result; if (destination != null) { result = Ast.Call( AstGenerator.GetHelperMethod("PrintNewlineWithDest"), ag.LocalContext, destination ); } else { result = Ast.Call( AstGenerator.GetHelperMethod("PrintNewline"), ag.LocalContext ); } return ag.AddDebugInfo(result, Span); } else { // Create list for the individual statements List<MSAst.Expression> statements = new List<MSAst.Expression>(); // Store destination in a temp, if we have one if (destination != null) { MSAst.ParameterExpression temp = ag.GetTemporary("destination"); statements.Add( ag.MakeAssignment(temp, destination) ); destination = temp; } for (int i = 0; i < _expressions.Length; i++) { string method = (i < _expressions.Length - 1 || _trailingComma) ? "PrintComma" : "Print"; Expression current = _expressions[i]; MSAst.MethodCallExpression mce; if (destination != null) { mce = Ast.Call( AstGenerator.GetHelperMethod(method + "WithDest"), ag.LocalContext, destination, ag.TransformAsObject(current) ); } else { mce = Ast.Call( AstGenerator.GetHelperMethod(method), ag.LocalContext, ag.TransformAsObject(current) ); } statements.Add(mce); } statements.Add(AstUtils.Empty()); return ag.AddDebugInfo(Ast.Block(statements.ToArray()), Span); } }
internal override MSAst.Expression Transform(AstGenerator ag) { // Temporary variable for the IEnumerator object MSAst.ParameterExpression enumerator = ag.GetTemporary("foreach_enumerator", typeof(IEnumerator)); // Only the body is "in the loop" for the purposes of break/continue // The "else" clause is outside MSAst.LabelTarget breakLabel, continueLabel; MSAst.Expression body = ag.TransformLoopBody(_body, _left.Start, out breakLabel, out continueLabel); if (body == null) { // error recovery return null; } return TransformForStatement(ag, enumerator, _list, _left, body, _else, Span, _header, breakLabel, continueLabel); }
private MSAst.Expression AssignComplex(AstGenerator ag, MSAst.Expression right) { // Python assignment semantics: // - only evaluate RHS once. // - evaluates assignment from left to right // - does not evaluate getters. // // So // a=b[c]=d=f() // should be: // $temp = f() // a = $temp // b[c] = $temp // d = $temp List<MSAst.Expression> statements = new List<MSAst.Expression>(); // 1. Create temp variable for the right value MSAst.ParameterExpression right_temp = ag.GetTemporary("assignment"); // 2. right_temp = right statements.Add( ag.MakeAssignment(right_temp, right) ); // Do left to right assignment foreach (Expression e in _left) { if (e == null) { continue; } // 3. e = right_temp MSAst.Expression transformed = e.TransformSet(ag, Span, right_temp, Operators.None); if (transformed == null) { throw PythonOps.SyntaxError(String.Format("can't assign to {0}", e.NodeName), ag.Context.SourceUnit, e.Span, -1); } statements.Add(transformed); } // 4. Create and return the resulting suite statements.Add(Ast.Empty()); return ag.AddDebugInfo( Ast.Block(statements.ToArray()), Span ); }
private MSAst.Expression AssignComplex(AstGenerator ag, MSAst.Expression right) { // Python assignment semantics: // - only evaluate RHS once. // - evaluates assignment from left to right // - does not evaluate getters. // // So // a=b[c]=d=f() // should be: // $temp = f() // a = $temp // b[c] = $temp // d = $temp List<MSAst.Expression> statements = new List<MSAst.Expression>(); // 1. Create temp variable for the right value MSAst.ParameterExpression right_temp = ag.GetTemporary("assignment"); // 2. right_temp = right statements.Add( AstGenerator.MakeAssignment(right_temp, right) ); // Do left to right assignment foreach (Expression e in _left) { if (e == null) { continue; } // 3. e = right_temp MSAst.Expression transformed = e.TransformSet(ag, Span, right_temp, PythonOperationKind.None); statements.Add(transformed); } // 4. Create and return the resulting suite statements.Add(AstUtils.Empty()); return ag.AddDebugInfoAndVoid( Ast.Block(statements.ToArray()), Span ); }
internal override MSAst.Expression TransformSet(AstGenerator ag, SourceSpan span, MSAst.Expression right, PythonOperationKind op) { if (op == PythonOperationKind.None) { return ag.AddDebugInfoAndVoid( ag.Set( typeof(object), _name, ag.Transform(_target), right ), span ); } else { MSAst.ParameterExpression temp = ag.GetTemporary("inplace"); return ag.AddDebugInfo( Ast.Block( Ast.Assign(temp, ag.Transform(_target)), SetMemberOperator(ag, right, op, temp), AstUtils.Empty() ), Span.Start, span.End ); } }
internal override MSAst.Expression Transform(AstGenerator ag) { if (_names == _star) { // from a[.b] import * return ag.AddDebugInfo( Ast.Call( AstGenerator.GetHelperMethod("ImportStar"), AstUtils.CodeContext(), Ast.Constant(_root.MakeString()), Ast.Constant(GetLevel()) ), Span ); } else { // from a[.b] import x [as xx], [ y [ as yy] ] [ , ... ] List<MSAst.Expression> statements = new List<MSAst.Expression>(); MSAst.ParameterExpression module = ag.GetTemporary("module"); // Create initializer of the array of names being passed to ImportWithNames MSAst.ConstantExpression[] names = new MSAst.ConstantExpression[_names.Length]; for (int i = 0; i < names.Length; i++) { names[i] = Ast.Constant(SymbolTable.IdToString(_names[i])); } // module = PythonOps.ImportWithNames(<context>, _root, make_array(_names)) statements.Add( ag.AddDebugInfo( AstUtils.Assign( module, Ast.Call( AstGenerator.GetHelperMethod("ImportWithNames"), AstUtils.CodeContext(), Ast.Constant(_root.MakeString()), Ast.NewArrayInit(typeof(string), names), Ast.Constant(GetLevel()) ) ), _root.Span ) ); // now load all the names being imported and assign the variables for (int i = 0; i < names.Length; i++) { statements.Add( ag.AddDebugInfo( AstUtils.Assign( _variables[i].Variable, Ast.Call( AstGenerator.GetHelperMethod("ImportFrom"), AstUtils.CodeContext(), module, names[i] ) ), Span ) ); } statements.Add(Ast.Empty()); return ag.AddDebugInfo(Ast.Block(statements.ToArray()), Span); } }
private static MSAst.Expression MakeBinaryOperation(AstGenerator ag, PythonOperator op, MSAst.Expression left, MSAst.Expression right, Type type, SourceSpan span) { if (op == PythonOperator.NotIn) { return AstUtils.Convert( Ast.Not( Binders.Operation( ag.BinderState, typeof(bool), StandardOperators.Contains, left, right ) ), type ); } Operators action = PythonOperatorToAction(op); if (action != Operators.None) { // Create action expression if (op == PythonOperator.Divide && (ag.DivisionOptions == PythonDivisionOptions.Warn || ag.DivisionOptions == PythonDivisionOptions.WarnAll)) { MSAst.ParameterExpression tempLeft = ag.GetTemporary("left", left.Type); MSAst.ParameterExpression tempRight = ag.GetTemporary("right", right.Type); return Ast.Block( Ast.Call( AstGenerator.GetHelperMethod("WarnDivision"), AstUtils.CodeContext(), Ast.Constant(ag.DivisionOptions), AstUtils.Convert( Ast.Assign(tempLeft, left), typeof(object) ), AstUtils.Convert( Ast.Assign(tempRight, right), typeof(object) ) ), Binders.Operation( ag.BinderState, type, StandardOperators.FromOperator(action), tempLeft, tempRight ) ); } return Binders.Operation( ag.BinderState, type, StandardOperators.FromOperator(action), left, right ); } else { // Call helper method return Ast.Call( AstGenerator.GetHelperMethod(GetHelperName(op)), AstGenerator.ConvertIfNeeded(left, typeof(object)), AstGenerator.ConvertIfNeeded(right, typeof(object)) ); } }
internal override MSAst.Expression TransformSet(AstGenerator ag, 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 = ag.GetTemporary("unpacking"); // 2. Add the assignment "right_temp = right" into the suite/block MSAst.Expression assignStmt1 = AstGenerator.MakeAssignment(right_temp, right); // 3. Call GetEnumeratorValues on the right side (stored in temp) MSAst.Expression enumeratorValues = Ast.Call( AstGenerator.GetHelperMethod("GetEnumeratorValues"), // method // arguments ag.LocalContext, right_temp, AstUtils.Constant(_items.Length) ); // 4. Create temporary variable for the array MSAst.ParameterExpression array_temp = ag.GetTemporary("array", typeof(object[])); // 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 = ag.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( ag, 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 = ag.AddDebugInfo(Ast.Block(sets.ToReadOnlyCollection()), leftSpan); // 10. Free the temps ag.FreeTemp(array_temp); ag.FreeTemp(right_temp); // 11. Return the suite statement (block) return ag.AddDebugInfo(Ast.Block(assignStmt1, assignStmt2, itemSet, AstUtils.Empty()), totalSpan); }
internal override MSAst.Expression Transform(AstGenerator ag, MSAst.Expression body) { MSAst.ParameterExpression temp = ag.GetTemporary("list_comprehension_for", typeof(IEnumerator)); return ForStatement.TransformForStatement(ag, temp, _list, _lhs, body, null, Span, _lhs.End, null, null); }
internal override MSAst.Expression Transform(AstGenerator ag) { // allocated all variables here so they won't be shared w/ other // locals allocated during the body or except blocks. MSAst.ParameterExpression noNestedException = null; if (_finally != null) { noNestedException = ag.GetTemporary("$noException", typeof(bool)); } MSAst.ParameterExpression lineUpdated = null; MSAst.ParameterExpression runElse = null; if (_else != null || (_handlers != null && _handlers.Length > 0)) { lineUpdated = ag.GetTemporary("$lineUpdated", typeof(bool)); if (_else != null) { runElse = ag.GetTemporary("run_else", typeof(bool)); } } // don't allocate locals below here... MSAst.Expression body = ag.Transform(_body); MSAst.Expression @else = ag.Transform(_else); if (body == null) { return null; } MSAst.ParameterExpression exception; MSAst.Expression @catch = TransformHandlers(ag, out exception); MSAst.Expression result; // 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, Ast.Constant(true)), // save existing line updated, we could choose to do this only for nested exception handlers. ag.PushLineUpdated(false, lineUpdated), AstUtils.Try( ag.AddDebugInfo(Ast.Empty(), new SourceSpan(Span.Start, _header)), body ).Catch(exception, Ast.Assign(runElse, Ast.Constant(false)), @catch, // restore existing line updated after exception handler completes ag.PopLineUpdated(lineUpdated), Ast.Default(body.Type) ), AstUtils.IfThen(runElse, @else ), Ast.Empty() ); } else if (@catch != null) { // no "else" clause // try { // <try body> // } catch (Exception e) { // ... catch handling ... // } // result = AstUtils.Try( ag.AddDebugInfo(Ast.Empty(), new SourceSpan(Span.Start, _header)), // save existing line updated ag.PushLineUpdated(false, lineUpdated), body ).Catch(exception, @catch, // restore existing line updated after exception handler completes ag.PopLineUpdated(lineUpdated), Ast.Default(body.Type) ); } else { result = body; } try { return AddFinally(ag, result, noNestedException); } finally { // free all locals here after the children nodes have been generated if (lineUpdated != null) { ag.FreeTemp(lineUpdated); } if (runElse != null) { ag.FreeTemp(@runElse); } } }
internal MSAst.Expression TransformToFunctionExpression(AstGenerator ag) { string name; if (IsLambda) { name = "<lambda$" + Interlocked.Increment(ref _lambdaId) + ">"; } else { name = SymbolTable.IdToString(_name); } // Create AST generator to generate the body with AstGenerator bodyGen = new AstGenerator(ag, name, IsGenerator, false); bodyGen.DisableInterpreter = true; // Transform the parameters. // Populate the list of the parameter names and defaults. List<MSAst.Expression> defaults = new List<MSAst.Expression>(0); List<MSAst.Expression> names = new List<MSAst.Expression>(); TransformParameters(ag, bodyGen, defaults, names); List<MSAst.Expression> statements = new List<MSAst.Expression>(); // Create variables and references. Since references refer to // parameters, do this after parameters have been created. CreateVariables(bodyGen, statements); // Initialize parameters - unpack tuples. // Since tuples unpack into locals, this must be done after locals have been created. InitializeParameters(bodyGen, statements); // 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(bodyGen, 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 = bodyGen.GetTemporary("$ex", typeof(Exception)); } // Transform the body and add the resulting statements into the list TransformBody(bodyGen, statements); if (ag.DebugMode) { // add beginning and ending break points for the function. if (GetExpressionEnd(statements[statements.Count - 1]) != Body.End) { statements.Add(ag.AddDebugInfo(Ast.Empty(), new SourceSpan(Body.End, Body.End))); } } MSAst.Expression body = Ast.Block(new ReadOnlyCollection<MSAst.Expression>(statements.ToArray())); // 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( AstGenerator.GetHelperMethod("SaveCurrentException") ) ), body ).Finally( Ast.Call( AstGenerator.GetHelperMethod("RestoreCurrentException"), extracted ) ); body = s; } body = bodyGen.WrapScopeStatements(body); bodyGen.Block.Body = bodyGen.AddReturnTarget(body); FunctionAttributes flags = ComputeFlags(_parameters); bool needsWrapperMethod = flags != FunctionAttributes.None; if (_canSetSysExcInfo) { flags |= FunctionAttributes.CanSetSysExcInfo; } MSAst.Expression code; if (IsGenerator) { code = bodyGen.Block.MakeGenerator(bodyGen.GeneratorLabel, GetGeneratorDelegateType(_parameters, needsWrapperMethod)); flags |= FunctionAttributes.Generator; } else { code = bodyGen.Block.MakeLambda(GetDelegateType(_parameters, needsWrapperMethod)); } MSAst.Expression ret = Ast.Call( null, // instance typeof(PythonOps).GetMethod("MakeFunction"), // method new ReadOnlyCollection<MSAst.Expression>( new [] { AstUtils.CodeContext(), // 1. Emit CodeContext Ast.Constant(name), // 2. FunctionName code, // 3. delegate names.Count == 0 ? // 4. parameter names Ast.Constant(null, typeof(string[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(string), names), defaults.Count == 0 ? // 5. default values Ast.Constant(null, typeof(object[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(object), defaults), Ast.Constant(flags), // 6. flags Ast.Constant(ag.GetDocumentation(_body), typeof(string)), // 7. doc string or null Ast.Constant(this.Start.Line), // 8. line number Ast.Constant(_sourceUnit.Path, typeof(string)) // 9. filename } ) ); ret = ag.AddDecorators(ret, _decorators); return ret; }
private MSAst.Expression AddFinally(AstGenerator/*!*/ ag, MSAst.Expression/*!*/ body, MSAst.ParameterExpression noNestedException) { if (_finally != null) { Debug.Assert(noNestedException != null); MSAst.ParameterExpression nestedFrames = ag.GetTemporary("$nestedFrames", typeof(List<DynamicStackFrame>)); bool inFinally = ag.InFinally; ag.InFinally = true; MSAst.Expression @finally = ag.Transform(_finally); ag.InFinally = inFinally; if (@finally == null) { // error reported during compilation return null; } if (ag.TrackLines) { // 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 filter to know when we have an exception and when control leaves normally (via // either a return or the body completing successfully). AstUtils.Try( ag.AddDebugInfo(Ast.Empty(), new SourceSpan(Span.Start, _header)), Ast.Assign(noNestedException, Ast.Constant(true)), body ).Filter( typeof(Exception), // condition is never true, just note the exception and let it propagate Ast.Equal( Ast.Assign(noNestedException, Ast.Constant(false)), Ast.Constant(true) ), Ast.Default(body.Type) ) ).Finally( // if we had an exception save the line # that was last executing during the try AstUtils.If( Ast.Not(noNestedException), ag.GetLineNumberUpdateExpression(false) ), // clear the frames incase thae finally throws, and allow line number // updates to proceed ag.UpdateLineUpdated(false), Ast.Assign( nestedFrames, Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames")) ), // run the finally code @finally, // if the finally exits normally restore any previous exception info Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), ag.UpdateLineUpdated(true) ); ag.FreeTemp(nestedFrames); ag.FreeTemp(noNestedException); } else { body = AstUtils.Try(body).Finally( Ast.Assign( nestedFrames, Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames")) ), // run the finally code @finally, // if the finally exits normally restore any previous exception info Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ) ); } } return body; }
/// <summary> /// Transform multiple python except handlers for a try block into a single catch body. /// </summary> /// <param name="ag"></param> /// <param name="variable">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(AstGenerator ag, out MSAst.ParameterExpression variable) { if (_handlers == null || _handlers.Length == 0) { variable = null; return null; } MSAst.ParameterExpression exception = ag.GetTemporary("exception", typeof(Exception)); MSAst.ParameterExpression extracted = ag.GetTemporary("extracted", typeof(object)); // The variable where the runtime will store the exception. variable = exception; 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( AstGenerator.GetHelperMethod("CheckException"), extracted, ag.TransformAsObject(tsh.Test) ); if (tsh.Target != null) { // translating: // except Test, Target: // <body> // into: // if ((converted = CheckException(exception, Test)) != null) { // Target = converted; // traceback-header // <body> // } if (converted == null) { converted = ag.GetTemporary("converted"); } ist = AstUtils.IfCondition( Ast.NotEqual( Ast.Assign(converted, test), Ast.Constant(null) ), Ast.Block( tsh.Target.TransformSet(ag, SourceSpan.None, converted, Operators.None), GetTracebackHeader( new SourceSpan(tsh.Start, tsh.Header), ag, exception, ag.Transform(tsh.Body) ), Ast.Empty() ) ); } else { // translating: // except Test: // <body> // into: // if (CheckException(exception, Test) != null) { // traceback-header // <body> // } ist = AstUtils.IfCondition( Ast.NotEqual( test, Ast.Constant(null) ), GetTracebackHeader( new SourceSpan(tsh.Start, tsh.Header), ag, exception, ag.Transform(tsh.Body) ) ); } // 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 = GetTracebackHeader(new SourceSpan(tsh.Start, tsh.Header), ag, exception, ag.Transform(tsh.Body)); } } MSAst.Expression body = null; if (tests.Count > 0) { // rethrow the exception if we have no catch-all block if (catchAll == null) { catchAll = Ast.Throw(exception); } body = AstUtils.If( tests.ToArray(), catchAll ); } else { Debug.Assert(catchAll != null); body = catchAll; } if (converted != null) { ag.FreeTemp(converted); } ag.FreeTemp(exception); ag.FreeTemp(extracted); // Codegen becomes: // extracted = PythonOps.SetCurrentException(exception) // < dynamic exception analysis > return Ast.Block( Ast.Assign( extracted, Ast.Call( AstGenerator.GetHelperMethod("SetCurrentException"), AstUtils.CodeContext(), exception ) ), body, Ast.Empty() ); }
private MSAst.Expression AddFinally(AstGenerator/*!*/ ag, MSAst.Expression/*!*/ body, MSAst.ParameterExpression nestedException) { if (_finally != null) { bool isEmitting = ag._isEmittingFinally; ag._isEmittingFinally = true; int loopId = ++ag._loopOrFinallyId; ag.LoopOrFinallyIds.Add(loopId, true); try { Debug.Assert(nestedException != null); MSAst.ParameterExpression nestedFrames = ag.GetTemporary("$nestedFrames", typeof(List<DynamicStackFrame>)); bool inFinally = ag.InFinally; ag.InFinally = true; MSAst.Expression @finally = ag.Transform(_finally); ag.InFinally = inFinally; if (@finally == null) { // error reported during compilation return null; } // 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( ag.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Span.Start, _header)), Ast.Assign(nestedException, AstUtils.Constant(false)), body ).Fault( // fault Ast.Assign(nestedException, AstUtils.Constant(true)) ) ).FinallyWithJumps( // if we had an exception save the line # that was last executing during the try AstUtils.If( nestedException, ag.GetSaveLineNumberExpression(false) ), // clear the frames incase thae finally throws, and allow line number // updates to proceed ag.UpdateLineUpdated(false), Ast.Assign( nestedFrames, Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames")) ), // run the finally code @finally, // if the finally exits normally restore any previous exception info Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), // 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( nestedException, ag.UpdateLineUpdated(true) ) ); ag.FreeTemp(nestedFrames); ag.FreeTemp(nestedException); } finally { ag._isEmittingFinally = isEmitting; ag.LoopOrFinallyIds.Remove(loopId); } } return body; }
internal MSAst.Expression TransformToFunctionExpression(AstGenerator ag) { string name; if (IsLambda) { name = "<lambda$" + Interlocked.Increment(ref _lambdaId) + ">"; } else { name = _name; } if (ag.PyContext.PythonOptions.FullFrames) { // force a dictionary if we have enabled full frames for sys._getframe support NeedsLocalsDictionary = true; } // Create AST generator to generate the body with AstGenerator bodyGen = new AstGenerator(ag, name, IsGenerator, MakeProfilerName(name)); FunctionAttributes flags = ComputeFlags(_parameters); bool needsWrapperMethod = _parameters.Length > PythonCallTargets.MaxArgs; // Transform the parameters. // Populate the list of the parameter names and defaults. List<MSAst.Expression> defaults = new List<MSAst.Expression>(0); List<MSAst.Expression> names = new List<MSAst.Expression>(); List<MSAst.Expression> init = new List<MSAst.Expression>(); init.Add(Ast.ClearDebugInfo(ag.Document)); TransformParameters(ag, bodyGen, defaults, names, needsWrapperMethod, init); MSAst.Expression parentContext; parentContext = MSAst.Expression.Call(_GetParentContextFromFunction, _functionParam); bodyGen.AddHiddenVariable(ArrayGlobalAllocator._globalContext); init.Add(Ast.Assign(ArrayGlobalAllocator._globalContext, Ast.Call(_GetGlobalContext, parentContext))); init.AddRange(bodyGen.Globals.PrepareScope(bodyGen)); // Create variables and references. Since references refer to // parameters, do this after parameters have been created. CreateVariables(bodyGen, parentContext, init, NeedsLocalsDictionary, NeedsLocalsDictionary); // Initialize parameters - unpack tuples. // Since tuples unpack into locals, this must be done after locals have been created. InitializeParameters(bodyGen, init, needsWrapperMethod); List<MSAst.Expression> statements = new List<MSAst.Expression>(); // add beginning sequence point statements.Add(bodyGen.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(bodyGen, SourceSpan.None); statements.Add(s1); } if (NeedsLocalsDictionary || ContainsNestedFreeVariables) { bodyGen.CreateNestedContext(); } 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 = bodyGen.GetTemporary("$ex", typeof(Exception)); } // Transform the body and add the resulting statements into the list if (!TryTransformBody(bodyGen, statements)) { // there's an error in the body return null; } 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( AstGenerator.GetHelperMethod("SaveCurrentException") ) ), body ).Finally( Ast.Call( AstGenerator.GetHelperMethod("RestoreCurrentException"), extracted ) ); body = s; } if (_body.CanThrow && ag.PyContext.PythonOptions.Frames) { body = AstGenerator.AddFrame(bodyGen.LocalContext, Ast.Property(_functionParam, typeof(PythonFunction).GetProperty("__code__")), body); bodyGen.AddHiddenVariable(AstGenerator._functionStack); } body = bodyGen.AddProfiling(body); body = bodyGen.WrapScopeStatements(body); body = bodyGen.AddReturnTarget(body); if (_canSetSysExcInfo) { flags |= FunctionAttributes.CanSetSysExcInfo; } if (ContainsTryFinally) { flags |= FunctionAttributes.ContainsTryFinally; } if (IsGenerator) { flags |= FunctionAttributes.Generator; } MSAst.Expression bodyStmt = bodyGen.MakeBody( parentContext, init.ToArray(), body ); Delegate originalDelegate; MSAst.LambdaExpression code = Ast.Lambda( GetDelegateType(_parameters, needsWrapperMethod, out originalDelegate), AstGenerator.AddDefaultReturn(bodyStmt, typeof(object)), bodyGen.Name + "$" + _lambdaId++, bodyGen.Parameters ); // create the function code object which all function instances will share MSAst.Expression funcCode = ag.Globals.GetConstant( new FunctionCode( ag.PyContext, EmitDebugFunction(ag) ? null : originalDelegate, code, name, ag.GetDocumentation(_body), ArrayUtils.ConvertAll(_parameters, (val) => val.Name), flags, Span, _sourceUnit.Path, ag.EmitDebugSymbols, ag.ShouldInterpret, FreeVariables, GlobalVariables, CellVariables, GetVarNames(), Variables == null ? 0 : Variables.Count, bodyGen.LoopLocationsNoCreate, bodyGen.HandlerLocationsNoCreate ) ); bodyGen.FuncCodeExpr = funcCode; MSAst.Expression ret; if (EmitDebugFunction(ag)) { // we need to compile all of the debuggable code together at once otherwise mdbg gets confused. If we're // in tracing mode we'll still compile things one off though just to keep things simple. The code will still // be debuggable but naive debuggers like mdbg will have more issues. ret = Ast.Call( _MakeFunctionDebug, // method ag.LocalContext, // 1. Emit CodeContext funcCode, // 2. FunctionCode ((IPythonGlobalExpression)ag.Globals.GetVariable(ag, _nameVariable)).RawValue(),// 3. module name defaults.Count == 0 ? // 4. default values AstUtils.Constant(null, typeof(object[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(object), defaults), IsGenerator ? (MSAst.Expression)new PythonGeneratorExpression(code) : (MSAst.Expression)code ); } else { ret = Ast.Call( _MakeFunction, // method ag.LocalContext, // 1. Emit CodeContext funcCode, // 2. FunctionCode ((IPythonGlobalExpression)ag.Globals.GetVariable(ag, _nameVariable)).RawValue(),// 3. module name defaults.Count == 0 ? // 4. default values AstUtils.Constant(null, typeof(object[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(object), defaults) ); } ret = ag.AddDecorators(ret, _decorators); return ret; }
internal MSAst.Expression TransformToFunctionExpression(AstGenerator ag) { string name; if (IsLambda) { name = "<lambda$" + Interlocked.Increment(ref _lambdaId) + ">"; } else { name = SymbolTable.IdToString(_name); } // Create AST generator to generate the body with AstGenerator bodyGen = new AstGenerator(ag, name, IsGenerator, MakeProfilerName(name)); FunctionAttributes flags = ComputeFlags(_parameters); bool needsWrapperMethod = flags != FunctionAttributes.None || _parameters.Length > PythonCallTargets.MaxArgs; // Transform the parameters. // Populate the list of the parameter names and defaults. List<MSAst.Expression> defaults = new List<MSAst.Expression>(0); List<MSAst.Expression> names = new List<MSAst.Expression>(); List<MSAst.Expression> init = new List<MSAst.Expression>(); TransformParameters(ag, bodyGen, defaults, names, needsWrapperMethod, init); MSAst.Expression parentContext; parentContext = MSAst.Expression.Call(typeof(PythonOps).GetMethod("GetParentContextFromFunction"), _functionParam); bodyGen.AddHiddenVariable(ArrayGlobalAllocator._globalContext); init.Add(Ast.Assign(ArrayGlobalAllocator._globalContext, Ast.Call(typeof(PythonOps).GetMethod("GetGlobalContext"), parentContext))); init.AddRange(bodyGen.Globals.PrepareScope(bodyGen)); // Create variables and references. Since references refer to // parameters, do this after parameters have been created. CreateVariables(bodyGen, parentContext, init, NeedsLocalsDictionary, NeedsLocalsDictionary); // Initialize parameters - unpack tuples. // Since tuples unpack into locals, this must be done after locals have been created. InitializeParameters(bodyGen, init, needsWrapperMethod); List<MSAst.Expression> statements = new List<MSAst.Expression>(); // 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(bodyGen, SourceSpan.None); statements.Add(s1); } if (NeedsLocalsDictionary || ContainsNestedFreeVariables) { bodyGen.CreateNestedContext(); } 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 = bodyGen.GetTemporary("$ex", typeof(Exception)); } // Transform the body and add the resulting statements into the list if (!TryTransformBody(bodyGen, statements)) { // there's an error in the body return null; } if (ag.DebugMode) { // add beginning and ending break points for the function. if (GetExpressionEnd(statements[statements.Count - 1]) != Body.End) { statements.Add(ag.AddDebugInfo(AstUtils.Empty(), new SourceSpan(Body.End, Body.End))); } } MSAst.Expression body = Ast.Block(new ReadOnlyCollection<MSAst.Expression>(statements.ToArray())); // 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( AstGenerator.GetHelperMethod("SaveCurrentException") ) ), body ).Finally( Ast.Call( AstGenerator.GetHelperMethod("RestoreCurrentException"), extracted ) ); body = s; } body = bodyGen.AddProfiling(body); body = bodyGen.WrapScopeStatements(body); body = bodyGen.AddReturnTarget(body); if (_canSetSysExcInfo) { flags |= FunctionAttributes.CanSetSysExcInfo; } MSAst.Expression bodyStmt = bodyGen.MakeBody( parentContext, init.ToArray(), body, true ); if (ContainsTryFinally) { flags |= FunctionAttributes.ContainsTryFinally; } MSAst.LambdaExpression code; Delegate originalDelegate; if (IsGenerator) { code = Ast.Lambda( GetDelegateType(_parameters, needsWrapperMethod, out originalDelegate), new PythonGeneratorExpression(bodyGen.Name + "$" + _lambdaId++, bodyStmt, ag.ShouldInterpret, ag.EmitDebugSymbols, bodyGen.Parameters, bodyGen.GeneratorLabel), bodyGen.Name + "$" + _lambdaId++, bodyGen.Parameters ); flags |= FunctionAttributes.Generator; } else { code = Ast.Lambda( GetDelegateType(_parameters, needsWrapperMethod, out originalDelegate), AstGenerator.AddDefaultReturn(bodyStmt, typeof(object)), bodyGen.Name + "$" + _lambdaId++, bodyGen.Parameters ); } MSAst.Expression startingDelegate; if (ag.Globals is SavableGlobalAllocator || ag.EmitDebugSymbols) { startingDelegate = code; } else { startingDelegate = Ast.Constant(originalDelegate); } MSAst.Expression ret = Ast.Call( null, // instance typeof(PythonOps).GetMethod("MakeFunction"), // method new ReadOnlyCollection<MSAst.Expression>( new [] { ag.LocalContext, // 1. Emit CodeContext startingDelegate, // 2. initial callable delegate Ast.Constant( // 3. function info new FunctionInfo( name, ag.GetDocumentation(_body), ArrayUtils.ConvertAll<Parameter, string>(_parameters, (val) => SymbolTable.IdToString(val.Name)), flags, Start.Line, _sourceUnit.Path, code, ag.ShouldInterpret ) ), ((IPythonGlobalExpression)ag.Globals.GetVariable(ag, _nameVariable)).RawValue(), defaults.Count == 0 ? // 4. default values AstUtils.Constant(null, typeof(object[])) : (MSAst.Expression)Ast.NewArrayInit(typeof(object), defaults) } ) ); ret = ag.AddDecorators(ret, _decorators); return ret; }
private MSAst.Expression AssignOne(AstGenerator ag) { Debug.Assert(_left.Length == 1); SequenceExpression seLeft = _left[0] as SequenceExpression; SequenceExpression seRight = _right as SequenceExpression; if (seLeft != null && seRight != null && seLeft.Items.Length == seRight.Items.Length) { int cnt = seLeft.Items.Length; // a, b = 1, 2, or [a,b] = 1,2 - not something like a, b = range(2) // we can do a fast parallel assignment MSAst.ParameterExpression[] tmps = new MSAst.ParameterExpression[cnt]; MSAst.Expression[] body = new MSAst.Expression[cnt * 2 + 1]; // generate the body, the 1st n are the temporary assigns, the // last n are the assignments to the left hand side // 0: tmp0 = right[0] // ... // n-1: tmpn-1 = right[n-1] // n: right[0] = tmp0 // ... // n+n-1: right[n-1] = tmpn-1 // allocate the temps first before transforming so we don't pick up a bad temp... for (int i = 0; i < cnt; i++) { MSAst.Expression tmpVal = ag.Transform(seRight.Items[i]); tmps[i] = ag.GetTemporary("parallelAssign", tmpVal.Type); body[i] = Ast.Assign(tmps[i], tmpVal); } // then transform which can allocate more temps for (int i = 0; i < cnt; i++) { body[i + cnt] = seLeft.Items[i].TransformSet(ag, SourceSpan.None, tmps[i], PythonOperationKind.None); } // finally free the temps. foreach (MSAst.ParameterExpression variable in tmps) { ag.FreeTemp(variable); } // 4. Create and return the resulting suite body[cnt * 2] = AstUtils.Empty(); return ag.AddDebugInfo(Ast.Block(body), Span); } return _left[0].TransformSet(ag, Span, ag.Transform(_right), PythonOperationKind.None); }
/// <summary> /// WithStatement is translated to the DLR AST equivalent to /// the following Python code snippet (from with statement spec): /// /// mgr = (EXPR) /// exit = mgr.__exit__ # Not calling it yet /// value = mgr.__enter__() /// exc = True /// try: /// VAR = value # Only if "as VAR" is present /// BLOCK /// except: /// # The exceptional case is handled here /// exc = False /// if not exit(*sys.exc_info()): /// raise /// # The exception is swallowed if exit() returns true /// finally: /// # The normal and non-local-goto cases are handled here /// if exc: /// exit(None, None, None) /// /// </summary> internal override MSAst.Expression Transform(AstGenerator ag) { // Five statements in the result... MSAst.Expression[] statements = new MSAst.Expression[6]; //****************************************************************** // 1. mgr = (EXPR) //****************************************************************** MSAst.ParameterExpression manager = ag.GetTemporary("with_manager"); statements[0] = ag.MakeAssignment( manager, ag.Transform(_contextManager), new SourceSpan(Start, _header) ); //****************************************************************** // 2. exit = mgr.__exit__ # Not calling it yet //****************************************************************** MSAst.ParameterExpression exit = ag.GetTemporary("with_exit"); statements[1] = ag.MakeAssignment( exit, Binders.Get( ag.BinderState, typeof(object), "__exit__", manager ) ); //****************************************************************** // 3. value = mgr.__enter__() //****************************************************************** MSAst.ParameterExpression value = ag.GetTemporary("with_value"); statements[2] = ag.MakeAssignment( value, Binders.Invoke( ag.BinderState, typeof(object), new CallSignature(0), Binders.Get( ag.BinderState, typeof(object), "__enter__", manager ) ) ); //****************************************************************** // 4. exc = True //****************************************************************** MSAst.ParameterExpression exc = ag.GetTemporary("with_exc", typeof(bool)); statements[3] = ag.MakeAssignment( exc, Ast.Constant(true) ); //****************************************************************** // 5. The final try statement: // // try: // VAR = value # Only if "as VAR" is present // BLOCK // except: // # The exceptional case is handled here // exc = False // if not exit(*sys.exc_info()): // raise // # The exception is swallowed if exit() returns true // finally: // # The normal and non-local-goto cases are handled here // if exc: // exit(None, None, None) //****************************************************************** MSAst.ParameterExpression exception = ag.GetTemporary("exception", typeof(Exception)); statements[4] = // try: AstUtils.Try(// try statement body _var != null ? ag.AddDebugInfo( Ast.Block( // VAR = value _var.TransformSet(ag, SourceSpan.None, value, Operators.None), // BLOCK ag.Transform(_body), Ast.Empty() ), _body.Span ) : // BLOCK ag.Transform(_body) // except:, // try statement location ).Catch(exception, Ast.Block( // Python specific exception handling code Ast.Call( AstGenerator.GetHelperMethod("ClearDynamicStackFrames") ), // exc = False ag.MakeAssignment( exc, Ast.Constant(false) ), // if not exit(*sys.exc_info()): // raise AstUtils.IfThen( Binders.Convert( ag.BinderState, typeof(bool), ConversionResultKind.ExplicitCast, Binders.Operation( ag.BinderState, typeof(object), StandardOperators.Not, MakeExitCall(ag, exit, exception) ) ), Ast.Rethrow() ), Ast.Empty() ) // finally: ).Finally( // if exc: // exit(None, None, None) AstUtils.IfThen( exc, ag.AddDebugInfo( Ast.Dynamic( new PythonInvokeBinder( ag.BinderState, new CallSignature(3) // signature doesn't include function ), typeof(object), new MSAst.Expression[] { AstUtils.CodeContext(), exit, Ast.Constant(null), Ast.Constant(null), Ast.Constant(null) } ), _contextManager.Span ) ) ); statements[4] = ag.AddDebugInfo(statements[4], Span); statements[5] = Ast.Empty(); return ag.AddDebugInfo(Ast.Block(statements), _body.Span); }
internal override MSAst.Expression TransformSet(AstGenerator ag, SourceSpan span, MSAst.Expression right, Operators op) { if (op != Operators.None) { ag.AddError("augmented assign to sequence prohibited", Span); return null; } // 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; } MSAst.Expression[] statements = new MSAst.Expression[4]; // 1. Evaluate the expression and assign the value to the temp. MSAst.ParameterExpression right_temp = ag.GetTemporary("unpacking"); // 2. Add the assignment "right_temp = right" into the suite/block statements[0] = ag.MakeAssignment(right_temp, right); // 3. Call GetEnumeratorValues on the right side (stored in temp) MSAst.Expression enumeratorValues = Ast.Call( AstGenerator.GetHelperMethod("GetEnumeratorValues"), // method // arguments AstUtils.CodeContext(), right_temp, Ast.Constant(_items.Length) ); // 4. Create temporary variable for the array MSAst.ParameterExpression array_temp = ag.GetTemporary("array", typeof(object[])); // 5. Assign the value of the method call (mce) into the array temp // And add the assignment "array_temp = Ops.GetEnumeratorValues(...)" into the block statements[1] = ag.MakeAssignment( array_temp, enumeratorValues, rightSpan ); MSAst.Expression[] sets = new 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 Ast.Constant(i) // index ); // 7. target = array_temp[i], and add the transformed assignment into the list of sets MSAst.Expression set = target.TransformSet( ag, emitIndividualSets ? // span target.Span : SourceSpan.None, element, Operators.None ); if (set == null) { throw PythonOps.SyntaxError(string.Format("can't assign to {0}", target.NodeName), ag.Context.SourceUnit, target.Span, -1); } sets[i] = set; } // 9. add the sets as their own block so they can be marked as a single span, if necessary. sets[_items.Length] = Ast.Empty(); statements[2] = ag.AddDebugInfo(Ast.Block(sets), leftSpan); // 10. Free the temps ag.FreeTemp(array_temp); ag.FreeTemp(right_temp); // 11. Return the suite statement (block) statements[3] = Ast.Empty(); return ag.AddDebugInfo(Ast.Block(statements), totalSpan); }
private static MSAst.Expression MakeBinaryOperation(AstGenerator ag, PythonOperator op, MSAst.Expression left, MSAst.Expression right, Type type, SourceSpan span) { if (op == PythonOperator.NotIn) { return AstUtils.Convert( Ast.Not( ag.Operation( typeof(bool), PythonOperationKind.Contains, left, right ) ), type ); } else if (op == PythonOperator.In) { return AstUtils.Convert( ag.Operation( typeof(bool), PythonOperationKind.Contains, left, right ), type ); } PythonOperationKind action = PythonOperatorToAction(op); if (action != PythonOperationKind.None) { // Create action expression if (CanEmitWarning(ag, op)) { MSAst.ParameterExpression tempLeft = ag.GetTemporary("left", left.Type); MSAst.ParameterExpression tempRight = ag.GetTemporary("right", right.Type); return Ast.Block( Ast.Call( AstGenerator.GetHelperMethod("WarnDivision"), ag.LocalContext, AstUtils.Constant(ag.DivisionOptions), AstUtils.Convert( Ast.Assign(tempLeft, left), typeof(object) ), AstUtils.Convert( Ast.Assign(tempRight, right), typeof(object) ) ), ag.Operation( type, action, tempLeft, tempRight ) ); } return ag.Operation( type, action, left, right ); } else { // Call helper method return Ast.Call( AstGenerator.GetHelperMethod(GetHelperName(op)), AstGenerator.ConvertIfNeeded(left, typeof(object)), AstGenerator.ConvertIfNeeded(right, typeof(object)) ); } }
// This is a compound comparison operator like: a < b < c. // That's represented as binary operators, but it's not the same as (a<b) < c, so we do special transformations. // We need to: // - return true iff (a<b) && (b<c), but ensure that b is only evaluated once. // - ensure evaluation order is correct (a,b,c) // - don't evaluate c if a<b is false. private MSAst.Expression FinishCompare(MSAst.Expression left, AstGenerator ag) { Debug.Assert(_right is BinaryExpression); BinaryExpression bright = (BinaryExpression)_right; // Transform the left child of my right child (the next node in sequence) MSAst.Expression rleft = ag.Transform(bright.Left); // Store it in the temp MSAst.ParameterExpression temp = ag.GetTemporary("chained_comparison"); // Create binary operation: left <_op> (temp = rleft) MSAst.Expression comparison = MakeBinaryOperation( ag, _op, left, Ast.Assign(temp, AstUtils.Convert(rleft, temp.Type)), typeof(object), Span ); MSAst.Expression rright; // Transform rright, comparing to temp if (IsComparison(bright._right)) { rright = bright.FinishCompare(temp, ag); } else { MSAst.Expression transformedRight = ag.Transform(bright.Right); rright = MakeBinaryOperation( ag, bright.Operator, temp, transformedRight, typeof(object), bright.Span ); } ag.FreeTemp(temp); // return (left (op) (temp = rleft)) and (rright) return AstUtils.CoalesceTrue( ag.Block, comparison, rright, AstGenerator.GetHelperMethod("IsTrue") ); }
/// <summary> /// WithStatement is translated to the DLR AST equivalent to /// the following Python code snippet (from with statement spec): /// /// mgr = (EXPR) /// exit = mgr.__exit__ # Not calling it yet /// value = mgr.__enter__() /// exc = True /// try: /// VAR = value # Only if "as VAR" is present /// BLOCK /// except: /// # The exceptional case is handled here /// exc = False /// if not exit(*sys.exc_info()): /// raise /// # The exception is swallowed if exit() returns true /// finally: /// # The normal and non-local-goto cases are handled here /// if exc: /// exit(None, None, None) /// /// </summary> internal override MSAst.Expression Transform(AstGenerator ag) { MSAst.ParameterExpression lineUpdated = ag.GetTemporary("$lineUpdated_with", typeof(bool)); // Five statements in the result... ReadOnlyCollectionBuilder<MSAst.Expression> statements = new ReadOnlyCollectionBuilder<MSAst.Expression>(6); //****************************************************************** // 1. mgr = (EXPR) //****************************************************************** MSAst.ParameterExpression manager = ag.GetTemporary("with_manager"); statements.Add( ag.MakeAssignment( manager, ag.Transform(_contextManager), new SourceSpan(Start, _header) ) ); //****************************************************************** // 2. exit = mgr.__exit__ # Not calling it yet //****************************************************************** MSAst.ParameterExpression exit = ag.GetTemporary("with_exit"); statements.Add( AstGenerator.MakeAssignment( exit, ag.Get( typeof(object), "__exit__", manager ) ) ); //****************************************************************** // 3. value = mgr.__enter__() //****************************************************************** MSAst.ParameterExpression value = ag.GetTemporary("with_value"); statements.Add( ag.AddDebugInfoAndVoid( AstGenerator.MakeAssignment( value, ag.Invoke( typeof(object), new CallSignature(0), ag.Get( typeof(object), "__enter__", manager ) ) ), new SourceSpan(Start, _header) ) ); //****************************************************************** // 4. exc = True //****************************************************************** MSAst.ParameterExpression exc = ag.GetTemporary("with_exc", typeof(bool)); statements.Add( AstGenerator.MakeAssignment( exc, AstUtils.Constant(true) ) ); //****************************************************************** // 5. The final try statement: // // try: // VAR = value # Only if "as VAR" is present // BLOCK // except: // # The exceptional case is handled here // exc = False // if not exit(*sys.exc_info()): // raise // # The exception is swallowed if exit() returns true // finally: // # The normal and non-local-goto cases are handled here // if exc: // exit(None, None, None) //****************************************************************** MSAst.ParameterExpression exception = ag.GetTemporary("exception", typeof(Exception)); MSAst.ParameterExpression nestedFrames = ag.GetTemporary("$nestedFrames", typeof(List<DynamicStackFrame>)); statements.Add( // try: AstUtils.Try( AstUtils.Try(// try statement body ag.PushLineUpdated(false, lineUpdated), _var != null ? Ast.Block( // VAR = value _var.TransformSet(ag, SourceSpan.None, value, PythonOperationKind.None), // BLOCK ag.Transform(_body), AstUtils.Empty() ) : // BLOCK ag.Transform(_body) // except:, // try statement location ).Catch(exception, // Python specific exception handling code TryStatement.GetTracebackHeader( ag, exception, ag.AddDebugInfoAndVoid( Ast.Block( // exc = False AstGenerator.MakeAssignment( exc, AstUtils.Constant(false) ), Ast.Assign( nestedFrames, Ast.Call(AstGenerator.GetHelperMethod("GetAndClearDynamicStackFrames")) ), // if not exit(*sys.exc_info()): // raise AstUtils.IfThen( ag.Convert( typeof(bool), ConversionResultKind.ExplicitCast, ag.Operation( typeof(bool), PythonOperationKind.Not, MakeExitCall(ag, exit, exception) ) ), ag.UpdateLineUpdated(true), Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), Ast.Throw( Ast.Call( AstGenerator.GetHelperMethod("MakeRethrowExceptionWorker"), exception ) ) ) ), _body.Span ) ), Ast.Call( AstGenerator.GetHelperMethod("SetDynamicStackFrames"), nestedFrames ), ag.PopLineUpdated(lineUpdated), Ast.Empty() ) // finally: ).Finally( // if exc: // exit(None, None, None) AstUtils.IfThen( exc, ag.AddDebugInfoAndVoid( Ast.Block( Ast.Dynamic( ag.PyContext.Invoke( new CallSignature(3) // signature doesn't include function ), typeof(object), new MSAst.Expression[] { ag.LocalContext, exit, AstUtils.Constant(null), AstUtils.Constant(null), AstUtils.Constant(null) } ), Ast.Empty() ), _contextManager.Span ) ) ) ); statements.Add(AstUtils.Empty()); return Ast.Block(statements.ToReadOnlyCollection()); }