private bool IsVarEffectivelyConstantHere(JSVariable variable) { if (variable == null) { return(false); } if (variable.IsParameter) { return(false); } // If we're making a copy of a local variable, and this is the last reference to // the variable, we can eliminate the copy. if ( !SecondPass.Data.VariablesPassedByRef.Contains(variable.Identifier) && SecondPass.Data.Accesses .Where(a => a.Source == variable.Identifier) // FIXME: This should probably be NodeIndex but that gets out of sync somehow? Outdated analysis data? .All(a => a.StatementIndex <= StatementIndex) && SecondPass.Data.Assignments .Where(a => a.Target == variable.Identifier) // FIXME: This should probably be NodeIndex but that gets out of sync somehow? Outdated analysis data? .All(a => a.StatementIndex < StatementIndex) ) { return(true); } return(false); }
private IEnumerable <HElement> GetPageContents(SessionData sessionData, string subUrl) { yield return(new HHeadline("Directoy Responses")); if (string.IsNullOrEmpty(subUrl)) { yield return(new HText($"Directoy Responses are called for all request targeting a specific sub-directory like '{URL}/<suburl>' for this {nameof(DirectoryElementResponse)}.")); yield return(new HText($"Enter a suburl to visit:")); // Create a text-field that changes the sub-url, a button should go to on click. JSVariable suburl = new JSVariable(); JSButton button = new JSButton("Go to Sub-URL"); button.onclick = new JScript(JSValue.CurrentBrowserURL.Set(new JSStringValue($"{URL}/") + suburl.Name)); JSInput input = new JSInput(HInput.EInputType.text, "suburl"); input.onchange = new JScript(suburl.Set(input.GetInnerValue())); yield return(new HContainer(new HScript(suburl.GetJsCode(sessionData)), input, button) { Class = "fit" }); } else { yield return(new HHeadline($"Sub-URL '{subUrl}' has been called.", 3)); yield return(new HTextBlock($"You can return to the main-page of this {nameof(DirectoryElementResponse)} by clicking ", new HLink("here", "/" + URL) { Style = "display: initial;" }, ".")); } }
protected void TransformParameterIntoReference(JSVariable parameter, JSBlockStatement block) { var newParameter = new JSParameter("$" + parameter.Identifier, parameter.IdentifierType, parameter.Function); var newVariable = new JSVariable(parameter.Identifier, new ByReferenceType(parameter.IdentifierType), parameter.Function); var newDeclaration = new JSVariableDeclarationStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, // We have to use parameter here, not newVariable or newParameter, otherwise the resulting // assignment looks like 'x.value = initializer' instead of 'x = initializer' parameter, JSIL.NewReference(newParameter), newVariable.IdentifierType ) ); if (Tracing) { Console.WriteLine(String.Format("Transformed {0} into {1}={2}", parameter, newVariable, newParameter)); } Variables[newVariable.Identifier] = newVariable; Variables.Add(newParameter.Identifier, newParameter); var enclosingFunction = Stack.OfType <JSFunctionExpression>().First(); enclosingFunction.Body.Statements.Insert(0, newDeclaration); var oldIndex = Array.IndexOf(enclosingFunction.Parameters, parameter); enclosingFunction.Parameters[oldIndex] = newParameter; }
public SideEffect( int[] parentNodeIndices, int statementIndex, int nodeIndex, JSVariable variable ) : base(parentNodeIndices, statementIndex, nodeIndex) { Variable = variable; }
public Invocation( int[] parentNodeIndices, int statementIndex, int nodeIndex, JSVariable thisVariable, JSMethod method, object nonJSMethod, IDictionary <string, string[]> variables ) : base(parentNodeIndices, statementIndex, nodeIndex) { if (thisVariable != null) { ThisVariable = thisVariable.Identifier; } else { ThisVariable = null; } ThisType = null; Method = method; if (method == null) { NonJSMethod = nonJSMethod; } else { NonJSMethod = null; } Variables = variables; }
protected void EliminateVariable(JSNode context, JSVariable variable, JSExpression replaceWith, QualifiedMemberIdentifier method) { { var replacer = new VariableEliminator( variable, JSChangeTypeExpression.New(replaceWith, variable.GetActualType(TypeSystem), TypeSystem) ); replacer.Visit(context); } { var replacer = new VariableEliminator(variable, replaceWith); var assignments = (from a in FirstPass.Assignments where variable.Equals(a.NewValue) || a.NewValue.SelfAndChildrenRecursive.Any(variable.Equals) select a).ToArray(); foreach (var a in assignments) { if (!variable.Equals(a.NewValue)) { replacer.Visit(a.NewValue); } } } Variables.Remove(variable.Identifier); FunctionSource.InvalidateFirstPass(method); }
public Access( int[] parentNodeIndices, int statementIndex, int nodeIndex, JSVariable source, bool isControlFlow ) : base(parentNodeIndices, statementIndex, nodeIndex) { Source = source; IsControlFlow = isControlFlow; }
public SlotFlags this[JSVariable v] { get { return(this[v.Name]); } set { this[v.Name] = value; } }
public Invocation(int statementIndex, int nodeIndex, JSVariable thisVariable, JSMethod method, IDictionary <string, string[]> variables) : base(statementIndex, nodeIndex) { ThisVariable = thisVariable.Identifier; ThisType = null; Method = method; Variables = variables; }
public void VisitNode(JSVariable variable) { Formatter.WriteSExpr( "get_local", (_) => _.WriteRaw("${0}", WasmUtil.EscapeIdentifier(variable.Name)) ); }
public void VisitNode(JSVariable variable) { if (variable.IsThis) { if (ThisReplacementStack.Count > 0) { var thisRef = ThisReplacementStack.Peek(); if (thisRef != null) { Visit(thisRef); } return; } else { Output.WriteRaw("this"); } } else { Output.Identifier(variable.Identifier); } // Don't emit .value when initializing a reference in a declaration. var boe = ParentNode as JSBinaryOperatorExpression; if ( (boe != null) && (boe.Left == variable) && (Stack.Skip(2).FirstOrDefault() is JSVariableDeclarationStatement) ) { return; } if (variable.IsReference) { if (variable.IsThis) { if (JSExpression.DeReferenceType(variable.Type).IsValueType) { return; } else { throw new InvalidOperationException(String.Format( "The this-reference '{0}' was a reference to a non-value type: {1}", variable, variable.Type )); } } Output.Dot(); Output.Identifier("value"); } }
protected void ModifiedVariable(JSVariable variable) { if (!State.ModificationCount.ContainsKey(variable.Name)) { State.ModificationCount[variable.Name] = 1; } else { State.ModificationCount[variable.Name] += 1; } }
protected bool MatchesConstructedReference(JSExpression lhs, JSVariable rhs) { var jsv = lhs as JSVariable; if ((jsv != null) && (jsv.Identifier == rhs.Identifier)) { return(true); } return(false); }
protected void AddToList <T> (Dictionary <JSVariable, List <T> > dict, JSVariable variable, T index) { List <T> list; if (!dict.TryGetValue(variable, out list)) { dict[variable] = list = new List <T>(); } list.Add(index); }
public Assignment( int[] parentNodeIndices, int statementIndex, int nodeIndex, JSVariable target, JSExpression newValue, JSOperator @operator, TypeReference targetType, TypeReference sourceType ) : base(parentNodeIndices, statementIndex, nodeIndex) { Target = target; NewValue = newValue; SourceVariable = newValue as JSVariable; SourceType = sourceType; TargetType = targetType; Operator = @operator; IsConversion = !TypeUtil.TypesAreEqual(targetType, sourceType); }
public void VisitNode(JSVariable variable) { int count; if (ReferenceCounts.TryGetValue(variable.Identifier, out count)) { ReferenceCounts[variable.Identifier] = count + 1; } else { ReferenceCounts[variable.Identifier] = 1; } VisitChildren(variable); }
public void VisitNode(JSVariable variable) { if (CurrentName == "FunctionSignature") { // In argument list return; } if (Variable.Equals(variable)) { ParentNode.ReplaceChild(variable, Replacement); } else { VisitChildren(variable); } }
private JSVariable MakeTemporaryVariable(TypeReference type, out string id, JSExpression defaultValue = null) { string _id = id = string.Format("$hoisted{0:X2}", HoistedVariableCount++); MethodReference methodRef = null; if (Function.Method != null) { methodRef = Function.Method.Reference; } // So introspection knows about it var result = new JSVariable(id, type, methodRef); Function.AllVariables.Add(_id, result); ToDeclare.Add(new PendingDeclaration(id, type, result, defaultValue)); return(result); }
protected void EliminateVariable(JSNode context, JSVariable variable, JSExpression replaceWith, QualifiedMemberIdentifier method) { { var replacer = new VariableEliminator( variable, JSChangeTypeExpression.New(replaceWith, TypeSystem, variable.GetActualType(TypeSystem)) ); replacer.Visit(context); } { var replacer = new VariableEliminator(variable, replaceWith); var assignments = (from a in FirstPass.Assignments where variable.Equals(a.NewValue) || a.NewValue.SelfAndChildrenRecursive.Any((_n) => variable.Equals(_n)) select a).ToArray(); foreach (var a in assignments) { if (variable.Equals(a.NewValue)) { FirstPass.Assignments.Remove(a); FirstPass.Assignments.Add( new FunctionAnalysis1stPass.Assignment( a.ParentNodeIndices, a.StatementIndex, a.NodeIndex, a.Target, replaceWith, a.Operator, a.TargetType, a.SourceType ) ); } else { replacer.Visit(a.NewValue); } } } Variables.Remove(variable.Identifier); FunctionSource.InvalidateFirstPass(method); }
public void VisitNode(JSVariable variable) { if ( (ParentNode is JSFunctionExpression) && (this.CurrentName == "FunctionSignature") ) { VisitChildren(variable); return; } if ( (variable.Identifier != Variable.Identifier) || // Don't transform if we're inside a read-through already (ParentNode is JSReadThroughReferenceExpression) || ( // If we're inside a write-through and on the LHS, don't transform (ParentNode is JSWriteThroughReferenceExpression) && (this.CurrentName == "Left") ) ) { VisitChildren(variable); return; } // If we're inside a pass-by-reference (ref x) then don't transform if ( Stack.OfType <JSPassByReferenceExpression>().Any() ) { VisitChildren(variable); return; } var replacement = new JSReadThroughReferenceExpression(variable); ParentNode.ReplaceChild(variable, replacement); VisitReplacement(replacement); }
public void VisitNode(JSWhileLoop whileLoop) { JSVariable initVariable = null, lastVariable = null; JSBinaryOperator initOperator = null; JSExpression initValue = null; var prevEStmt = PreviousSibling as JSExpressionStatement; var prevVDS = PreviousSibling as JSVariableDeclarationStatement; if (prevEStmt != null) { var boe = prevEStmt.Expression as JSBinaryOperatorExpression; if ( (boe != null) && (boe.Operator is JSAssignmentOperator) && (boe.Left is JSVariable) ) { initVariable = (JSVariable)boe.Left; initOperator = boe.Operator; initValue = boe.Right; } } else if (prevVDS != null) { var decl = prevVDS.Declarations.FirstOrDefault( (d) => !d.IsNull ); if (decl != null) { initVariable = (JSVariable)decl.Left; initOperator = decl.Operator; initValue = decl.Right; } } var lastStatement = whileLoop.Statements.LastOrDefault(); while ((lastStatement != null) && (lastStatement.GetType() == typeof(JSBlockStatement))) { lastStatement = ((JSBlockStatement)lastStatement).Statements.LastOrDefault(); } var lastExpressionStatement = lastStatement as JSExpressionStatement; if (lastExpressionStatement != null) { var lastUoe = lastExpressionStatement.Expression as JSUnaryOperatorExpression; var lastBoe = lastExpressionStatement.Expression as JSBinaryOperatorExpression; if ((lastUoe != null) && (lastUoe.Operator is JSUnaryMutationOperator)) { lastVariable = lastUoe.Expression as JSVariable; } else if ((lastBoe != null) && (lastBoe.Operator is JSAssignmentOperator)) { lastVariable = lastBoe.Left as JSVariable; if ( (lastVariable != null) && !lastBoe.Right.SelfAndChildrenRecursive.Any( (n) => lastVariable.Equals(n) ) ) { lastVariable = null; } } } var lastIfStatement = lastStatement as JSIfStatement; if ( (lastIfStatement != null) && whileLoop.Condition is JSBooleanLiteral && ((JSBooleanLiteral)whileLoop.Condition).Value ) { var innerStatement = lastIfStatement.TrueClause; while (innerStatement is JSBlockStatement) { var bs = (JSBlockStatement)innerStatement; if (bs.Statements.Count != 1) { innerStatement = null; break; } innerStatement = bs.Statements[0]; } var eStmt = innerStatement as JSExpressionStatement; if (eStmt != null) { var breakExpr = eStmt.Expression as JSBreakExpression; if ((breakExpr != null) && (breakExpr.TargetLoop == whileLoop.Index)) { whileLoop.ReplaceChildRecursive(lastIfStatement, new JSNullStatement()); var doLoop = new JSDoLoop( new JSUnaryOperatorExpression(JSOperator.LogicalNot, lastIfStatement.Condition, TypeSystem.Boolean), whileLoop.Statements.ToArray() ); doLoop.Index = whileLoop.Index; ParentNode.ReplaceChild(whileLoop, doLoop); VisitChildren(doLoop); return; } } } bool cantBeFor = false; if ((initVariable != null) && (lastVariable != null) && !initVariable.Equals(lastVariable) ) { cantBeFor = true; } else if ((initVariable ?? lastVariable) == null) { cantBeFor = true; } else if (!whileLoop.Condition.SelfAndChildrenRecursive.Any( (n) => (initVariable ?? lastVariable).Equals(n) )) { cantBeFor = true; } else if ( !PostSwitchTransform && ( (lastStatement is JSSwitchStatement) || (lastStatement is JSLabelGroupStatement) ) ) { cantBeFor = true; } if (!cantBeFor) { JSStatement initializer = null, increment = null; if (initVariable != null) { initializer = PreviousSibling as JSStatement; ParentNode.ReplaceChild(PreviousSibling, new JSNullStatement()); } if (lastVariable != null) { increment = lastExpressionStatement; whileLoop.ReplaceChildRecursive(lastExpressionStatement, new JSNullStatement()); } var forLoop = new JSForLoop( initializer, whileLoop.Condition, increment, whileLoop.Statements.ToArray() ); forLoop.Index = whileLoop.Index; ParentNode.ReplaceChild(whileLoop, forLoop); VisitChildren(forLoop); } else { VisitChildren(whileLoop); } }
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); }
private void EmitFieldIntrinsics(int heapSize) { // FIXME: Gross var tis = (ITypeInfoSource)Translator.TypeInfoProvider; Formatter.WriteRaw(";; Compiler-generated field accessors"); Formatter.NewLine(); foreach (var kvp in FieldTable.OrderBy(kvp => kvp.Value.Offset)) { var fd = kvp.Value.Field; var fi = (FieldInfo)tis.Get(fd); var name = WasmUtil.FormatMemberName(fi.Member); var typeSystem = fd.FieldType.Module.TypeSystem; // HACK var baseAddressParam = new JSVariable("address", typeSystem.Int32, null); // HACK var valueParam = new JSVariable("value", fd.FieldType, null); JSExpression address; if (fd.IsStatic) { address = JSLiteral.New(kvp.Value.Offset + heapSize); } else { address = new JSBinaryOperatorExpression( JSOperator.Add, baseAddressParam, JSLiteral.New(kvp.Value.Offset), typeSystem.Int32 ); } Formatter.ConditionalNewLine(); Formatter.WriteRaw( "(func $__get_{0} (result {1}){2}(return ", name, WasmUtil.PickTypeKeyword(fd.FieldType), fd.IsStatic ? " " : " (param $address i32) " ); var gm = new GetMemory( fd.FieldType, /* FIXME: Align addresses */ false, address ); // HACK EntryPointAstEmitter.Emit(gm); Formatter.WriteRaw(") )"); if (fd.IsInitOnly) { continue; } Formatter.NewLine(); Formatter.WriteRaw( "(func $__set_{0}{2}(param $value {1}) ", name, WasmUtil.PickTypeKeyword(fd.FieldType), fd.IsStatic ? " " : " (param $address i32) " ); Formatter.Indent(); Formatter.NewLine(); var sm = new SetMemory( fd.FieldType, /* FIXME: Align addresses */ false, address, valueParam ); // HACK EntryPointAstEmitter.Emit(sm); Formatter.Unindent(); Formatter.ConditionalNewLine(); Formatter.WriteRaw(")"); } Formatter.NewLine(); Formatter.NewLine(); }
public VariableReferenceAccessTransformer(JSILIdentifier jsil, JSVariable variable) { JSIL = jsil; Variable = variable; }
public void VisitNode(JSVariable v) { SeenAlready.Add(v); VisitChildren(v); }
public void VisitNode(JSVariable variable) { if (CurrentName == "FunctionSignature") { // In argument list VisitChildren(variable); return; } var enclosingStatement = GetEnclosingNodes <JSStatement>().FirstOrDefault(); var enclosingAssignmentStatements = GetEnclosingNodes <JSExpressionStatement>( (es) => { var boe = es.Expression as JSBinaryOperatorExpression; if (boe == null) { return(false); } var isAssignment = boe.Operator == JSOperator.Assignment; var leftIsVariable = boe.Left is JSVariable; return(isAssignment && leftIsVariable && (boe.Left.Equals(variable) || boe.Right.Equals(variable))); } ).ToArray(); if ((enclosingAssignmentStatements.Length == 0) && ( !(enclosingStatement.Node is JSVariableDeclarationStatement) )) { bool isControlFlow = ( (enclosingStatement.Node is JSIfStatement) && (enclosingStatement.ChildName != "Condition") ) || ( (enclosingStatement.Node is JSSwitchStatement) && (enclosingStatement.ChildName != "Condition") ); // Don't do the condition check here since a loop's condition can be evaluated multiple times var enclosingBlock = enclosingStatement.Node as JSBlockStatement; if (enclosingBlock != null) { isControlFlow |= enclosingBlock is JSLoopStatement; } State.Accesses.Add( new FunctionAnalysis1stPass.Access( GetParentNodeIndices(), StatementIndex, NodeIndex, variable, isControlFlow ) ); } else { // Ignored because it is not an actual access } if ( (ParentNode is JSPassByReferenceExpression) || ( (ParentNode is JSReferenceExpression) && (Stack.Skip(2).FirstOrDefault() is JSPassByReferenceExpression) ) ) { State.VariablesPassedByRef.Add(variable.Name); } VisitChildren(variable); }
public VariableEliminator(JSVariable variable, JSExpression replacement) { Variable = variable; Replacement = replacement; }
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); }
protected bool IsEffectivelyConstant(JSVariable target, JSExpression source) { if ((source == null) || (source.IsNull)) { return(false); } // Can't eliminate struct temporaries, since that might eliminate some implied copies. if (TypeUtil.IsStruct(target.Type)) { return(false); } // Handle special cases where our interpretation of 'constant' needs to be more flexible { var ie = source as JSIndexerExpression; if (ie != null) { if ( IsEffectivelyConstant(target, ie.Target) && IsEffectivelyConstant(target, ie.Index) ) { return(true); } } } { var ae = source as JSArrayExpression; if ( (ae != null) && (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b) ) { return(true); } } { var de = source as JSDotExpressionBase; if ( (de != null) && IsEffectivelyConstant(target, de.Target) && IsEffectivelyConstant(target, de.Member) ) { return(true); } } { var ie = source as JSInvocationExpression; if ( (ie != null) && ie.ConstantIfArgumentsAre && IsEffectivelyConstant(target, ie.ThisReference) && ie.Arguments.All((a) => IsEffectivelyConstant(target, a)) ) { return(true); } if ((ie != null) && (ie.JSMethod != null)) { var sa = FunctionSource.GetSecondPass(ie.JSMethod); if (sa != null) { if (sa.IsPure) { if (ie.Arguments.All((a) => IsEffectivelyConstant(target, a))) { return(true); } else { return(false); } } } } } if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression)) { if (source.Children.OfType <JSExpression>().All((_v) => IsEffectivelyConstant(target, _v))) { return(true); } } if (source.IsConstant) { return(true); } // Try to find a spot between the source variable's assignments where all of our // copies and accesses can fit. If we find one, our variable is effectively constant. var v = source as JSVariable; if (v != null) { var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); if (assignments.Length < 1) { return(v.IsParameter); } var targetAssignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); if (targetAssignments.Length < 1) { return(false); } var targetAccesses = (from a in FirstPass.Accesses where target.Equals(a.Source) select a).ToArray(); if (targetAccesses.Length < 1) { return(false); } var targetFirstAssigned = targetAssignments.FirstOrDefault(); var targetLastAssigned = targetAssignments.LastOrDefault(); var targetFirstAccessed = targetAccesses.FirstOrDefault(); var targetLastAccessed = targetAccesses.LastOrDefault(); bool foundAssignmentSlot = false; for (int i = 0, c = assignments.Length; i < c; i++) { int assignment = assignments[i].StatementIndex, nextAssignment = int.MaxValue; if (i < c - 1) { nextAssignment = assignments[i + 1].StatementIndex; } if ( (targetFirstAssigned.StatementIndex >= assignment) && (targetFirstAssigned.StatementIndex < nextAssignment) && (targetFirstAccessed.StatementIndex >= assignment) && (targetLastAccessed.StatementIndex <= nextAssignment) ) { foundAssignmentSlot = true; break; } } if (!foundAssignmentSlot) { return(false); } return(true); } return(false); }
protected bool IsEffectivelyConstant(JSVariable target, JSExpression source) { if ((source == null) || (source.IsNull)) { return(false); } // Can't eliminate struct temporaries, since that might eliminate some implied copies. if (TypeUtil.IsStruct(target.IdentifierType)) { return(false); } // Handle special cases where our interpretation of 'constant' needs to be more flexible { var ie = source as JSIndexerExpression; if (ie != null) { if ( IsEffectivelyConstant(target, ie.Target) && IsEffectivelyConstant(target, ie.Index) ) { return(true); } } } { var ae = source as JSArrayExpression; if ( (ae != null) && (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b) ) { return(true); } } { var de = source as JSDotExpressionBase; if ( (de != null) && IsEffectivelyConstant(target, de.Target) && IsEffectivelyConstant(target, de.Member) ) { var pa = source as JSPropertyAccess; if (pa != null) { // Property accesses must not be treated as constant since they call functions // TODO: Use static analysis information to figure out whether the accessor is pure/has state dependencies return(false); } return(true); } } { var ie = source as JSInvocationExpression; if ( (ie != null) && ie.ConstantIfArgumentsAre && IsEffectivelyConstant(target, ie.ThisReference) && ie.Arguments.All((a) => IsEffectivelyConstant(target, a)) ) { return(true); } if ((ie != null) && (ie.JSMethod != null)) { var sa = GetSecondPass(ie.JSMethod); if (sa != null) { if (sa.IsPure) { if (ie.Arguments.All((a) => IsEffectivelyConstant(target, a))) { return(true); } else { return(false); } } } } } if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression)) { if (source.Children.OfType <JSExpression>().All((_v) => IsEffectivelyConstant(target, _v))) { return(true); } } if (source.IsConstant) { return(true); } // Try to find a spot between the source variable's assignments where all of our // copies and accesses can fit. If we find one, our variable is effectively constant. // FIXME: I think this section might be fundamentally flawed. Do let me know if you agree. :) var v = source as JSVariable; if (v != null) { // Ensure that we never treat a local variable as constant if functions we call allow it to escape // or modify it, because that can completely invalidate our purity analysis. if (VariablesExemptedFromEffectivelyConstantStatus.Contains(v.Identifier)) { return(false); } var sourceAssignments = (from a in FirstPass.Assignments where v.Identifier.Equals(a.Target) select a).ToArray(); if (sourceAssignments.Length < 1) { return(v.IsParameter); } var sourceAccesses = (from a in FirstPass.Accesses where v.Identifier.Equals(a.Source) select a).ToArray(); if (sourceAccesses.Length < 1) { return(false); } var targetAssignmentIndices = (from a in FirstPass.Assignments where target.Identifier.Equals(a.Target) select a.StatementIndex); var targetAccessIndices = (from a in FirstPass.Accesses where target.Identifier.Equals(a.Source) select a.StatementIndex).ToArray(); var targetUseIndices = targetAccessIndices.Concat(targetAssignmentIndices).ToArray(); if (sourceAssignments.Length == 1) { if (targetAccessIndices.All((tai) => tai > sourceAssignments[0].StatementIndex)) { return(true); } } var sourceFirstAssigned = sourceAssignments.First(); var sourceLastAssigned = sourceAssignments.Last(); var sourceFirstAccessed = sourceAccesses.First(); var sourceLastAccessed = sourceAccesses.Last(); bool foundAssignmentSlot = false; for (int i = 0, c = targetUseIndices.Length; i < c; i++) { int assignment = targetUseIndices[i], nextAssignment = int.MaxValue; if (i < c - 1) { nextAssignment = targetUseIndices[i + 1]; } if ( (sourceFirstAssigned.StatementIndex >= assignment) && (sourceLastAssigned.StatementIndex < nextAssignment) && (sourceFirstAccessed.StatementIndex >= assignment) && (sourceLastAccessed.StatementIndex <= nextAssignment) ) { if (TraceLevel >= 5) { Console.WriteLine("Found assignment slot for {0} <- {1} between {2} and {3}", target, source, assignment, nextAssignment); } foundAssignmentSlot = true; break; } } if (!foundAssignmentSlot) { return(false); } return(true); } return(false); }