private BoundExpression RewriteLocal(BoundLocal node) { var local = node.LocalSymbol; var placeholder = local as PlaceholderLocalSymbol; if ((object)placeholder != null) { return placeholder.RewriteLocal(_compilation, _container, node.Syntax, _diagnostics); } if (_declaredLocals.Contains(local)) { return ObjectIdLocalSymbol.RewriteLocal(_compilation, _container, node.Syntax, local); } return node; }
public override BoundNode VisitLocal(BoundLocal node) { var local = node.LocalSymbol; if (!local.IsCompilerGenerated) { var variable = this.GetVariable(local.Name); if (variable != null) { var result = variable.ToBoundExpression(node.Syntax); Debug.Assert(node.Type == result.Type); return result; } } return node; }
private void EmitLocalAddress(BoundLocal localAccess) { var local = localAccess.LocalSymbol; if (IsStackLocal(local)) { if (local.RefKind != RefKind.None) { // do nothing, ref should be on the stack } else { // cannot get address of a stack value. // Something is wrong with optimizer throw ExceptionUtilities.UnexpectedValue(local.RefKind); } } else { builder.EmitLocalAddress(GetLocal(localAccess)); } }
/// <summary> /// Generate a class containing methods that represent /// the set of arguments and locals at the current scope. /// </summary> internal CommonPEModuleBuilder CompileGetLocals( string typeName, ArrayBuilder<LocalAndMethod> localBuilder, bool argumentsOnly, ImmutableArray<Alias> aliases, Microsoft.CodeAnalysis.CodeGen.CompilationTestData testData, DiagnosticBag diagnostics) { var objectType = this.Compilation.GetSpecialType(SpecialType.System_Object); var allTypeParameters = _currentFrame.GetAllTypeParameters(); var additionalTypes = ArrayBuilder<NamedTypeSymbol>.GetInstance(); EENamedTypeSymbol typeVariablesType = null; if (!argumentsOnly && (allTypeParameters.Length > 0)) { // Generate a generic type with matching type parameters. // A null instance of the type will be used to represent the // "Type variables" local. typeVariablesType = new EENamedTypeSymbol( this.Compilation.SourceModule.GlobalNamespace, objectType, _syntax, _currentFrame, ExpressionCompilerConstants.TypeVariablesClassName, (m, t) => ImmutableArray.Create<MethodSymbol>(new EEConstructorSymbol(t)), allTypeParameters, (t1, t2) => allTypeParameters.SelectAsArray((tp, i, t) => (TypeParameterSymbol)new SimpleTypeParameterSymbol(t, i, tp.Name), t2)); additionalTypes.Add(typeVariablesType); } var synthesizedType = new EENamedTypeSymbol( Compilation.SourceModule.GlobalNamespace, objectType, _syntax, _currentFrame, typeName, (m, container) => { var methodBuilder = ArrayBuilder<MethodSymbol>.GetInstance(); if (!argumentsOnly) { // Pseudo-variables: $exception, $ReturnValue, etc. if (aliases.Length > 0) { var sourceAssembly = Compilation.SourceAssembly; var typeNameDecoder = new EETypeNameDecoder(Compilation, (PEModuleSymbol)_currentFrame.ContainingModule); foreach (var alias in aliases) { if (alias.IsReturnValueWithoutIndex()) { Debug.Assert(aliases.Count(a => a.Kind == DkmClrAliasKind.ReturnValue) > 1); continue; } var local = PlaceholderLocalSymbol.Create( typeNameDecoder, _currentFrame, sourceAssembly, alias); var methodName = GetNextMethodName(methodBuilder); var syntax = SyntaxFactory.IdentifierName(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken)); var aliasMethod = this.CreateMethod(container, methodName, syntax, (method, diags) => { var expression = new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type); return new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; }); var flags = local.IsWritable ? DkmClrCompilationResultFlags.None : DkmClrCompilationResultFlags.ReadOnlyResult; localBuilder.Add(MakeLocalAndMethod(local, aliasMethod, flags)); methodBuilder.Add(aliasMethod); } } // "this" for non-static methods that are not display class methods or // display class methods where the display class contains "<>4__this". if (!m.IsStatic && (!IsDisplayClassType(m.ContainingType) || _displayClassVariables.ContainsKey(GeneratedNames.ThisProxyFieldName()))) { var methodName = GetNextMethodName(methodBuilder); var method = this.GetThisMethod(container, methodName); localBuilder.Add(new CSharpLocalAndMethod("this", "this", method, DkmClrCompilationResultFlags.None)); // Note: writable in dev11. methodBuilder.Add(method); } } // Hoisted method parameters (represented as locals in the EE). if (!_hoistedParameterNames.IsEmpty) { int localIndex = 0; foreach (var local in _localsForBinding) { // Since we are showing hoisted method parameters first, the parameters may appear out of order // in the Locals window if only some of the parameters are hoisted. This is consistent with the // behavior of the old EE. if (_hoistedParameterNames.Contains(local.Name)) { AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localIndex, GetLocalResultFlags(local)); } localIndex++; } } // Method parameters (except those that have been hoisted). int parameterIndex = m.IsStatic ? 0 : 1; foreach (var parameter in m.Parameters) { var parameterName = parameter.Name; if (!_hoistedParameterNames.Contains(parameterName) && GeneratedNames.GetKind(parameterName) == GeneratedNameKind.None) { AppendParameterAndMethod(localBuilder, methodBuilder, parameter, container, parameterIndex); } parameterIndex++; } if (!argumentsOnly) { // Locals. int localIndex = 0; foreach (var local in _localsForBinding) { if (!_hoistedParameterNames.Contains(local.Name)) { AppendLocalAndMethod(localBuilder, methodBuilder, local, container, localIndex, GetLocalResultFlags(local)); } localIndex++; } // "Type variables". if ((object)typeVariablesType != null) { var methodName = GetNextMethodName(methodBuilder); var returnType = typeVariablesType.Construct(allTypeParameters.Cast<TypeParameterSymbol, TypeSymbol>()); var method = this.GetTypeVariablesMethod(container, methodName, returnType); localBuilder.Add(new CSharpLocalAndMethod( ExpressionCompilerConstants.TypeVariablesLocalName, ExpressionCompilerConstants.TypeVariablesLocalName, method, DkmClrCompilationResultFlags.ReadOnlyResult)); methodBuilder.Add(method); } } return methodBuilder.ToImmutableAndFree(); }); additionalTypes.Add(synthesizedType); var module = CreateModuleBuilder( this.Compilation, synthesizedType.Methods, additionalTypes: additionalTypes.ToImmutableAndFree(), synthesizedType: synthesizedType, testData: testData, diagnostics: diagnostics); Debug.Assert(module != null); this.Compilation.Compile( module, win32Resources: null, xmlDocStream: null, emittingPdb: false, diagnostics: diagnostics, filterOpt: null, cancellationToken: CancellationToken.None); return diagnostics.HasAnyErrors() ? null : module; }
// private static T <Factory>(object[] submissionArray) // { // var submission = new Submission#N(submissionArray); // return submission.<Initialize>(); // } internal override BoundBlock CreateBody() { var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; // var submission = new Submission#N(submissionArray); var submissionAssignment = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create<BoundExpression>(submissionArrayParameter), default(ImmutableArray<string>), default(ImmutableArray<RefKind>), false, default(ImmutableArray<int>), null, null, _containingType) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // return submission.<Initialize>(); var initializeResult = CreateParameterlessCall( syntax, submissionLocal, initializer); Debug.Assert(initializeResult.Type == _returnType); var returnStatement = new BoundReturnStatement( syntax, RefKind.None, initializeResult) { WasCompilerGenerated = true }; return new BoundBlock(syntax, ImmutableArray.Create<LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray<LocalFunctionSymbol>.Empty, ImmutableArray.Create<BoundStatement>(submissionAssignment, returnStatement)) { WasCompilerGenerated = true }; }
public override BoundNode VisitLocal(BoundLocal node) { CapturedSymbolReplacement proxy; if (proxies.TryGetValue(node.LocalSymbol, out proxy)) { return proxy.Replacement(node.Syntax, frameType => FramePointer(node.Syntax, frameType)); } Debug.Assert(!VariablesCaptured.Contains(node.LocalSymbol)); LocalSymbol replacementLocal; if (this.localMap.TryGetValue(node.LocalSymbol, out replacementLocal)) { return new BoundLocal(node.Syntax, replacementLocal, node.ConstantValueOpt, replacementLocal.Type, node.HasErrors); } return base.VisitLocal(node); }
/// <summary> /// The rewrites are as follows: /// /// x++ /// temp = x /// x = temp + 1 /// return temp /// x-- /// temp = x /// x = temp - 1 /// return temp /// ++x /// temp = x + 1 /// x = temp /// return temp /// --x /// temp = x - 1 /// x = temp /// return temp /// /// In each case, the literal 1 is of the type required by the builtin addition/subtraction operator that /// will be used. The temp is of the same type as x, but the sum/difference may be wider, in which case a /// conversion is required. /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <param name="isPrefix">True for prefix, false for postfix.</param> /// <param name="isIncrement">True for increment, false for decrement.</param> /// <returns>A bound sequence that uses a temp to acheive the correct side effects and return value.</returns> private BoundNode LowerOperator(BoundUnaryOperator node, bool isPrefix, bool isIncrement) { BoundExpression operand = node.Operand; TypeSymbol operandType = operand.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); ConstantValue constantOne; BinaryOperatorKind binaryOperatorKind; MakeConstantAndOperatorKind(node.OperatorKind.OperandTypes(), node, out constantOne, out binaryOperatorKind); binaryOperatorKind |= isIncrement ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); TypeSymbol constantType = compilation.GetSpecialType(constantOne.SpecialType); BoundExpression boundOne = new BoundLiteral( syntax: null, syntaxTree: null, constantValueOpt: constantOne, type: constantType); LocalSymbol tempSymbol = new TempLocalSymbol(operandType, RefKind.None, containingSymbol); BoundExpression boundTemp = new BoundLocal( syntax: null, syntaxTree: null, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // NOTE: the LHS may have a narrower type than the operator expects, but that // doesn't seem to cause any problems. If a problem does arise, just add an // explicit BoundConversion. BoundExpression newValue = new BoundBinaryOperator( syntax: null, syntaxTree: null, operatorKind: binaryOperatorKind, left: isPrefix ? operand : boundTemp, right: boundOne, constantValueOpt: null, type: constantType); if (constantType != operandType) { newValue = new BoundConversion( syntax: null, syntaxTree: null, operand: newValue, conversionKind: operandType.IsEnumType() ? ConversionKind.ImplicitEnumeration : ConversionKind.ImplicitNumeric, symbolOpt: null, @checked: false, explicitCastInCode: false, constantValueOpt: null, type: operandType); } ReadOnlyArray<BoundExpression> assignments = ReadOnlyArray<BoundExpression>.CreateFrom( new BoundAssignmentOperator( syntax: null, syntaxTree: null, left: boundTemp, right: isPrefix ? newValue : operand, type: operandType), new BoundAssignmentOperator( syntax: null, syntaxTree: null, left: operand, right: isPrefix ? boundTemp : newValue, type: operandType)); return new BoundSequence( syntax: node.Syntax, syntaxTree: node.SyntaxTree, locals: ReadOnlyArray<LocalSymbol>.CreateFrom(tempSymbol), sideEffects: assignments, value: boundTemp, type: operandType); }
public override BoundNode VisitLocal(BoundLocal node) { if (node.LocalSymbol == _local) { _found = true; } return null; }
internal override BoundBlock CreateBody(BindingDiagnosticBag diagnostics) { var syntax = DummySyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, TypeWithAnnotations.Create(_containingType), SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; // var submission = new Submission#N(submissionArray); var submissionAssignment = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create <BoundExpression>(submissionArrayParameter), argumentNamesOpt: default(ImmutableArray <string>), argumentRefKindsOpt: default(ImmutableArray <RefKind>), expanded: false, argsToParamsOpt: default(ImmutableArray <int>), defaultArguments: default(BitVector), constantValueOpt: null, initializerExpressionOpt: null, type: _containingType) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // return submission.<Initialize>(); var initializeResult = CreateParameterlessCall( syntax, submissionLocal, initializer); Debug.Assert(TypeSymbol.Equals(initializeResult.Type, _returnType.Type, TypeCompareKind.ConsiderEverything2)); var returnStatement = new BoundReturnStatement( syntax, RefKind.None, initializeResult, @checked: false) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, ImmutableArray.Create <LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray.Create <BoundStatement>(submissionAssignment, returnStatement)) { WasCompilerGenerated = true }); }
internal override void GenerateMethodBody(TypeCompilationState compilationState, DiagnosticBag diagnostics) { AnonymousTypeManager manager = ((AnonymousTypeTemplateSymbol)this.ContainingType).Manager; SyntheticBoundNodeFactory F = this.CreateBoundNodeFactory(compilationState, diagnostics); // Method body: // // { // $anonymous$ local = value as $anonymous$; // return local != null // && System.Collections.Generic.EqualityComparer<T_1>.Default.Equals(this.backingFld_1, local.backingFld_1) // ... // && System.Collections.Generic.EqualityComparer<T_N>.Default.Equals(this.backingFld_N, local.backingFld_N); // } // Type and type expression AnonymousTypeTemplateSymbol anonymousType = (AnonymousTypeTemplateSymbol)this.ContainingType; // local BoundAssignmentOperator assignmentToTemp; BoundLocal boundLocal = F.StoreToTemp(F.As(F.Parameter(_parameters[0]), anonymousType), out assignmentToTemp); // Generate: statement <= 'local = value as $anonymous$' BoundStatement assignment = F.ExpressionStatement(assignmentToTemp); // Generate expression for return statement // retExpression <= 'local != null' BoundExpression retExpression = F.Binary(BinaryOperatorKind.ObjectNotEqual, manager.System_Boolean, F.Convert(manager.System_Object, boundLocal), F.Null(manager.System_Object)); // prepare symbols MethodSymbol equalityComparer_Equals = manager.System_Collections_Generic_EqualityComparer_T__Equals; MethodSymbol equalityComparer_get_Default = manager.System_Collections_Generic_EqualityComparer_T__get_Default; NamedTypeSymbol equalityComparerType = equalityComparer_Equals.ContainingType; // Compare fields for (int index = 0; index < anonymousType.Properties.Length; index++) { // Prepare constructed symbols TypeParameterSymbol typeParameter = anonymousType.TypeParameters[index]; FieldSymbol fieldSymbol = anonymousType.Properties[index].BackingField; NamedTypeSymbol constructedEqualityComparer = equalityComparerType.Construct(typeParameter); // Generate 'retExpression' = 'retExpression && System.Collections.Generic.EqualityComparer<T_index>. // Default.Equals(this.backingFld_index, local.backingFld_index)' retExpression = F.LogicalAnd(retExpression, F.Call(F.StaticCall(constructedEqualityComparer, equalityComparer_get_Default.AsMember(constructedEqualityComparer)), equalityComparer_Equals.AsMember(constructedEqualityComparer), F.Field(F.This(), fieldSymbol), F.Field(boundLocal, fieldSymbol))); } // Final return statement BoundStatement retStatement = F.Return(retExpression); // Create a bound block F.CloseMethod(F.Block(ImmutableArray.Create <LocalSymbol>(boundLocal.LocalSymbol), assignment, retStatement)); }
internal IVariableDeclaratorOperation CreateVariableDeclarator(BoundLocal boundLocal) { return(boundLocal == null ? null : new VariableDeclaratorOperation(boundLocal.LocalSymbol.GetPublicSymbol(), initializer: null, ignoredArguments: ImmutableArray <IOperation> .Empty, semanticModel: _semanticModel, syntax: boundLocal.Syntax, type: null, constantValue: null, isImplicit: false)); }
public override BoundNode VisitLocal(BoundLocal node) { AddIfCaptured(node.LocalSymbol, node.Syntax); return(base.VisitLocal(node)); }
static BoundStatement makeAppendString(SyntheticBoundNodeFactory F, BoundLocal builder, string value) { return(F.ExpressionStatement(F.Call(receiver: builder, F.WellKnownMethod(WellKnownMember.System_Text_StringBuilder__AppendString), F.StringLiteral(value)))); }
// Generates: // // private static T {Factory}(InteractiveSession session) // { // T submissionResult; // new {ThisScriptClass}(session, out submissionResult); // return submissionResult; // } private BoundBlock CreateSubmissionFactoryBody() { Debug.Assert(_containingType.TypeKind == TypeKind.Submission); SyntaxTree syntaxTree = CSharpSyntaxTree.Dummy; CSharpSyntaxNode syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); var interactiveSessionParam = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var ctor = _containingType.InstanceConstructors.Single(); Debug.Assert(ctor is SynthesizedInstanceConstructor); Debug.Assert(ctor.ParameterCount == 2); var submissionResultType = ctor.Parameters[1].Type; var resultLocal = new SynthesizedLocal(ctor, submissionResultType, SynthesizedLocalKind.LoweringTemp); var localReference = new BoundLocal(syntax, resultLocal, null, submissionResultType) { WasCompilerGenerated = true }; BoundExpression submissionResult = localReference; if (submissionResultType.IsStructType() && _returnType.SpecialType == SpecialType.System_Object) { submissionResult = new BoundConversion(syntax, submissionResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType) { WasCompilerGenerated = true }; } return(new BoundBlock(syntax, // T submissionResult; ImmutableArray.Create <LocalSymbol>(resultLocal), ImmutableArray.Create <BoundStatement>( // new Submission(interactiveSession, out submissionResult); new BoundExpressionStatement(syntax, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create <BoundExpression>(interactiveSessionParam, localReference), ImmutableArray <string> .Empty, ImmutableArray.Create <RefKind>(RefKind.None, RefKind.Ref), false, default(ImmutableArray <int>), null, null, _containingType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // return submissionResult; new BoundReturnStatement(syntax, submissionResult) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }); }
public sealed override BoundNode VisitLocal(BoundLocal node) { BoundNode replacement; if (TryReplaceWithProxy(node.LocalSymbol, node.Syntax, out replacement)) { return replacement; } // if a local needs a proxy it should have been allocated by its declaration node. Debug.Assert(!NeedsProxy(node.LocalSymbol)); return VisitUnhoistedLocal(node); }
/// <summary> /// Gets already declared and initialized local. /// </summary> private LocalDefinition GetLocal(BoundLocal localExpression) { var symbol = localExpression.LocalSymbol; return GetLocal(symbol); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { BoundExpression originalLeft = node.Left; if (originalLeft.Kind != BoundKind.Local) { return(base.VisitAssignmentOperator(node)); } var leftLocal = (BoundLocal)originalLeft; BoundExpression originalRight = node.Right; if (leftLocal.LocalSymbol.RefKind != RefKind.None && node.IsRef && NeedsProxy(leftLocal.LocalSymbol)) { Debug.Assert(!proxies.ContainsKey(leftLocal.LocalSymbol)); Debug.Assert(originalRight.Kind != BoundKind.ConvertedStackAllocExpression); //spilling ref local variables throw ExceptionUtilities.Unreachable; } if (NeedsProxy(leftLocal.LocalSymbol) && !proxies.ContainsKey(leftLocal.LocalSymbol)) { Debug.Assert(leftLocal.LocalSymbol.DeclarationKind == LocalDeclarationKind.None); // spilling temp variables throw ExceptionUtilities.Unreachable; } BoundExpression rewrittenLeft = (BoundExpression)this.Visit(leftLocal); BoundExpression rewrittenRight = (BoundExpression)this.Visit(originalRight); TypeSymbol rewrittenType = VisitType(node.Type); // Check if we're assigning the result of stackalloc to a hoisted local. // If we are, we need to store the result in a temp local and then assign // the value of the local to the field corresponding to the hoisted local. // If the receiver of the field is on the stack when the stackalloc happens, // popping it will free the memory (?) or otherwise cause verification issues. // DevDiv Bugs 59454 if (rewrittenLeft.Kind != BoundKind.Local && originalRight.Kind == BoundKind.ConvertedStackAllocExpression) { // From ILGENREC::genAssign: // DevDiv Bugs 59454: Handle hoisted local initialized with a stackalloc // NOTE: Need to check for cast of stackalloc on RHS. // If LHS isLocal, then genAddr is a noop so regular case works fine. SyntheticBoundNodeFactory factory = new SyntheticBoundNodeFactory(this.CurrentMethod, rewrittenLeft.Syntax, this.CompilationState, this.Diagnostics); BoundAssignmentOperator tempAssignment; BoundLocal tempLocal = factory.StoreToTemp(rewrittenRight, out tempAssignment); Debug.Assert(!node.IsRef); BoundAssignmentOperator rewrittenAssignment = node.Update(rewrittenLeft, tempLocal, node.IsRef, rewrittenType); return(new BoundSequence( node.Syntax, ImmutableArray.Create <LocalSymbol>(tempLocal.LocalSymbol), ImmutableArray.Create <BoundExpression>(tempAssignment), rewrittenAssignment, rewrittenType)); } return(node.Update(rewrittenLeft, rewrittenRight, node.IsRef, rewrittenType)); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { var sequence = node.Left as BoundSequence; if (sequence != null) { // assigning to a sequence is uncommon, but could happen in a // case if LHS was a declaration expression. // // Just rewrite {se1, se2, se3, val} = something // into ==> {se1, se2, se3, val = something} BoundExpression rewritten = sequence.Update(sequence.Locals, sequence.SideEffects, node.Update(sequence.Value, node.Right, node.RefKind, node.Type), sequence.Type); rewritten = (BoundExpression)Visit(rewritten); // do not count the assignment twice _counter--; return rewritten; } var isIndirectAssignment = IsIndirectAssignment(node); var left = VisitExpression(node.Left, isIndirectAssignment ? ExprContext.Address : ExprContext.AssignmentTarget); // must delay recording a write until after RHS is evaluated var assignmentLocal = _assignmentLocal; _assignmentLocal = null; Debug.Assert(_context != ExprContext.AssignmentTarget, "assignment expression cannot be a target of another assignment"); ExprContext rhsContext; if (node.RefKind != RefKind.None || _context == ExprContext.Address) { // we need the address of rhs one way or another so we cannot have it on the stack. rhsContext = ExprContext.Address; } else { Debug.Assert(_context == ExprContext.Value || _context == ExprContext.Box || _context == ExprContext.Sideeffects, "assignment expression cannot be a target of another assignment"); // we only need a value of rhs, so if otherwise possible it can be a stack value. rhsContext = ExprContext.Value; } // if right is a struct ctor, it may be optimized into in-place call // Such call will push the receiver ref before the arguments // so we need to ensure that arguments cannot use stack temps BoundExpression right = node.Right; object rhsCookie = null; if (right.Kind == BoundKind.ObjectCreationExpression && right.Type.IsVerifierValue() && ((BoundObjectCreationExpression)right).Constructor.ParameterCount != 0) { rhsCookie = this.GetStackStateCookie(); } right = VisitExpression(node.Right, rhsContext); // if assigning to a local, now it is the time to record the Write if (assignmentLocal != null) { // This assert will fire if code relies on implicit CLR coercions // - i.e assigns int value to a short local. // in that case we should force lhs to be a real local. Debug.Assert( node.Left.Type.Equals(node.Right.Type, ignoreCustomModifiersAndArraySizesAndLowerBounds: true, ignoreDynamic: true), @"type of the assignment value is not the same as the type of assignment target. This is not expected by the optimizer and is typically a result of a bug somewhere else."); Debug.Assert(!isIndirectAssignment, "indirect assignment is a read, not a write"); LocalSymbol localSymbol = assignmentLocal.LocalSymbol; // Special Case: If the RHS is a pointer conversion, then the assignment functions as // a conversion (because the RHS will actually be typed as a native u/int in IL), so // we should not optimize away the local (i.e. schedule it on the stack). if (CanScheduleToStack(localSymbol) && assignmentLocal.Type.IsPointerType() && right.Kind == BoundKind.Conversion && ((BoundConversion)right).ConversionKind.IsPointerConversion()) { ShouldNotSchedule(localSymbol); } RecordVarWrite(localSymbol); assignmentLocal = null; } if (rhsCookie != null) { // we currently have the rhs on stack, adjust for that. PopEvalStack(); this.EnsureStackState(rhsCookie); } return node.Update(left, right, node.RefKind, node.Type); }
// private static void <Main>() // { // var script = new Script(); // script.<Initialize>().GetAwaiter().GetResult(); // } internal override BoundBlock CreateBody() { // CreateBody should only be called if no errors. Debug.Assert((object)_getAwaiterMethod != null); Debug.Assert((object)_getResultMethod != null); var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 0); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var scriptLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, ImmutableArray.Create <LocalSymbol>(scriptLocal.LocalSymbol), ImmutableArray <LocalFunctionSymbol> .Empty, ImmutableArray.Create <BoundStatement>( // var script = new Script(); new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, scriptLocal, new BoundObjectCreationExpression( syntax, ctor) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // script.<Initialize>().GetAwaiter().GetResult(); new BoundExpressionStatement( syntax, CreateParameterlessCall( syntax, CreateParameterlessCall( syntax, CreateParameterlessCall( syntax, scriptLocal, initializer), _getAwaiterMethod), _getResultMethod)) { WasCompilerGenerated = true }, // return; new BoundReturnStatement( syntax, RefKind.None, null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }); }
// Used to increment integer index into an array or string. private static BoundExpressionStatement MakePositionIncrement(SyntaxNode syntax, BoundLocal boundPositionVar, TypeSymbol intType) { return new BoundExpressionStatement(syntax, expression: new BoundAssignmentOperator(syntax, left: boundPositionVar, right: new BoundBinaryOperator(syntax, operatorKind: BinaryOperatorKind.IntAddition, // unchecked, never overflows since array/string index can't be >= Int32.MaxValue left: boundPositionVar, right: new BoundLiteral(syntax, constantValueOpt: ConstantValue.ConstantValueOne.Int32, type: intType), constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: intType), type: intType)); }
// private static void <Main>() // { // var script = new Script(); // script.<Initialize>().GetAwaiter().GetResult(); // } internal override BoundBlock CreateBody(DiagnosticBag diagnostics) { var syntax = DummySyntax(); var compilation = _containingType.DeclaringCompilation; // Creates a new top-level binder that just contains the global imports for the compilation. // The imports are required if a consumer of the scripting API is using a Task implementation // that uses extension methods. var binder = new InContainerBinder( container: null, next: new BuckStopsHereBinder(compilation), imports: compilation.GlobalImports); binder = new InContainerBinder(compilation.GlobalNamespace, binder); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 0); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var scriptLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; var initializeCall = CreateParameterlessCall(syntax, scriptLocal, initializer); BoundExpression getAwaiterGetResultCall; if (!binder.GetAwaitableExpressionInfo(initializeCall, out _, out _, out _, out getAwaiterGetResultCall, syntax, diagnostics)) { return(new BoundBlock( syntax: syntax, locals: ImmutableArray <LocalSymbol> .Empty, statements: ImmutableArray <BoundStatement> .Empty, hasErrors: true)); } return(new BoundBlock(syntax, ImmutableArray.Create <LocalSymbol>(scriptLocal.LocalSymbol), ImmutableArray.Create <BoundStatement>( // var script = new Script(); new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, scriptLocal, new BoundObjectCreationExpression( syntax, ctor, null) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // script.<Initialize>().GetAwaiter().GetResult(); new BoundExpressionStatement(syntax, getAwaiterGetResultCall) { WasCompilerGenerated = true }, // return; new BoundReturnStatement( syntax, RefKind.None, null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }); }
public override BoundNode VisitLocal(BoundLocal node) { var result = RewriteLocal(node); Debug.Assert(result.Type == node.Type); return result; }
// private static T <Factory>(object[] submissionArray) // { // var submission = new Submission#N(submissionArray); // return submission.<Initialize>(); // } internal override BoundBlock CreateBody(DiagnosticBag diagnostics) { var syntax = DummySyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; // var submission = new Submission#N(submissionArray); var submissionAssignment = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create <BoundExpression>(submissionArrayParameter), default(ImmutableArray <string>), default(ImmutableArray <RefKind>), false, default(ImmutableArray <int>), null, null, null, _containingType) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // return submission.<Initialize>(); var initializeResult = CreateParameterlessCall( syntax, submissionLocal, initializer); Debug.Assert(initializeResult.Type == _returnType); var returnStatement = new BoundReturnStatement( syntax, RefKind.None, initializeResult) { WasCompilerGenerated = true }; return(new BoundBlock(syntax, ImmutableArray.Create <LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray.Create <BoundStatement>(submissionAssignment, returnStatement)) { WasCompilerGenerated = true }); }
// private static void <Main>() // { // var script = new Script(); // script.<Initialize>().GetAwaiter().GetResult(); // } internal override BoundBlock CreateBody() { // CreateBody should only be called if no errors. Debug.Assert((object)_getAwaiterMethod != null); Debug.Assert((object)_getResultMethod != null); var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 0); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var scriptLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; return new BoundBlock(syntax, ImmutableArray.Create<LocalSymbol>(scriptLocal.LocalSymbol), ImmutableArray<LocalFunctionSymbol>.Empty, ImmutableArray.Create<BoundStatement>( // var script = new Script(); new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, scriptLocal, new BoundObjectCreationExpression( syntax, ctor) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // script.<Initialize>().GetAwaiter().GetResult(); new BoundExpressionStatement( syntax, CreateParameterlessCall( syntax, CreateParameterlessCall( syntax, CreateParameterlessCall( syntax, scriptLocal, initializer), _getAwaiterMethod), _getResultMethod)) { WasCompilerGenerated = true }, // return; new BoundReturnStatement( syntax, RefKind.None, null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }; }
private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used) { // Avoid rewriting if node has errors since at least // one of the operands is invalid. if (node.HasErrors) { return node; } var propertyAccessor = node.Left as BoundPropertyAccess; if (propertyAccessor == null) { return (BoundExpression)base.VisitAssignmentOperator(node); } // Rewrite property assignment into call to setter. var property = propertyAccessor.PropertySymbol.GetBaseProperty(); var setMethod = property.SetMethod; Debug.Assert(setMethod != null); Debug.Assert(setMethod.Parameters.Count == 1); Debug.Assert(!setMethod.IsOverride); var rewrittenReceiver = (BoundExpression)Visit(propertyAccessor.ReceiverOpt); var rewrittenArgument = (BoundExpression)Visit(node.Right); if (used) { // Save expression value to a temporary before calling the // setter, and restore the temporary after the setter, so the // assignment can be used as an embedded expression. var exprType = rewrittenArgument.Type; var tempSymbol = new TempLocalSymbol(exprType, RefKind.None, containingSymbol); var tempLocal = new BoundLocal(null, null, tempSymbol, null, exprType); var saveTemp = new BoundAssignmentOperator( null, null, tempLocal, rewrittenArgument, exprType); var call = BoundCall.SynthesizedCall( rewrittenReceiver, setMethod, saveTemp); return new BoundSequence( node.Syntax, node.SyntaxTree, ReadOnlyArray<LocalSymbol>.CreateFrom(tempSymbol), ReadOnlyArray<BoundExpression>.CreateFrom(call), tempLocal, exprType); } else { return BoundCall.SynthesizedCall( rewrittenReceiver, setMethod, rewrittenArgument); } }
private EEMethodSymbol GetPseudoVariableMethod( TypeNameDecoder<PEModuleSymbol, TypeSymbol> typeNameDecoder, EENamedTypeSymbol container, string methodName, Alias alias) { var syntax = SyntaxFactory.IdentifierName(SyntaxFactory.MissingToken(SyntaxKind.IdentifierToken)); return this.CreateMethod(container, methodName, syntax, (method, diagnostics) => { var local = PlaceholderLocalBinder.CreatePlaceholderLocal(typeNameDecoder, method, alias); var expression = new BoundLocal(syntax, local, constantValueOpt: null, type: local.Type); return new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; }); }
// Generates: // // private static T {Factory}(InteractiveSession session) // { // T submissionResult; // new {ThisScriptClass}(session, out submissionResult); // return submissionResult; // } private BoundBlock CreateSubmissionFactoryBody() { Debug.Assert(_containingType.TypeKind == TypeKind.Submission); SyntaxTree syntaxTree = CSharpSyntaxTree.Dummy; CSharpSyntaxNode syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); var interactiveSessionParam = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var ctor = _containingType.InstanceConstructors.Single(); Debug.Assert(ctor is SynthesizedInstanceConstructor); Debug.Assert(ctor.ParameterCount == 2); var submissionResultType = ctor.Parameters[1].Type; var resultLocal = new SynthesizedLocal(ctor, submissionResultType, SynthesizedLocalKind.LoweringTemp); var localReference = new BoundLocal(syntax, resultLocal, null, submissionResultType) { WasCompilerGenerated = true }; BoundExpression submissionResult = localReference; if (submissionResultType.IsStructType() && _returnType.SpecialType == SpecialType.System_Object) { submissionResult = new BoundConversion(syntax, submissionResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType) { WasCompilerGenerated = true }; } return new BoundBlock(syntax, // T submissionResult; ImmutableArray.Create<LocalSymbol>(resultLocal), ImmutableArray.Create<BoundStatement>( // new Submission(interactiveSession, out submissionResult); new BoundExpressionStatement(syntax, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create<BoundExpression>(interactiveSessionParam, localReference), ImmutableArray<string>.Empty, ImmutableArray.Create<RefKind>(RefKind.None, RefKind.Ref), false, default(ImmutableArray<int>), null, null, _containingType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // return submissionResult; new BoundReturnStatement(syntax, submissionResult) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }; }
private EEMethodSymbol GetLocalMethod(EENamedTypeSymbol container, string methodName, string localName, int localIndex) { var syntax = SyntaxFactory.IdentifierName(localName); return this.CreateMethod(container, methodName, syntax, (method, diagnostics) => { var local = method.LocalsForBinding[localIndex]; var expression = new BoundLocal(syntax, local, constantValueOpt: local.GetConstantValue(null, null, diagnostics), type: local.Type); return new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true }; }); }
public override BoundNode VisitLocal(BoundLocal node) { if (node.ConstantValueOpt == null) { switch (this.context) { case ExprContext.Address: if (node.LocalSymbol.RefKind != RefKind.None) { RecordVarRead(node.LocalSymbol); } else { RecordVarRef(node.LocalSymbol); } break; case ExprContext.AssignmentTarget: Debug.Assert(assignmentLocal == null); // actual assignment will happen later, after Right is evaluated // just remember what we are assigning to. assignmentLocal = node; // whatever is available as lastExpression is still available // (adjust for visit of this node) this.lastExpressionCnt++; break; case ExprContext.Sideeffects: break; case ExprContext.Value: case ExprContext.Box: RecordVarRead(node.LocalSymbol); break; } } return base.VisitLocal(node); }
public override BoundNode VisitCatchBlock(BoundCatchBlock node) { EnsureOnlyEvalStack(); var exceptionSourceOpt = node.ExceptionSourceOpt; DeclareLocals(node.Locals, stack: 0); if (exceptionSourceOpt != null) { // runtime pushes the exception object PushEvalStack(null, ExprContext.None); _counter++; // We consume it by writing into the exception source. if (exceptionSourceOpt.Kind == BoundKind.Local) { RecordVarWrite(((BoundLocal)exceptionSourceOpt).LocalSymbol); } else { int prevStack = StackDepth(); exceptionSourceOpt = VisitExpression(exceptionSourceOpt, ExprContext.AssignmentTarget); _assignmentLocal = null; // not using this for exceptionSource SetStackDepth(prevStack); } PopEvalStack(); _counter++; } BoundExpression boundFilter; if (node.ExceptionFilterOpt != null) { boundFilter = (BoundExpression)this.Visit(node.ExceptionFilterOpt); // the value of filter expression is consumed by the VM PopEvalStack(); _counter++; // variables allocated on stack in a filter can't be used in the catch handler EnsureOnlyEvalStack(); } else { boundFilter = null; } var boundBlock = (BoundBlock)this.Visit(node.Body); var exceptionTypeOpt = this.VisitType(node.ExceptionTypeOpt); return node.Update(node.Locals, exceptionSourceOpt, exceptionTypeOpt, boundFilter, boundBlock, node.IsSynthesizedAsyncCatchAll); }
// Generates: // // private static T {Factory}(InteractiveSession session) // { // T submissionResult; // new {ThisScriptClass}(session, out submissionResult); // return submissionResult; // } private BoundBlock CreateSubmissionFactoryBody() { Debug.Assert(containingType.TypeKind == TypeKind.Submission); SyntaxTree syntaxTree = CSharpSyntaxTree.Dummy; CSharpSyntaxNode syntax = (CSharpSyntaxNode)syntaxTree.GetRoot(); var interactiveSessionParam = new BoundParameter(syntax, parameters[0]) { WasCompilerGenerated = true }; var ctor = containingType.InstanceConstructors.Single(); Debug.Assert(ctor is SynthesizedInstanceConstructor); Debug.Assert(ctor.ParameterCount == 2); var submissionResultType = ctor.Parameters[1].Type; var submissionResult = new BoundLocal(syntax, new SynthesizedLocal(ctor, submissionResultType), null, submissionResultType) { WasCompilerGenerated = true }; return new BoundBlock(syntax, // T submissionResult; ImmutableArray.Create<LocalSymbol>(submissionResult.LocalSymbol), ImmutableArray.Create<BoundStatement>( // new Submission(interactiveSession, out submissionResult); new BoundExpressionStatement(syntax, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create<BoundExpression>(interactiveSessionParam, submissionResult), ImmutableArray<string>.Empty, ImmutableArray.Create<RefKind>(RefKind.None, RefKind.Ref), false, default(ImmutableArray<int>), null, null, containingType ) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // return submissionResult; new BoundReturnStatement(syntax, submissionResult) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }; }
private BoundNode VisitUnhoistedLocal(BoundLocal node) { LocalSymbol replacementLocal; if (this.localMap.TryGetValue(node.LocalSymbol, out replacementLocal)) { return new BoundLocal(node.Syntax, replacementLocal, node.ConstantValueOpt, replacementLocal.Type, node.HasErrors); } return base.VisitLocal(node); }
// private static void <Main>() // { // var script = new Script(); // script.<Initialize>(); // } internal override BoundBlock CreateBody() { var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 0); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; return new BoundBlock(syntax, ImmutableArray.Create<LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray.Create<BoundStatement>( // var script = new Script(); new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // script.<Initialize>(); new BoundExpressionStatement( syntax, new BoundCall( syntax, submissionLocal, initializer, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<string>), default(ImmutableArray<RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray<int>), resultKind: LookupResultKind.Viable, type: initializer.ReturnType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }, // return; new BoundReturnStatement( syntax, null) { WasCompilerGenerated = true })) { WasCompilerGenerated = true }; }
public override BoundNode VisitLocal(BoundLocal node) { LocalDefUseInfo locInfo; if (!_info.TryGetValue(node.LocalSymbol, out locInfo)) { return base.VisitLocal(node); } // not the last access, emit Dup. if (!IsLastAccess(locInfo, _nodeCounter)) { return new BoundDup(node.Syntax, node.LocalSymbol.RefKind, node.Type); } // last access - leave the node as is. Emit will do nothing expecting the node on the stack return base.VisitLocal(node); }
// private static T <Factory>(object[] submissionArray) // { // var submission = new Submission#N(submissionArray); // return submission.<Initialize>(); // } internal override BoundBlock CreateBody() { var syntax = this.GetSyntax(); var ctor = _containingType.GetScriptConstructor(); Debug.Assert(ctor.ParameterCount == 1); var initializer = _containingType.GetScriptInitializer(); Debug.Assert(initializer.ParameterCount == 0); var submissionArrayParameter = new BoundParameter(syntax, _parameters[0]) { WasCompilerGenerated = true }; var submissionLocal = new BoundLocal( syntax, new SynthesizedLocal(this, _containingType, SynthesizedLocalKind.LoweringTemp), null, _containingType) { WasCompilerGenerated = true }; // var submission = new Submission#N(submissionArray); var submissionAssignment = new BoundExpressionStatement( syntax, new BoundAssignmentOperator( syntax, submissionLocal, new BoundObjectCreationExpression( syntax, ctor, ImmutableArray.Create<BoundExpression>(submissionArrayParameter), default(ImmutableArray<string>), default(ImmutableArray<RefKind>), false, default(ImmutableArray<int>), null, null, _containingType) { WasCompilerGenerated = true }, _containingType) { WasCompilerGenerated = true }) { WasCompilerGenerated = true }; // return submission.<Initialize>(); BoundExpression initializeResult = new BoundCall( syntax, submissionLocal, initializer, ImmutableArray<BoundExpression>.Empty, default(ImmutableArray<string>), default(ImmutableArray<RefKind>), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray<int>), resultKind: LookupResultKind.Viable, type: initializer.ReturnType) { WasCompilerGenerated = true }; if (initializeResult.Type.IsStructType() && (_returnType.SpecialType == SpecialType.System_Object)) { initializeResult = new BoundConversion(syntax, initializeResult, Conversion.Boxing, false, true, ConstantValue.NotAvailable, _returnType) { WasCompilerGenerated = true }; } var returnStatement = new BoundReturnStatement( syntax, initializeResult) { WasCompilerGenerated = true }; return new BoundBlock(syntax, ImmutableArray.Create<LocalSymbol>(submissionLocal.LocalSymbol), ImmutableArray.Create<BoundStatement>(submissionAssignment, returnStatement)) { WasCompilerGenerated = true }; }
public override BoundNode VisitLocal(BoundLocal node) { if (node.ConstantValueOpt == null) { switch (_context) { case ExprContext.Address: if (node.LocalSymbol.RefKind != RefKind.None) { RecordVarRead(node.LocalSymbol); } else { RecordVarRef(node.LocalSymbol); } break; case ExprContext.AssignmentTarget: Debug.Assert(_assignmentLocal == null); // actual assignment will happen later, after Right is evaluated // just remember what we are assigning to. _assignmentLocal = node; break; case ExprContext.Sideeffects: break; case ExprContext.Value: case ExprContext.Box: RecordVarRead(node.LocalSymbol); break; } } return base.VisitLocal(node); }
private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node) { ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax; BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node); Debug.Assert(collectionExpression.Type.IsArray()); ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type; int rank = arrayType.Rank; Debug.Assert(rank > 1); TypeSymbol intType = compilation.GetSpecialType(SpecialType.System_Int32); TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean); BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression); BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body); // A[...] a LocalSymbol arrayVar = new TempLocalSymbol(arrayType, RefKind.None, containingMethod); // A[...] a = /*node.Expression*/; BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression); AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl); // Reference to a. BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType); // int p_0, p_1, ... LocalSymbol[] positionVar = new LocalSymbol[rank]; BoundLocal[] boundPositionVar = new BoundLocal[rank]; for (int dimension = 0; dimension < rank; dimension++) { positionVar[dimension] = new TempLocalSymbol(intType, RefKind.None, containingMethod); boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType); } // V v LocalSymbol iterationVar = node.IterationVariable; TypeSymbol iterationVarType = iterationVar.Type; // (V)a[p_0, p_1, ...] BoundExpression iterationVarInitValue = SynthesizeConversion( syntax: forEachSyntax, operand: new BoundArrayAccess(forEachSyntax, expression: boundArrayVar, indices: ReadOnlyArray<BoundExpression>.CreateFrom((BoundExpression[])boundPositionVar), type: arrayType.ElementType), conversion: node.ElementConversion, type: iterationVarType); // V v = (V)a[p_0, p_1, ...]; BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue); AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl); // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement innermostLoopBody = new BoundBlock(forEachSyntax, localsOpt: ReadOnlyArray<LocalSymbol>.CreateFrom(iterationVar), statements: ReadOnlyArray<BoundStatement>.CreateFrom(iterationVarDecl, rewrittenBody)); // Values we'll use every iteration MethodSymbol getLowerBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetLowerBound); MethodSymbol getUpperBoundMethod = (MethodSymbol)this.compilation.GetSpecialTypeMember(SpecialMember.System_Array__GetUpperBound); // work from most-nested to least-nested // for (A[...] a = /*node.Expression*/; int p_0 = a.GetLowerBound(0); p_0 <= a.GetUpperBound(0); p_0 = p_0 + 1) // for (int p_1 = a.GetLowerBound(0); p_1 <= a.GetUpperBound(0); p_1 = p_1 + 1) // ... // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ } BoundStatement forLoop = null; for (int dimension = rank - 1; dimension >= 0; dimension--) { ReadOnlyArray<BoundExpression> dimensionArgument = ReadOnlyArray<BoundExpression>.CreateFrom( new BoundLiteral(forEachSyntax, constantValueOpt: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32), type: intType)); // a.GetLowerBound(/*dimension*/) BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); // a.GetUpperBound(/*dimension*/) //CONSIDER: dev10 creates a temp for each dimension's upper bound BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); // int p_/*dimension*/ = a.GetLowerBound(/*dimension*/); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); ReadOnlyArray<LocalSymbol> locals; BoundStatement initializer; GeneratedLabelSymbol breakLabel; if (dimension == 0) { // outermost for-loop locals = ReadOnlyArray<LocalSymbol>.CreateFrom(arrayVar, positionVar[dimension]); initializer = new BoundStatementList(forEachSyntax, statements: ReadOnlyArray<BoundStatement>.CreateFrom(arrayVarDecl, positionVarDecl)); breakLabel = node.BreakLabel; // i.e. the one that break statements will jump to } else { locals = ReadOnlyArray<LocalSymbol>.CreateFrom(positionVar[dimension]); initializer = positionVarDecl; breakLabel = new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused } // p_/*dimension*/ <= a.GetUpperBound(/*dimension*/) //NB: OrEqual BoundExpression exitCondition = new BoundBinaryOperator( syntax: forEachSyntax, operatorKind: BinaryOperatorKind.IntLessThanOrEqual, left: boundPositionVar[dimension], right: currentDimensionUpperBound, constantValueOpt: null, methodOpt: null, resultKind: LookupResultKind.Viable, type: boolType); // p_/*dimension*/ = p_/*dimension*/ + 1; BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType); BoundStatement body; GeneratedLabelSymbol continueLabel; if(forLoop == null) { // innermost for-loop body = innermostLoopBody; continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to } else { body = forLoop; continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused } forLoop = RewriteForStatement( node.Syntax, locals, initializer, exitCondition, forEachSyntax.InKeyword, positionIncrement, body, breakLabel, continueLabel, node.HasErrors); } Debug.Assert(forLoop != null); AddForEachExpressionSequencePoint(forEachSyntax, ref forLoop); return forLoop; }
private void EmitLocalLoad(BoundLocal local, bool used) { if (IsStackLocal(local.LocalSymbol)) { // local must be already on the stack EmitPopIfUnused(used); } else { if (used) { LocalDefinition definition = GetLocal(local); _builder.EmitLocalLoad(definition); } else { // do nothing. Unused local load has no side-effects. return; } } if (used && local.LocalSymbol.RefKind != RefKind.None) { EmitLoadIndirect(local.LocalSymbol.Type, local.Syntax); } }
/// <summary> /// The rewrites are as follows: /// /// x++ /// temp = x /// x = temp + 1 /// return temp /// x-- /// temp = x /// x = temp - 1 /// return temp /// ++x /// temp = x + 1 /// x = temp /// return temp /// --x /// temp = x - 1 /// x = temp /// return temp /// /// In each case, the literal 1 is of the type required by the builtin addition/subtraction operator that /// will be used. The temp is of the same type as x, but the sum/difference may be wider, in which case a /// conversion is required. /// </summary> /// <param name="node">The unary operator expression representing the increment/decrement.</param> /// <param name="isPrefix">True for prefix, false for postfix.</param> /// <param name="isIncrement">True for increment, false for decrement.</param> /// <returns>A bound sequence that uses a temp to acheive the correct side effects and return value.</returns> private BoundNode LowerOperator(BoundUnaryOperator node, bool isPrefix, bool isIncrement) { BoundExpression operand = node.Operand; TypeSymbol operandType = operand.Type; //type of the variable being incremented Debug.Assert(operandType == node.Type); ConstantValue constantOne; BinaryOperatorKind binaryOperatorKind; MakeConstantAndOperatorKind(node.OperatorKind.OperandTypes(), node, out constantOne, out binaryOperatorKind); binaryOperatorKind |= isIncrement ? BinaryOperatorKind.Addition : BinaryOperatorKind.Subtraction; Debug.Assert(constantOne != null); Debug.Assert(constantOne.SpecialType != SpecialType.None); Debug.Assert(binaryOperatorKind.OperandTypes() != 0); TypeSymbol constantType = compilation.GetSpecialType(constantOne.SpecialType); BoundExpression boundOne = new BoundLiteral( syntax: null, syntaxTree: null, constantValueOpt: constantOne, type: constantType); LocalSymbol tempSymbol = new TempLocalSymbol(operandType, RefKind.None, containingSymbol); BoundExpression boundTemp = new BoundLocal( syntax: null, syntaxTree: null, localSymbol: tempSymbol, constantValueOpt: null, type: operandType); // NOTE: the LHS may have a narrower type than the operator expects, but that // doesn't seem to cause any problems. If a problem does arise, just add an // explicit BoundConversion. BoundExpression newValue = new BoundBinaryOperator( syntax: null, syntaxTree: null, operatorKind: binaryOperatorKind, left: isPrefix ? operand : boundTemp, right: boundOne, constantValueOpt: null, type: constantType); if (constantType != operandType) { newValue = new BoundConversion( syntax: null, syntaxTree: null, operand: newValue, conversionKind: operandType.IsEnumType() ? ConversionKind.ImplicitEnumeration : ConversionKind.ImplicitNumeric, symbolOpt: null, @checked: false, explicitCastInCode: false, constantValueOpt: null, type: operandType); } ReadOnlyArray <BoundExpression> assignments = ReadOnlyArray <BoundExpression> .CreateFrom( new BoundAssignmentOperator( syntax : null, syntaxTree : null, left : boundTemp, right : isPrefix ? newValue : operand, type : operandType), new BoundAssignmentOperator( syntax : null, syntaxTree : null, left : operand, right : isPrefix ? boundTemp : newValue, type : operandType)); return(new BoundSequence( syntax: node.Syntax, syntaxTree: node.SyntaxTree, locals: ReadOnlyArray <LocalSymbol> .CreateFrom(tempSymbol), sideEffects: assignments, value: boundTemp, type: operandType)); }