private JSForLoop ReplaceWhileLoopAndEnumerator(JSWhileLoop wl, JSExpression backingStore, JSExpression enumerator, TypeInfo enumeratorType, string arrayMember, string lengthMember) { var loopId = _NextLoopId++; var arrayVariableName = String.Format("a${0:x}", loopId); var indexVariableName = String.Format("i${0:x}", loopId); var lengthVariableName = String.Format("l${0:x}", loopId); var currentPropertyReference = enumeratorType.Definition.Properties.First((p) => p.Name == "Current"); var currentPropertyInfo = enumeratorType.Source.GetProperty(currentPropertyReference); var itemType = currentPropertyInfo.ReturnType; var arrayType = new ArrayType(itemType); var arrayVariable = new JSVariable( arrayVariableName, arrayType, Function.Method.Reference, JSDotExpression.New(backingStore, new JSStringIdentifier(arrayMember, arrayType)) ); var indexVariable = new JSVariable( indexVariableName, TypeSystem.Int32, Function.Method.Reference, JSLiteral.New(0) ); var lengthVariable = new JSVariable( lengthVariableName, TypeSystem.Int32, Function.Method.Reference, JSDotExpression.New(backingStore, new JSStringIdentifier(lengthMember, TypeSystem.Int32)) ); var initializer = new JSVariableDeclarationStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, arrayVariable, arrayVariable.DefaultValue, arrayVariable.IdentifierType ), new JSBinaryOperatorExpression( JSOperator.Assignment, indexVariable, indexVariable.DefaultValue, indexVariable.IdentifierType ), new JSBinaryOperatorExpression( JSOperator.Assignment, lengthVariable, lengthVariable.DefaultValue, lengthVariable.IdentifierType ) ); var condition = new JSBinaryOperatorExpression( JSOperator.LessThan, indexVariable, lengthVariable, TypeSystem.Boolean ); var increment = new JSUnaryOperatorExpression( JSOperator.PostIncrement, indexVariable, TypeSystem.Int32 ); var result = new JSForLoop( initializer, condition, new JSExpressionStatement(increment), wl.Statements.ToArray() ); result.Index = wl.Index; new PropertyAccessReplacer( enumerator, new JSProperty(currentPropertyReference, currentPropertyInfo), new JSIndexerExpression( arrayVariable, indexVariable, itemType ) ).Visit(result); return(result); }
protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex, JSBlockStatement enclosingBlock) { var oldDeclaration = statement.Declarations[declarationIndex]; var valueType = oldDeclaration.Right.GetActualType(JSIL.TypeSystem); var newVariable = variable.Reference(); var enclosingFunction = Stack.OfType <JSFunctionExpression>().First(); JSExpression initialValue; // If the declaration was in function scope originally we can hoist the initial value // into our new variable declaration. If not, we need to initialize the ref variable // to the default value for its type. It will get the correct value assigned later. if (enclosingBlock == enclosingFunction.Body) { initialValue = oldDeclaration.Right; } else { initialValue = new JSDefaultValueLiteral(valueType); } var newDeclaration = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use a constructed ref to the variable here, otherwise // the declaration will look like 'var x.value = foo' new JSVariable(variable.Identifier, variable.IdentifierType, variable.Function), JSIL.NewReference(initialValue), newVariable.IdentifierType )); if (Tracing) { Console.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement)); } // Insert the new declaration directly before the top-level block containing the original // declaration. This ensures that if its initial value has a dependency on external state, // the declaration will not precede the values it depends on. // Note that for declarations that were hoisted out of inner blocks (conditionals, loops) // it doesn't actually matter where the insert occurs, since we initialize with a default // value in that case. enclosingFunction.Body.InsertNearChildRecursive( statement, newDeclaration, 0 ); // If the reference is being declared in function scope, it doesn't need a separate assignment // for its initialization. Otherwise, we need to insert an assignment after the original variable // declaration statement to ensure that the reference variable is initialized to the right value // at the exact right point in the function's execution. if (enclosingBlock != enclosingFunction.Body) { var newAssignment = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, newVariable, oldDeclaration.Right, valueType ) ); var insertLocation = enclosingBlock.Statements.IndexOf(statement) + 1; enclosingBlock.Statements.Insert(insertLocation, newAssignment); } Variables[variable.Identifier] = newVariable; statement.Declarations.RemoveAt(declarationIndex); TransformedVariables.Add(variable.Identifier); }