private MSA.Expression /*!*/ MakeParametersInitialization(AstGenerator /*!*/ gen, MSA.Expression[] /*!*/ parameters) { Assert.NotNull(gen); Assert.NotNullItems(parameters); var result = AstFactory.CreateExpressionArray( _parameters.LeftValues.Count + (_parameters.UnsplattedValue != null ? 1 : 0) + 1 ); int resultIndex = 0; bool paramsInArray = HasFormalParametersInArray; for (int i = 0; i < _parameters.LeftValues.Count; i++) { var parameter = paramsInArray ? Ast.ArrayAccess(parameters[HiddenParameterCount], Ast.Constant(i)) : parameters[HiddenParameterCount + i]; result[resultIndex++] = _parameters.LeftValues[i].TransformWrite(gen, parameter); } if (_parameters.UnsplattedValue != null) { // the last parameter is unsplat: var parameter = parameters[parameters.Length - 1]; result[resultIndex++] = _parameters.UnsplattedValue.TransformWrite(gen, parameter); } result[resultIndex++] = Ast.Empty(); Debug.Assert(resultIndex == result.Length); return(AstFactory.Block(result)); }
// when [<expr>, ...] *<array> // // generates this code: // // IEnumerator<object>/*!*/ enumVar = RubyOps.Unsplat(<array>).GetEnumerator(); // bool result = false; // while (enumVar.MoveNext()) { // if (<MakeTest>(enumVar.Current)) { // result = true; // break; // } // } private static MSA.Expression /*!*/ MakeArrayTest(AstGenerator /*!*/ gen, MSA.Expression /*!*/ array, MSA.Expression value) { MSA.Expression enumVariable = gen.CurrentScope.DefineHiddenVariable("#case-enumerator", typeof(IEnumerator <object>)); MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#case-compare-result", typeof(bool)); MSA.LabelTarget label = Ast.Label(); return(AstFactory.Block( Ast.Assign(enumVariable, Ast.Call( Methods.Unsplat.OpCall(AstFactory.Box(array)), Methods.IEnumerable_Of_Object_GetEnumerator )), Ast.Assign(resultVariable, Ast.Constant(false)), AstUtils.While( Ast.Call(enumVariable, Methods.IEnumerator_MoveNext), AstUtils.If( MakeTest(gen, Ast.Call(enumVariable, Methods.IEnumerator_get_Current), value), Ast.Block( Ast.Assign(resultVariable, Ast.Constant(true)), Ast.Break(label), Ast.Empty() ) ), null, label, null ), resultVariable )); }
internal sealed override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { string debugString = (IsSingletonDeclaration) ? "SINGLETON" : ((this is ClassDeclaration) ? "CLASS" : "MODULE") + " " + QualifiedName.Name; ScopeBuilder outerLocals = gen.CurrentScope; // definition needs to take place outside the defined lexical scope: MSA.Expression definition = MakeDefinitionExpression(gen); MSA.Expression selfVariable = outerLocals.DefineHiddenVariable("#module", typeof(RubyModule)); MSA.Expression rfcVariable = gen.CurrentRfcVariable; MSA.Expression parentScope = gen.CurrentScopeVariable; // inner locals: ScopeBuilder scope = new ScopeBuilder(); MSA.Expression scopeVariable = scope.DefineHiddenVariable("#scope", typeof(RubyScope)); gen.EnterModuleDefinition( scope, selfVariable, scopeVariable, IsSingletonDeclaration ); // first, transform locals defined within the module body: DefinedScope.TransformLocals(scope); // second, transform body: MSA.Expression transformedBody = Body.TransformRead(gen); // outer local: MSA.Expression resultVariable = outerLocals.DefineHiddenVariable("#result", transformedBody.Type); // begin with new scope // self = DefineModule/Class(... parent scope here ...) // <body> // end MSA.Expression result = AstFactory.Block( gen.DebugMarker(debugString), Ast.Assign(selfVariable, definition), scope.CreateScope( Ast.Block( Ast.Assign(scopeVariable, Methods.CreateModuleScope.OpCall(scope.VisibleVariables(), parentScope, rfcVariable, selfVariable)), Ast.Assign(resultVariable, transformedBody), Ast.Empty() ) ), gen.DebugMarker("END OF " + debugString), resultVariable ); gen.LeaveModuleDefinition(); return(result); }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { MSA.Expression transformedCondition = AstFactory.Box(_condition.TransformRead(gen)); MSA.Expression tmpVariable = gen.CurrentScope.DefineHiddenVariable("#tmp_cond", transformedCondition.Type); return(AstFactory.Block( Ast.Assign(tmpVariable, transformedCondition), AstUtils.IfThen( (_negateCondition ? AstFactory.IsFalse(tmpVariable) : AstFactory.IsTrue(tmpVariable)), _jumpStatement.Transform(gen) ), (_value != null) ? _value.TransformRead(gen) : tmpVariable )); }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { Assert.NotNull(gen); if (HasExceptionHandling) { MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#block-result", typeof(object)); return(AstFactory.Block( TransformExceptionHandling(gen, ResultOperation.Store(resultVariable)), resultVariable )); } else { return(gen.TransformStatementsToExpression(_statements)); } }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { MSA.Expression result; if (_elseStatements != null) { // ... else body end result = gen.TransformStatementsToExpression(_elseStatements); } else { // no else clause => the result of the if-expression is nil: result = Ast.Constant(null); } MSA.Expression value; if (_value != null) { value = gen.CurrentScope.DefineHiddenVariable("#case-compare-value", typeof(object)); } else { value = null; } for (int i = _whenClauses.Count - 1; i >= 0; i--) { // emit: else (if (condition) body else result) result = AstFactory.Condition( TransformWhenCondition(gen, _whenClauses[i].Comparisons, _whenClauses[i].ComparisonArray, value), gen.TransformStatementsToExpression(_whenClauses[i].Statements), result ); } if (_value != null) { result = AstFactory.Block( Ast.Assign(value, Ast.Convert(_value.TransformRead(gen), typeof(object))), result ); } return(result); }
// see Ruby Language.doc/Runtime/Control Flow Implementation/Yield internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { MSA.Expression bfcVariable = gen.CurrentScope.DefineHiddenVariable("#yielded-bfc", typeof(BlockParam)); MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#result", typeof(object)); MSA.Expression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); MSA.Expression postYield; if (gen.CompilerOptions.IsEval) { // eval: postYield = Methods.EvalYield.OpCall(gen.CurrentRfcVariable, bfcVariable, resultVariable); } else if (gen.CurrentBlock != null) { // block: postYield = Methods.BlockYield.OpCall(gen.CurrentRfcVariable, gen.CurrentBlock.BfcVariable, bfcVariable, resultVariable); } else { // method: postYield = Methods.MethodYield.OpCall(gen.CurrentRfcVariable, bfcVariable, resultVariable); } return(AstFactory.Block( gen.DebugMarker("#RB: yield begin"), Ast.Assign(bfcVariable, Methods.CreateBfcForYield.OpCall(gen.MakeMethodBlockParameterRead())), Ast.Assign(resultVariable, (Arguments ?? Arguments.Empty).TransformToYield(gen, bfcVariable, Ast.Property(AstUtils.Convert(gen.MakeMethodBlockParameterRead(), typeof(Proc)), Proc.SelfProperty) )), AstUtils.IfThen(postYield, gen.Return(resultVariable)), gen.DebugMarker("#RB: yield end"), resultVariable )); }
internal static MSA.Expression /*!*/ MakeCallWithBlockRetryable(AstGenerator /*!*/ gen, MSA.Expression /*!*/ invoke, MSA.Expression blockArgVariable, MSA.Expression transformedBlock, bool isBlockDefinition) { Assert.NotNull(invoke); Debug.Assert((blockArgVariable == null) == (transformedBlock == null)); // see Ruby Language.doc/Control Flow Implementation/Method Call With a Block MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#method-result", typeof(object)); MSA.ParameterExpression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); MSA.LabelTarget label = Ast.Label(); return(AstFactory.Block( Ast.Assign(blockArgVariable, Ast.Convert(transformedBlock, blockArgVariable.Type)), AstFactory.Infinite(label, null, (!isBlockDefinition) ? (MSA.Expression)Ast.Empty() : (MSA.Expression)Methods.InitializeBlock.OpCall(blockArgVariable), AstUtils.Try( Ast.Assign(resultVariable, invoke) ).Catch(evalUnwinder, Ast.Assign( resultVariable, Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField) ) ), // if result != RetrySingleton then break end AstUtils.Unless(Methods.IsRetrySingleton.OpCall(AstFactory.Box(resultVariable)), Ast.Break(label)), // if blockParam == #block then retry end (gen.CurrentMethod.IsTopLevelCode) ? Ast.Empty() : AstUtils.IfThen(Ast.Equal(gen.MakeMethodBlockParameterRead(), blockArgVariable), RetryStatement.TransformRetry(gen)) ), resultVariable )); }
internal MSA.Expression /*!*/ DebugMark(MSA.Expression /*!*/ expression, string /*!*/ marker) { return(_debugCompiler ? AstFactory.Block(Methods.X.OpCall(Ast.Constant(marker)), expression) : expression); }
// arguments: complex arguments (expressions, maplets, splat, block) // singleArgument: siple argument (complex are not used) // assignmentRhsArgument: rhs of the assignment: target.method=(rhs) internal static MSA.Expression /*!*/ TransformRead(Expression /*!*/ node, AstGenerator /*!*/ gen, bool hasImplicitSelf, string /*!*/ methodName, MSA.Expression /*!*/ transformedTarget, Arguments arguments, Block block, MSA.Expression singleArgument, MSA.Expression assignmentRhsArgument) { Debug.Assert(assignmentRhsArgument == null || block == null, "Block not allowed in assignment"); Debug.Assert(singleArgument == null || arguments == null && assignmentRhsArgument == null); Assert.NotNull(gen, transformedTarget); Assert.NotEmpty(methodName); // Pass args in this order: // 1. instance // 2. block (if present) // 3. passed args: normal args, maplets, array // 4. RHS of assignment (if present) CallBuilder callBuilder = new CallBuilder(gen); callBuilder.Instance = transformedTarget; MSA.Expression blockArgVariable = null; MSA.Expression transformedBlock = null; if (block != null) { blockArgVariable = gen.CurrentScope.DefineHiddenVariable("#block-def", typeof(Proc)); transformedBlock = block.Transform(gen); callBuilder.Block = blockArgVariable; } if (arguments != null) { arguments.TransformToCall(gen, callBuilder); } else if (singleArgument != null) { callBuilder.Add(singleArgument); } MSA.Expression rhsVariable = null; if (assignmentRhsArgument != null) { rhsVariable = gen.CurrentScope.DefineHiddenVariable("#rhs", assignmentRhsArgument.Type); callBuilder.RhsArgument = Ast.Assign(rhsVariable, assignmentRhsArgument); } var dynamicSite = callBuilder.MakeCallAction(methodName, hasImplicitSelf); gen.TraceCallSite(node, dynamicSite); MSA.Expression result = gen.DebugMark(dynamicSite, methodName); if (block != null) { result = gen.DebugMark(MakeCallWithBlockRetryable(gen, result, blockArgVariable, transformedBlock, block.IsDefinition), "#RB: method call with a block ('" + methodName + "')"); } if (assignmentRhsArgument != null) { result = AstFactory.Block(result, rhsVariable); } return(result); }
// // rescue stmts ... if (StandardError === $!) { stmts; } // rescue <types> stmts ... temp1 = type1; ...; if (<temp1> === $! || ...) { stmts; } // rescue <types> => <lvalue> stmts ... temp1 = type1; ...; if (<temp1> === $! || ...) { <lvalue> = $!; stmts; } // internal IfStatementTest /*!*/ Transform(AstGenerator /*!*/ gen, ResultOperation resultOperation) { Assert.NotNull(gen); MSA.Expression condition; if (_types.Count != 0 || _splatType != null) { if (_types.Count == 0) { // splat only: condition = MakeCompareSplattedExceptions(gen, TransformSplatType(gen)); } else if (_types.Count == 1 && _splatType == null) { condition = MakeCompareException(gen, _types[0].TransformRead(gen)); } else { // forall{i}: <temps[i]> = evaluate type[i] var temps = new MSA.Expression[_types.Count + (_splatType != null ? 1 : 0)]; var exprs = new MSA.Expression[temps.Length + 1]; int i = 0; while (i < _types.Count) { var tmp = gen.CurrentScope.DefineHiddenVariable("#type_" + i, typeof(object)); temps[i] = tmp; exprs[i] = Ast.Assign(tmp, _types[i].TransformRead(gen)); i++; } if (_splatType != null) { var tmp = gen.CurrentScope.DefineHiddenVariable("#type_" + i, typeof(object)); temps[i] = tmp; exprs[i] = Ast.Assign(tmp, TransformSplatType(gen)); i++; } Debug.Assert(i == temps.Length); // CompareException(<temps[0]>) || ... CompareException(<temps[n]>) || CompareSplattedExceptions(<splatTypes>) i = 0; condition = MakeCompareException(gen, temps[i++]); while (i < _types.Count) { condition = Ast.OrElse(condition, MakeCompareException(gen, temps[i++])); } if (_splatType != null) { condition = Ast.OrElse(condition, MakeCompareSplattedExceptions(gen, temps[i++])); } Debug.Assert(i == temps.Length); // (temps[0] = type[0], ..., temps[n] == type[n], condition) exprs[exprs.Length - 1] = condition; condition = AstFactory.Block(exprs); } } else { condition = Methods.CompareDefaultException.OpCall(gen.CurrentScopeVariable, gen.CurrentSelfVariable); } return(AstUtils.IfCondition(condition, gen.TransformStatements( // <lvalue> = e; (_target != null) ? _target.TransformWrite(gen, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)) : null, // body: _statements, resultOperation ) )); }
private MSA.Expression /*!*/ TransformWrite(AstGenerator /*!*/ gen, List <MSA.Expression> /*!*/ rightValues, MSA.Expression splattedValue) { // We need to distinguish various special cases here. // Each of the bool variables defined below is true iff the corresponding special form of LHS/RHS occurs. // These flags drive the DLR AST being produced by this method. // For parallel assignment specification, see "Ruby Language.docx/Runtime/Parallel Assignment". // L(0,-) not applicable Debug.Assert(!(_leftValues.Count == 0 && _unsplattedValue == null)); // L(1,-)? bool leftOneNone = _leftValues.Count == 1 && _unsplattedValue == null; // L(0,*)? bool leftNoneSplat = _leftValues.Count == 0 && _unsplattedValue != null; // R(0,*)? bool rightNoneSplat = rightValues.Count == 0 && splattedValue != null; // R(1,-)? bool rightOneNone = rightValues.Count == 1 && splattedValue == null; // R(1,*)? bool rightOneSplat = rightValues.Count == 1 && splattedValue != null; // R(0,-) not applicable Debug.Assert(!(rightValues.Count == 0 && splattedValue == null)); MSA.Expression resultExpression; if (leftOneNone) { // L(1,-): // recurse right away (X) = RHS is equivalent to X = RHS: CompoundLeftValue compound = _leftValues[0] as CompoundLeftValue; if (compound != null) { return(compound.TransformWrite(gen, rightValues, splattedValue)); } if (rightOneSplat) { // R(1,*) resultExpression = Methods.SplatPair.OpCall(AstFactory.Box(rightValues[0]), AstFactory.Box(splattedValue)); } else { // case 1: R(1,-) // case 2: R(0,*) // case 3: otherwise resultExpression = Arguments.TransformRead(gen, rightValues, splattedValue, true /* Splat */); } return(_leftValues[0].TransformWrite(gen, resultExpression)); } bool optimizeReads = true; if (rightOneNone && !leftNoneSplat) { // R(1,-) && !L(0,*) resultExpression = Methods.Unsplat.OpCall(AstFactory.Box(rightValues[0])); optimizeReads = false; } else { // case 1: R(0,*) = L // case 2: otherwise resultExpression = Arguments.TransformRead(gen, rightValues, splattedValue, false /* Unsplat */); optimizeReads = !rightNoneSplat; } int writesCount = _leftValues.Count + (_unsplattedValue != null ? 1 : 0); if (writesCount == 0) { return(resultExpression); } var writes = new MSA.Expression[ 1 + // store result to a temp writesCount + 1 // load from the temp ]; int writeIndex = 0; MSA.Expression result = gen.CurrentScope.DefineHiddenVariable("#rhs", typeof(List <object>)); writes[writeIndex++] = Ast.Assign(result, resultExpression); MethodInfo itemGetter = typeof(List <object>).GetMethod("get_Item"); for (int i = 0; i < _leftValues.Count; i++) { MSA.Expression rvalue; if (optimizeReads) { if (i < rightValues.Count) { // unchecked get item: rvalue = Ast.Call(result, itemGetter, Ast.Constant(i)); } else if (splattedValue != null) { // checked get item: rvalue = Methods.GetArrayItem.OpCall(result, Ast.Constant(i)); } else { // missing item: rvalue = Ast.Constant(null); } } else { rvalue = Methods.GetArrayItem.OpCall(result, Ast.Constant(i)); } writes[writeIndex++] = _leftValues[i].TransformWrite(gen, rvalue); } // unsplatting the rest of rhs values into an array: if (_unsplattedValue != null) { // copies the rest of resulting array to the *LHS; // the resulting array contains splatted *RHS - no need for additional appending: MSA.Expression array = Methods.GetArraySuffix.OpCall(result, Ast.Constant(_leftValues.Count)); // assign the array (possibly empty) to *LHS: writes[writeIndex++] = _unsplattedValue.TransformWrite(gen, array); } writes[writeIndex++] = result; Debug.Assert(writes.Length == writeIndex); return(AstFactory.Block(writes)); }
internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { return(AstFactory.Block(Transform(gen), Ast.Constant(null))); }
// see Ruby Language.doc/Runtime/Control Flow Implementation/While-Until internal override MSA.Expression /*!*/ TransformRead(AstGenerator /*!*/ gen) { MSA.Expression resultVariable = gen.CurrentScope.DefineHiddenVariable("#loop-result", typeof(object)); MSA.Expression redoVariable = gen.CurrentScope.DefineHiddenVariable("#skip-condition", typeof(bool)); MSA.ParameterExpression blockUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(BlockUnwinder)); MSA.ParameterExpression evalUnwinder = gen.CurrentScope.DefineHiddenVariable("#unwinder", typeof(EvalUnwinder)); bool isInnerLoop = gen.CurrentLoop != null; MSA.LabelTarget breakLabel = Ast.Label(); MSA.LabelTarget continueLabel = Ast.Label(); gen.EnterLoop(redoVariable, resultVariable, breakLabel, continueLabel); MSA.Expression transformedBody = gen.TransformStatements(_statements, ResultOperation.Ignore); MSA.Expression transformedCondition = AstFactory.IsTrue(_condition.TransformRead(gen)); gen.LeaveLoop(); MSA.Expression conditionPositiveStmt, conditionNegativeStmt; if (_isWhileLoop) { conditionPositiveStmt = Ast.Empty(); conditionNegativeStmt = Ast.Break(breakLabel); } else { conditionPositiveStmt = Ast.Break(breakLabel); conditionNegativeStmt = Ast.Empty(); } // make the loop first: MSA.Expression loop = Ast.Block( Ast.Assign(redoVariable, Ast.Constant(_isPostTest)), AstFactory.Infinite(breakLabel, continueLabel, AstUtils.Try( AstUtils.If(redoVariable, Ast.Assign(redoVariable, Ast.Constant(false)) ).ElseIf(transformedCondition, conditionPositiveStmt ).Else( conditionNegativeStmt ), transformedBody ).Catch(blockUnwinder, // redo = u.IsRedo Ast.Assign(redoVariable, Ast.Field(blockUnwinder, BlockUnwinder.IsRedoField)) ).Filter(evalUnwinder, Ast.Equal(Ast.Field(evalUnwinder, EvalUnwinder.ReasonField), AstFactory.BlockReturnReasonBreak), // result = unwinder.ReturnValue Ast.Assign(resultVariable, Ast.Field(evalUnwinder, EvalUnwinder.ReturnValueField)), Ast.Break(breakLabel) ) ), Ast.Empty() ); // wrap it to try finally that updates RFC state: if (!isInnerLoop) { loop = AstUtils.Try( Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InLoopField), Ast.Constant(true)), loop ).Finally( Ast.Assign(Ast.Field(gen.CurrentRfcVariable, RuntimeFlowControl.InLoopField), Ast.Constant(false)) ); } return(AstFactory.Block(loop, resultVariable)); }