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 JSTryCatchBlock(JSStatement body, JSVariable catchVariable = null, JSStatement @catch = null, JSStatement @finally = null) { Body = body; CatchVariable = catchVariable; Catch = @catch; Finally = @finally; }
protected bool MatchesConstructedReference (JSExpression lhs, JSVariable rhs) { var jsv = lhs as JSVariable; if ((jsv != null) && (jsv.Identifier == rhs.Identifier)) return true; return false; }
public ILBlockTranslator(AssemblyTranslator translator, DecompilerContext context, MethodReference methodReference, MethodDefinition methodDefinition, ILBlock ilb, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> allVariables) { Translator = translator; Context = context; ThisMethodReference = methodReference; ThisMethod = methodDefinition; Block = ilb; SpecialIdentifiers = new JSIL.SpecialIdentifiers(TypeSystem); if (methodReference.HasThis) Variables.Add("this", JSThisParameter.New(methodReference.DeclaringType, methodReference)); foreach (var parameter in parameters) { if ((parameter.Name == "this") && (parameter.OriginalParameter.Index == -1)) continue; ParameterNames.Add(parameter.Name); Variables.Add(parameter.Name, new JSParameter(parameter.Name, parameter.Type, methodReference)); } foreach (var variable in allVariables) { var v = JSVariable.New(variable, methodReference); if (Variables.ContainsKey(v.Identifier)) { v = new JSVariable(variable.OriginalVariable.Name, variable.Type, methodReference); RenamedVariables[variable] = v; Variables.Add(v.Identifier, v); } else { Variables.Add(v.Identifier, v); } } }
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 our parent node is the function, we're in the argument list if (ParentNode is JSFunctionExpression) return; int count; if (ReferenceCounts.TryGetValue(variable.Identifier, out count)) ReferenceCounts[variable.Identifier] = count + 1; else ReferenceCounts[variable.Identifier] = 1; }
public override void ReplaceChild(JSNode oldChild, JSNode newChild) { if (oldChild == null) { throw new ArgumentNullException("oldChild"); } if (CatchVariable == oldChild) { CatchVariable = (JSVariable)newChild; } if (Catch == oldChild) { Catch = (JSStatement)newChild; } if (Finally == oldChild) { Finally = (JSStatement)newChild; } Body.ReplaceChild(oldChild, newChild); }
protected bool IsEffectivelyConstant(JSVariable target, JSExpression source) { if ((source == null) || (source.IsNull)) 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 JSDotExpression; 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 ((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; } // TODO /* var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); if (accesses.Length < 1) return false; var firstAccess = accesses.FirstOrDefault(); var lastAccess = accesses.LastOrDefault(); if (firstAccess == firstAssignment + 1) return true; */ return false; }
public override void ReplaceChild(JSNode oldChild, JSNode newChild) { if (oldChild == null) throw new ArgumentNullException("oldChild"); if (CatchVariable == oldChild) CatchVariable = (JSVariable)newChild; if (Catch == oldChild) Catch = (JSStatement)newChild; if (Finally == oldChild) Finally = (JSStatement)newChild; Body.ReplaceChild(oldChild, newChild); }
public VariableReferenceAccessTransformer(JSILIdentifier jsil, JSVariable variable) { JSIL = jsil; Variable = variable; }
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) Debug.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 VariableReferenceAccessTransformer (JSILIdentifier jsil, JSVariable variable, IFunctionSource functionSource) { JSIL = jsil; Variable = variable; FunctionSource = functionSource; }
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 JSVariableReference(JSVariable referent, MethodReference function) : base(referent.Identifier, null, function) { Referent = referent; }
protected void TransformVariableIntoReference(JSVariable variable, JSVariableDeclarationStatement statement, int declarationIndex) { if (variable.IsReference) Debugger.Break(); var oldDeclaration = statement.Declarations[declarationIndex]; var newVariable = variable.Reference(); var newDeclaration = 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.Type), JSIL.NewReference(oldDeclaration.Right), newVariable.Type ); if (Tracing) Debug.WriteLine(String.Format("Transformed {0} into {1} in {2}", variable, newVariable, statement)); Variables[variable.Identifier] = newVariable; statement.Declarations[declarationIndex] = newDeclaration; TransformedVariables.Add(variable.Identifier); }
public void VisitNode (JSVariable variable) { if (CurrentName == "FunctionSignature") { // In argument list return; } if (Variable.Equals(variable)) { ParentNode.ReplaceChild(variable, Replacement); } else { VisitChildren(variable); } }
protected void TransformParameterIntoReference(JSVariable parameter, JSBlockStatement block) { var newParameter = new JSParameter("$" + parameter.Identifier, parameter.Type); var newVariable = new JSVariable(parameter.Identifier, new ByReferenceType(parameter.Type)); 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.Type ) ); if (Tracing) Debug.WriteLine(String.Format("Transformed {0} into {1}={2}", parameter, newVariable, newParameter)); Variables[newVariable.Identifier] = newVariable; Variables.Add(newParameter.Identifier, newParameter); ParameterNames.Remove(parameter.Identifier); ParameterNames.Add(newParameter.Identifier); block.Statements.Insert(0, newDeclaration); }
public void VisitNode(JSVariable v) { SeenAlready.Add(v); VisitChildren(v); }
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) Debug.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); }
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; }
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); }
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) { { var replacer = new VariableEliminator( variable, JSChangeTypeExpression.New(replaceWith, TypeSystem, variable.GetExpectedType(TypeSystem)) ); replacer.Visit(context); } { var replacer = new VariableEliminator(variable, replaceWith); var assignments = (from a in FirstPass.Assignments where variable.Equals(a.NewValue) || a.NewValue.AllChildrenRecursive.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.StatementIndex, a.NodeIndex, a.Target, replaceWith, a.Operator, a.TargetType, a.SourceType ) ); } else { replacer.Visit(a.NewValue); } } } Variables.Remove(variable.Identifier); }
internal JSFunctionExpression Create ( MethodInfo info, MethodDefinition methodDef, MethodReference method, QualifiedMemberIdentifier identifier, ILBlockTranslator translator, JSVariable[] parameters, JSBlockStatement body ) { var args = new PopulatedCacheEntryArgs { Info = info, Method = method, Translator = translator, Parameters = parameters, Body = body, }; return Cache.GetOrCreate(identifier, args, MakePopulatedCacheEntry).Expression; }
public void VisitNode(JSVariable variable) { if (ParentNode is JSFunctionExpression) { // In argument list return; } if (Variable.Equals(variable)) { ParentNode.ReplaceChild(variable, Replacement); } else { VisitChildren(variable); } }
public JSVariableDereference(JSVariable referent, MethodReference function) : base(referent.Identifier, DeReferenceType(referent.IdentifierType), function) { Referent = referent; }
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( JSBinaryOperator.LessThan, indexVariable, lengthVariable, TypeSystem.Boolean ); var increment = new JSUnaryOperatorExpression( JSUnaryOperator.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; }
public JSVariableReference(JSVariable referent, MethodReference function) : base(referent.Identifier, new ByReferenceType(referent.IdentifierType), function) { Referent = referent; }
public VariableEliminator (JSVariable variable, JSExpression replacement) { Variable = variable; Replacement = replacement; }
protected JSVariable DeclareVariable(JSVariable variable) { JSVariable existing; if (Variables.TryGetValue(variable.Identifier, out existing)) { if (!TypesAreEqual(variable.Type, existing.Type)) throw new InvalidOperationException("A variable with that name is already declared in this scope, with a different type."); if (!variable.DefaultValue.Equals(existing.DefaultValue)) throw new InvalidOperationException("A variable with that name is already declared in this scope, with a different default value."); return existing; } Variables[variable.Identifier] = variable; return variable; }
protected bool IsEffectivelyConstant (JSVariable target, JSExpression source) { if ((source == null) || (source.IsNull)) return false; // HACK: Can't eliminate struct temporaries, since that might eliminate some implied copies. if (TypeUtil.IsStruct(target.IdentifierType)) { // HACK: Allow optimizing out default(T) for structs, // but only if they're referred to once. if (source is JSDefaultValueLiteral) { var numUsages = FirstPass.Accesses.Where(a => a.Source == target.Identifier).Count(); return numUsages <= 1; } else { 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; }
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) ) 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. 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 we didn't find a slot, check to see if all the assignments come before all the accesses. if (!foundAssignmentSlot) { var minAccessIndex = targetAccesses.Min((a) => a.StatementIndex); if (assignments.All((a) => a.StatementIndex < minAccessIndex)) foundAssignmentSlot = true; } if (!foundAssignmentSlot) return false; return true; } return false; }