public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes InvalidateFirstPass(); FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; foreach (var pd in ToDeclare) { var es = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, pd.DefaultValue ?? new JSDefaultValueLiteral(pd.Type), pd.Type )); fn.Body.Statements.Insert(i++, es); } InvalidateFirstPass(); } }
public static JSTemporaryVariable ForFunction( JSFunctionExpression function, TypeReference type, IFunctionSource functionSource ) { var index = function.TemporaryVariableTypes.Count; function.TemporaryVariableTypes.Add(type); MethodReference methodRef = null; if (function.Method != null) { methodRef = function.Method.Reference; } var id = string.Format("$temp{0:X2}", index); var result = new JSTemporaryVariable(id, type, methodRef); function.AllVariables.Add(id, result); // HACK: If the static analysis data for the function is stale, this temporary // variable might get eliminated later despite being in use. // We should really just fix all the transforms that aren't invalidating static // analysis data when they should, but this is good enough for now. if (function.Method != null) { functionSource.InvalidateFirstPass(function.Method.QualifiedIdentifier); } return(result); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { new IntroduceVariableReferences(JSIL, fn.AllVariables, new HashSet <string>(from p in fn.Parameters select p.Name)).Visit(fn); return; } VisitChildren(fn); foreach (var r in ReferencesToTransform) { var cr = GetConstructedReference(r); if (cr == null) { // We have already done the variable transform for this variable in the past. continue; } // For 'ref this', we need to replace each individual expression, because we can't // rename the this-variable. if (cr.IsThis) { var refThis = JSIL.NewReference(Variables["this"]); fn.ReplaceChildRecursive(r, refThis); continue; } var parameter = (from p in fn.Parameters where p.Identifier == cr.Identifier select p).FirstOrDefault(); if (parameter != null) { TransformParameterIntoReference(parameter, fn.Body); } else { var declaration = (from kvp in Declarations let vds = kvp.Key from ivd in vds.Declarations.Select((vd, i) => new { vd = vd, i = i }) where MatchesConstructedReference(ivd.vd.Left, cr) select new { vds = vds, vd = ivd.vd, i = ivd.i, block = kvp.Value }).FirstOrDefault(); if (declaration == null) { throw new InvalidOperationException(String.Format("Could not locate declaration for {0}", cr)); } TransformVariableIntoReference( (JSVariable)declaration.vd.Left, declaration.vds, declaration.i, declaration.block ); } } }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); }
internal JSFunctionExpression Create( MethodInfo info, MethodDefinition methodDef, MethodReference method, QualifiedMemberIdentifier identifier, ILBlockTranslator translator, IEnumerable <JSVariable> parameters, JSBlockStatement body ) { return(Cache.GetOrCreate(identifier, () => { var result = new JSFunctionExpression( new JSMethod(method, info, MethodTypes), translator.Variables, parameters, body, MethodTypes ); OptimizationQueue.TryEnqueue(identifier); return new Entry { Identifier = identifier, Info = info, Reference = method, Expression = result, Variables = translator.Variables, ParameterNames = translator.ParameterNames, SpecialIdentifiers = translator.SpecialIdentifiers }; }).Expression); }
public void VisitNode (JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; var vds = new JSVariableDeclarationStatement(); foreach (var pd in ToDeclare) { vds.Declarations.Add( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, pd.DefaultValue ?? new JSDefaultValueLiteral(pd.Type), pd.Type ) ); } fn.Body.Statements.Insert(i++, vds); InvalidateFirstPass(); } }
/// <summary> /// Writes a method signature to the output. /// </summary> public void WriteSignatureToOutput( JavascriptFormatter output, JSFunctionExpression enclosingFunction, MethodReference methodReference, MethodSignature methodSignature, TypeReferenceContext referenceContext, bool forConstructor ) { int index; var record = new CachedSignatureRecord(methodReference, methodSignature, forConstructor); if ((enclosingFunction.Method != null) && (enclosingFunction.Method.Method != null)) { var functionIdentifier = enclosingFunction.Method.Method.Identifier; CacheSet localSignatureSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSignatureSet)) { if (localSignatureSet.Signatures.TryGetValue(record, out index)) { output.WriteRaw("$s{0:X2}", index); return; } } } if (!Global.Signatures.TryGetValue(record, out index)) { output.Signature(methodReference, methodSignature, referenceContext, forConstructor, true); } else { output.WriteRaw("$S{0:X2}()", index); } }
public void VisitNode(JSFunctionExpression node) { if (node.Body != null) { VisitNodeInternalDown(node.Body); VisitNodeInternalUp(node.Body, null); } }
public static JSRawOutputIdentifier ForFunction( JSFunctionExpression function, TypeReference type ) { return(new JSRawOutputIdentifier( type, "$temp{0:X2}", function.TemporaryVariableCount++ )); }
/// <summary> /// Writes an interface member reference to the output. /// </summary> public void WriteInterfaceMemberToOutput( JavascriptFormatter output, Compiler.Extensibility.IAstEmitter astEmitter, JSFunctionExpression enclosingFunction, JSMethod jsMethod, JSExpression method, TypeReferenceContext referenceContext ) { int index; CachedInterfaceMemberRecord record; GenericParameter[] rewrittenGenericParameters = null; if (LocalCachingEnabled && PreferLocalCacheForGenericInterfaceMethodSignatures) { record = new CachedInterfaceMemberRecord(jsMethod.Reference.DeclaringType, jsMethod.Identifier); } else { var rewritten = GenericTypesRewriter.Normalized(jsMethod.Reference.DeclaringType); record = new CachedInterfaceMemberRecord(rewritten.CacheRecord, jsMethod.Identifier, rewritten.RewritedGenericParameters.Length); rewrittenGenericParameters = rewritten.RewritedGenericParameters; } if (enclosingFunction.Method != null && enclosingFunction.Method.Method != null) { var functionIdentifier = enclosingFunction.Method.Method.Identifier; CacheSet localSignatureSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSignatureSet)) { if (localSignatureSet.InterfaceMembers.TryGetValue(record, out index)) { output.WriteRaw("$im{0:X2}", index); return; } } } if (!Global.InterfaceMembers.TryGetValue(record, out index)) { output.Identifier(jsMethod.Reference.DeclaringType, referenceContext, false); output.Dot(); astEmitter.Emit(method); } else { output.WriteRaw("$IM{0:X2}", index); output.LPar(); if (rewrittenGenericParameters != null) { output.CommaSeparatedList(rewrittenGenericParameters, referenceContext); } output.RPar(); } }
/// <summary> /// Writes a method signature to the output. /// </summary> public void WriteSignatureToOutput( JavascriptFormatter output, JSFunctionExpression enclosingFunction, MethodReference methodReference, MethodSignature methodSignature, TypeReferenceContext referenceContext, bool forConstructor ) { int index; CachedSignatureRecord cacheRecord; GenericParameter[] rewrittenGenericParameters = null; if (LocalCachingEnabled && PreferLocalCacheForGenericMethodSignatures) { cacheRecord = new CachedSignatureRecord(methodReference, GenericTypesRewriter.NormalizedConstructorSignature(methodReference, methodSignature, forConstructor), forConstructor); } else { RewritedCacheRecord <MethodSignature> rewritten = GenericTypesRewriter.Normalized(methodReference, methodSignature, forConstructor); cacheRecord = new CachedSignatureRecord(methodReference, rewritten.CacheRecord, forConstructor, rewritten.RewritedGenericParameters.Length); rewrittenGenericParameters = rewritten.RewritedGenericParameters; } if ((enclosingFunction.Method != null) && (enclosingFunction.Method.Method != null)) { var functionIdentifier = enclosingFunction.Method.Method.Identifier; CacheSet localSignatureSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSignatureSet)) { if (localSignatureSet.Signatures.TryGetValue(cacheRecord, out index)) { output.WriteRaw("$s{0:X2}", index); return; } } } if (!Global.Signatures.TryGetValue(cacheRecord, out index)) { output.Signature(methodReference, methodSignature, referenceContext, forConstructor, true); } else { output.WriteRaw("$S{0:X2}", index); output.LPar(); if (rewrittenGenericParameters != null) { output.CommaSeparatedList(rewrittenGenericParameters, referenceContext); } output.RPar(); } }
public FunctionCache(ITypeInfoSource typeInfo) { TypeInfo = typeInfo; Comparer = new QualifiedMemberIdentifier.Comparer(typeInfo); Cache = new ConcurrentCache <QualifiedMemberIdentifier, Entry>( Environment.ProcessorCount, 4096, Comparer ); PendingTransformsQueue = new ConcurrentHashQueue <QualifiedMemberIdentifier>( Math.Max(1, Environment.ProcessorCount / 4), 4096, Comparer ); ActiveTransformPipelines = new ConcurrentDictionary <QualifiedMemberIdentifier, FunctionTransformPipeline>( Math.Max(1, Environment.ProcessorCount / 4), 128, Comparer ); MethodTypes = new MethodTypeFactory(); MakeCacheEntry = (id, method) => { PendingTransformsQueue.TryEnqueue(id); return(new Entry(id, Locks) { Info = method.Method, Reference = method.Reference, SecondPass = new FunctionAnalysis2ndPass(this, method.Method) }); }; MakePopulatedCacheEntry = (id, args) => { var result = new JSFunctionExpression( new JSMethod(args.Method, args.Info, MethodTypes), args.Translator.Variables, args.Parameters, args.Body, MethodTypes ); PendingTransformsQueue.TryEnqueue(id); return(new Entry(id, Locks) { Info = args.Info, Reference = args.Method, Expression = result, SpecialIdentifiers = args.Translator.SpecialIdentifiers }); }; MakeNullCacheEntry = (id, args) => { return(new Entry(id, Locks) { Info = args.Info, Reference = args.Method, Expression = null }); }; }
public static JSRawOutputIdentifier ForFunction( JSFunctionExpression function, TypeReference type ) { var id = String.Format("$temp{0:X2}", function.TemporaryVariableCount++); return(new JSRawOutputIdentifier( (jsf) => jsf.WriteRaw(id), type )); }
public FunctionCache(ITypeInfoSource typeInfo) { var comparer = new QualifiedMemberIdentifier.Comparer(typeInfo); Cache = new ConcurrentCache <QualifiedMemberIdentifier, Entry>( Environment.ProcessorCount, 4096, comparer ); OptimizationQueue = new ConcurrentHashQueue <QualifiedMemberIdentifier>( Math.Max(1, Environment.ProcessorCount / 4), 4096, comparer ); MethodTypes = new MethodTypeFactory(); MakeCacheEntry = (id, method) => { OptimizationQueue.TryEnqueue(id); return(new Entry { Info = method.Method, Reference = method.Reference, Identifier = id, ParameterNames = new HashSet <string>(from p in method.Method.Parameters select p.Name), SecondPass = new FunctionAnalysis2ndPass(this, method.Method) }); }; MakePopulatedCacheEntry = (id, args) => { var result = new JSFunctionExpression( new JSMethod(args.Method, args.Info, MethodTypes), args.Translator.Variables, args.Parameters, args.Body, MethodTypes ); OptimizationQueue.TryEnqueue(id); return(new Entry { Identifier = id, Info = args.Info, Reference = args.Method, Expression = result, Variables = args.Translator.Variables, ParameterNames = args.Translator.ParameterNames, SpecialIdentifiers = args.Translator.SpecialIdentifiers }); }; MakeNullCacheEntry = (id, args) => { return(new Entry { Identifier = id, Info = args.Info, Reference = args.Method, Expression = null }); }; }
public void BuildLabelGroups(JSFunctionExpression function) { // If a label is applied to the first statement in a block, hoist it upward // onto the parent block. var lh = new LabelHoister(); do { lh.HoistedALabel = false; lh.Visit(function); } while (lh.HoistedALabel); // Walk the function to build our list of labels and gotos. Visit(function); // When a goto crosses block boundaries, we need to move the target label // upwards so that the goto can reach it. foreach (var g in Gotos) { var targetLabel = Labels[g.TargetLabel]; if (targetLabel.EnclosingBlock.Depth > g.EnclosingBlock.Depth) { targetLabel.EnclosingBlock = g.EnclosingBlock; } } foreach (var l in Labels.Values) { l.EnsureLabelGroupExists(LabelGroups); var replacementGoto = new JSExpressionStatement( new JSGotoExpression(l.LabelledStatement.Label) ); l.EnclosingBlock.Block.ReplaceChildRecursive(l.LabelledStatement, replacementGoto); l.LabelGroup.Add(l.LabelledStatement); } // If a label group only contains one label (plus an entry label), // and it has a parent label group, hoist the label up. var lgs = new LabelGroupFlattener(); do { lgs.FlattenedAGroup = false; lgs.Visit(function); } while (lgs.FlattenedAGroup); // Remove any labels within a label group that contain no statements (as long // as no goto targets that label directly). This will prune empty entry/exit labels. var elr = new EmptyLabelRemover(UsedLabels); elr.Visit(function); }
public bool TryGetExpression(QualifiedMemberIdentifier method, out JSFunctionExpression function) { Entry entry; if (!Cache.TryGetValue(method, out entry)) { function = null; return false; } function = entry.Expression; return true; }
public FunctionCache(ITypeInfoSource typeInfo) { var comparer = new QualifiedMemberIdentifier.Comparer(typeInfo); Cache = new ConcurrentCache<QualifiedMemberIdentifier, Entry>( Environment.ProcessorCount, 4096, comparer ); OptimizationQueue = new ConcurrentHashQueue<QualifiedMemberIdentifier>( Math.Max(1, Environment.ProcessorCount / 4), 4096, comparer ); MethodTypes = new MethodTypeFactory(); MakeCacheEntry = (id, method) => { OptimizationQueue.TryEnqueue(id); return new Entry { Info = method.Method, Reference = method.Reference, Identifier = id, ParameterNames = new HashSet<string>(from p in method.Method.Parameters select p.Name), SecondPass = new FunctionAnalysis2ndPass(this, method.Method) }; }; MakePopulatedCacheEntry = (id, args) => { var result = new JSFunctionExpression( new JSMethod(args.Method, args.Info, MethodTypes), args.Translator.Variables, args.Parameters, args.Body, MethodTypes ); OptimizationQueue.TryEnqueue(id); return new Entry { Identifier = id, Info = args.Info, Reference = args.Method, Expression = result, Variables = args.Translator.Variables, ParameterNames = args.Translator.ParameterNames, SpecialIdentifiers = args.Translator.SpecialIdentifiers }; }; MakeNullCacheEntry = (id, args) => { return new Entry { Identifier = id, Info = args.Info, Reference = args.Method, Expression = null }; }; }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new EliminatePointlessFinallyBlocks(TypeSystem, TypeInfo, FunctionSource); nested.Visit(fn); return; } VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new EliminatePointlessFinallyBlocks(TypeSystem, TypeInfo, FunctionSource); nested.Visit(fn); return; } VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new OptimizeArrayEnumerators(TypeSystem, FunctionSource); nested.Visit(fn); return; } Function = fn; VisitChildren(fn); }
public bool TryGetExpression(QualifiedMemberIdentifier method, out JSFunctionExpression function) { Entry entry; if (!Cache.TryGet(method, out entry)) { function = null; return(false); } function = entry.Expression; return(true); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { new IntroduceVariableReferences(JSIL, fn.AllVariables, new HashSet<string>(from p in fn.Parameters select p.Name)).Visit(fn); return; } VisitChildren(fn); foreach (var r in ReferencesToTransform) { var cr = GetConstructedReference(r); if (cr == null) { // We have already done the variable transform for this variable in the past. continue; } // For 'ref this', we need to replace each individual expression, because we can't // rename the this-variable. if (cr.IsThis) { var refThis = JSIL.NewReference(Variables["this"]); fn.ReplaceChildRecursive(r, refThis); continue; } var parameter = (from p in fn.Parameters where p.Identifier == cr.Identifier select p).FirstOrDefault(); if (parameter != null) { TransformParameterIntoReference(parameter, fn.Body); } else { var declaration = (from kvp in Declarations let vds = kvp.Key from ivd in vds.Declarations.Select((vd, i) => new { vd = vd, i = i }) where MatchesConstructedReference(ivd.vd.Left, cr) select new { vds = vds, vd = ivd.vd, i = ivd.i, block = kvp.Value }).FirstOrDefault(); if (declaration == null) throw new InvalidOperationException(String.Format("Could not locate declaration for {0}", cr)); TransformVariableIntoReference( (JSVariable)declaration.vd.Left, declaration.vds, declaration.i, declaration.block ); } } }
public void VisitNode(JSFunctionExpression fe) { BlockStack.Push(new EnclosingBlockEntry { Block = fe.Body, ParentNode = fe, Depth = BlockStack.Count }); try { VisitChildren(fe); } finally { BlockStack.Pop(); } }
public FunctionAnalysis1stPass FirstPass(JSFunctionExpression function) { State = new FunctionAnalysis1stPass(function); Visit(function); State.Accesses.Sort(FunctionAnalysis1stPass.ItemComparer); State.Assignments.Sort(FunctionAnalysis1stPass.ItemComparer); var result = State; State = null; if (false) { var bg = new StaticAnalysis.BarrierGenerator(TypeSystem, function); bg.Generate(); var targetFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Barriers" ); Directory.CreateDirectory(targetFolder); var typeName = function.Method.QualifiedIdentifier.Type.ToString(); var methodName = function.Method.Method.Name; if (typeName.Length >= 96) { typeName = typeName.Substring(0, 93) + "…"; } if (methodName.Length >= 32) { methodName = methodName.Substring(0, 29) + "…"; } var filename = String.Format("{0}.{1}", typeName, methodName); filename = filename.Replace("<", "").Replace(">", "").Replace("/", ""); var targetFile = Path.Combine( targetFolder, String.Format("{0}.xml", filename) ); bg.SaveXML(targetFile); } return(result); }
public JSCachedType[] CacheTypesForFunction(JSFunctionExpression function) { var currentKeys = new HashSet<GenericTypeIdentifier>(CachedTypes.Keys); Visit(function); var newKeys = new HashSet<GenericTypeIdentifier>(CachedTypes.Keys); newKeys.ExceptWith(currentKeys); return (from k in newKeys let ct = CachedTypes[k] orderby ct.Index select ct).ToArray(); }
public void VisitNode(JSFunctionExpression fn) { var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn.Body); SecondPass = GetSecondPass(fn.Method); if (SecondPass == null) { throw new InvalidDataException("No second-pass static analysis data for function '" + fn.Method.QualifiedIdentifier + "'"); } VisitChildren(fn); }
public JSCachedType[] CacheTypesForFunction(JSFunctionExpression function) { var currentKeys = new HashSet <GenericTypeIdentifier>(CachedTypes.Keys); Visit(function); var newKeys = new HashSet <GenericTypeIdentifier>(CachedTypes.Keys); newKeys.ExceptWith(currentKeys); return((from k in newKeys let ct = CachedTypes[k] orderby ct.Index select ct).ToArray()); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new EmulateStructAssignment(TypeSystem, FunctionSource, CLR, OptimizeCopies); nested.Visit(fn); return; } var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn.Body); SecondPass = FunctionSource.GetSecondPass(fn.Method); VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new IntroduceVariableDeclarations(fn.AllVariables, TypeInfo); nested.Visit(fn); return; } var existingDeclarations = new HashSet <string>( fn.AllChildrenRecursive.OfType <JSVariableDeclarationStatement>() .SelectMany( (vds) => from vd in vds.Declarations select((JSVariable)vd.Left).Identifier ).Union( from tcb in fn.AllChildrenRecursive.OfType <JSTryCatchBlock>() where tcb.CatchVariable != null select tcb.CatchVariable.Identifier ) ); foreach (var v_ in from v in Variables.Values where !v.IsParameter && !existingDeclarations.Contains(v.Identifier) select v) { ToDeclare.Add(v_); } VisitChildren(fn); if (ToDeclare.Count > 0) { fn.Body.Statements.Insert( 0, new JSVariableDeclarationStatement( (from v in ToDeclare select new JSBinaryOperatorExpression( JSOperator.Assignment, v, v.DefaultValue, v.Type )).ToArray() ) ); } }
public void BuildLabelGroups(JSFunctionExpression function) { // If a label is applied to the first statement in a block, hoist it upward // onto the parent block. var lh = new LabelHoister(); do { lh.HoistedALabel = false; lh.Visit(function); } while (lh.HoistedALabel); // Walk the function to build our list of labels and gotos. Visit(function); // When a goto crosses block boundaries, we need to move the target label // upwards so that the goto can reach it. foreach (var g in Gotos) { var targetLabel = Labels[g.TargetLabel]; if (targetLabel.EnclosingBlock.Depth > g.EnclosingBlock.Depth) targetLabel.EnclosingBlock = g.EnclosingBlock; } foreach (var l in Labels.Values) { l.EnsureLabelGroupExists(LabelGroups); var replacementGoto = new JSExpressionStatement( new JSGotoExpression(l.LabelledStatement.Label) ); l.EnclosingBlock.Block.ReplaceChildRecursive(l.LabelledStatement, replacementGoto); l.LabelGroup.Add(l.LabelledStatement); } // If a label group only contains one label (plus an entry label), // and it has a parent label group, hoist the label up. var lgs = new LabelGroupFlattener(); do { lgs.FlattenedAGroup = false; lgs.Visit(function); } while (lgs.FlattenedAGroup); // Remove any labels within a label group that contain no statements (as long // as no goto targets that label directly). This will prune empty entry/exit labels. var elr = new EmptyLabelRemover(UsedLabels); elr.Visit(function); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new EmulateStructAssignment(TypeSystem, FunctionSource, CLR, OptimizeCopies); nested.Visit(fn); return; } var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn.Body); SecondPass = FunctionSource.GetSecondPass(fn.Method); VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { var existingDeclarations = new HashSet <string>( fn.AllChildrenRecursive.OfType <JSVariableDeclarationStatement>() .SelectMany( (vds) => from vd in vds.Declarations select((JSVariable)vd.Left).Identifier ).Union( from tcb in fn.AllChildrenRecursive.OfType <JSTryCatchBlock>() where tcb.CatchVariable != null select tcb.CatchVariable.Identifier ) ); foreach (var v_ in from v in Variables.Values where !v.IsParameter && !existingDeclarations.Contains(v.Identifier) select v) { ToDeclare.Add(v_); } VisitChildren(fn); foreach (var kvp in ToReplace) { fn.ReplaceChildRecursive(kvp.Key, kvp.Value); } if (ToDeclare.Count > 0) { fn.Body.Statements.Insert( 0, new JSVariableDeclarationStatement( (from v in ToDeclare select new JSBinaryOperatorExpression( JSOperator.Assignment, v, v.DefaultValue, v.IdentifierType )).ToArray() ) ); } }
public void VisitNode(JSFunctionExpression fe) { FunctionStack.Push(fe); try { VisitChildren(fe); } finally { var functionIdentifier = fe.Method.Method.Identifier; CacheSet localSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSet)) { var trType = fe.Method.Reference.Module.TypeSystem.SystemType(); int i = 0; foreach (var kvp in localSet.Signatures) { var record = kvp.Key; var stmt = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, MakeRawOutputIdentifierForIndex(trType, kvp.Value, true), new JSLocalCachedSignatureExpression(trType, record.Method, record.Signature, record.IsConstructor), trType )); fe.Body.Statements.Insert(i++, stmt); } foreach (var kvp in localSet.InterfaceMembers) { var record = kvp.Key; var stmt = new JSVariableDeclarationStatement(new JSBinaryOperatorExpression( JSOperator.Assignment, MakeRawOutputIdentifierForIndex(trType, kvp.Value, false), new JSLocalCachedInterfaceMemberExpression(trType, record.InterfaceType, record.InterfaceMember), trType )); fe.Body.Statements.Insert(i++, stmt); } } FunctionStack.Pop(); } }
public void VisitNode(JSFunctionExpression fn) { if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new IntroduceVariableDeclarations(fn.AllVariables, TypeInfo); nested.Visit(fn); return; } var existingDeclarations = new HashSet<string>( fn.AllChildrenRecursive.OfType<JSVariableDeclarationStatement>() .SelectMany( (vds) => from vd in vds.Declarations select ((JSVariable)vd.Left).Identifier ).Union( from tcb in fn.AllChildrenRecursive.OfType<JSTryCatchBlock>() where tcb.CatchVariable != null select tcb.CatchVariable.Identifier ) ); foreach (var v_ in from v in Variables.Values where !v.IsParameter && !existingDeclarations.Contains(v.Identifier) select v) { ToDeclare.Add(v_); } VisitChildren(fn); if (ToDeclare.Count > 0) fn.Body.Statements.Insert( 0, new JSVariableDeclarationStatement( (from v in ToDeclare select new JSBinaryOperatorExpression( JSOperator.Assignment, v, v.DefaultValue, v.IdentifierType )).ToArray() ) ); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new OptimizeArrayEnumerators(TypeSystem, FunctionSource); nested.Visit(fn); return; } Function = fn; FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes FunctionSource.InvalidateFirstPass(Function.Method.QualifiedIdentifier); FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { new IntroduceVariableReferences(JSIL, fn.AllVariables, new HashSet<string>(from p in fn.Parameters select p.Name)).Visit(fn); return; } VisitChildren(fn); foreach (var r in ReferencesToTransform) { var cr = GetConstructedReference(r); if (cr == null) { // We have already done the variable transform for this variable in the past. continue; } var parameter = (from p in fn.Parameters where p.Identifier == cr.Identifier select p).FirstOrDefault(); if (parameter != null) { TransformParameterIntoReference(parameter, fn.Body); } else { var declaration = (from vds in Declarations from ivd in vds.Declarations.Select((vd, i) => new { vd = vd, i = i }) where MatchesConstructedReference(ivd.vd.Left, cr) select new { vds = vds, vd = ivd.vd, i = ivd.i }).FirstOrDefault(); if (declaration == null) throw new InvalidOperationException(String.Format("Could not locate declaration for {0}", cr)); TransformVariableIntoReference( (JSVariable)declaration.vd.Left, declaration.vds, declaration.i ); } } }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new OptimizeArrayEnumerators(TypeSystem, FunctionSource); nested.Visit(fn); return; } Function = fn; FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes FunctionSource.InvalidateFirstPass(Function.Method.QualifiedIdentifier); FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
public void VisitNode (JSFunctionExpression fn) { var existingDeclarations = new HashSet<string>( fn.AllChildrenRecursive.OfType<JSVariableDeclarationStatement>() .SelectMany( (vds) => from vd in vds.Declarations select ((JSVariable)vd.Left).Identifier ).Union( from tcb in fn.AllChildrenRecursive.OfType<JSTryCatchBlock>() where tcb.CatchVariable != null select tcb.CatchVariable.Identifier ) ); foreach (var v_ in from v in Variables.Values where !v.IsParameter && !existingDeclarations.Contains(v.Identifier) select v) { ToDeclare.Add(v_); } VisitChildren(fn); foreach (var kvp in ToReplace) fn.ReplaceChildRecursive(kvp.Key, kvp.Value); if (ToDeclare.Count > 0) fn.Body.Statements.Insert( 0, new JSVariableDeclarationStatement( (from v in ToDeclare select new JSBinaryOperatorExpression( JSOperator.Assignment, v, v.DefaultValue, v.IdentifierType )).ToArray() ) ); }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; foreach (var pd in ToDeclare) { var es = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, new JSDefaultValueLiteral(pd.Type), pd.Type )); fn.Body.Statements.Insert(i++, es); } } }
public void VisitNode(JSFunctionExpression function) { var oldCurrentMethod = Output.CurrentMethod; if (function.Method != null) { Output.CurrentMethod = function.Method.Reference; } else { Output.CurrentMethod = null; } Output.OpenFunction( function.DisplayName, (o) => o.WriteParameterList(function.Parameters) ); Visit(function.Body); Output.CloseBrace(false); Output.CurrentMethod = oldCurrentMethod; }
/// <summary> /// Writes an interface member reference to the output. /// </summary> public void WriteInterfaceMemberToOutput( JavascriptFormatter output, JavascriptAstEmitter emitter, JSFunctionExpression enclosingFunction, JSMethod jsMethod, JSExpression method, TypeReferenceContext referenceContext ) { int index; var record = new CachedInterfaceMemberRecord(jsMethod.Reference.DeclaringType, jsMethod.Identifier); if ((enclosingFunction.Method != null) || (enclosingFunction.Method.Method != null)) { var functionIdentifier = enclosingFunction.Method.Method.Identifier; CacheSet localSignatureSet; if (LocalCachedSets.TryGetValue(functionIdentifier, out localSignatureSet)) { if (localSignatureSet.InterfaceMembers.TryGetValue(record, out index)) { output.WriteRaw("$im{0:X2}", index); return; } } } if (!Global.InterfaceMembers.TryGetValue(record, out index)) { output.Identifier(jsMethod.Reference.DeclaringType, referenceContext, false); output.Dot(); emitter.Visit(method); } else { output.WriteRaw("$IM{0:X2}()", index); } }
public FunctionTransformPipeline( AssemblyTranslator translator, QualifiedMemberIdentifier identifier, JSFunctionExpression function, SpecialIdentifiers si ) { Translator = translator; Identifier = identifier; Function = function; SpecialIdentifiers = si; FillPipeline(); if (!Translator.FunctionCache.ActiveTransformPipelines.TryAdd(Identifier, this)) { throw new ThreadStateException(); } if (CheckForStaticAnalysisChanges) { OriginalFunctionBody = Function.Body.ToString(); OriginalSecondPass = Translator.FunctionCache.GetSecondPass(function.Method, function.Method.QualifiedIdentifier); } }
public void VisitNode (JSFunctionExpression fn) { FirstPass = GetFirstPass(fn.Method.QualifiedIdentifier); if (FirstPass == null) throw new InvalidOperationException(String.Format( "No first pass static analysis data for method '{0}'", fn.Method.QualifiedIdentifier )); ExemptVariablesFromEffectivelyConstantStatus(); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) continue; var assignments = (from a in FirstPass.Assignments where v.Identifier.Equals(a.Target) select a).ToArray(); var reassignments = (from a in FirstPass.Assignments where v.Identifier.Equals(a.SourceVariable) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Identifier.Equals(a.Source) select a).ToArray(); var invocations = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray(); var unsafeInvocations = FilterInvocations(invocations); var isPassedByReference = FirstPass.VariablesPassedByRef.Contains(v.Name); if (assignments.FirstOrDefault() == null) { if ((accesses.Length == 0) && (invocations.Length == 0) && (reassignments.Length == 0) && !isPassedByReference) { if (TraceLevel >= 1) Console.WriteLine("Eliminating {0} because it is never used.", v); if (!DryRun) { EliminatedVariables.Add(v); EliminateVariable(fn, v, new JSEliminatedVariable(v), fn.Method.QualifiedIdentifier); // We've invalidated the static analysis data so the best choice is to abort. break; } } else { if (TraceLevel >= 2) Console.WriteLine("Never found an initial assignment for {0}.", v); } continue; } var valueType = v.GetActualType(TypeSystem); if (TypeUtil.IsIgnoredType(valueType)) continue; if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it is passed by reference.", v); continue; } if (unsafeInvocations.Length > 1) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; methods are invoked on it multiple times that are not provably safe.", v); continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it participates in control flow.", v); continue; } if (assignments.Length > 1) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it is reassigned.", v); continue; } var replacementAssignment = assignments.First(); var replacement = replacementAssignment.NewValue; if (replacement.SelfAndChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it contains a self-reference.", v); continue; } if (replacement.SelfAndChildrenRecursive.OfType<JSBinaryOperatorExpression>().Any(boe => boe.Operator is JSAssignmentOperator)) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it contains an assignment.", v); continue; } var copies = (from a in FirstPass.Assignments where v.Identifier.Equals(a.SourceVariable) select a).ToArray(); if ( (copies.Length + accesses.Length) > 1 ) { if (CanSkipUsageVeto(replacement)) { if (TraceLevel >= 5) Console.WriteLine("Skipping veto of elimination for {0} because it is a literal.", v); } else { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it is used multiple times.", v); continue; } } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; {1} is not a constant expression.", v, replacement); continue; } var replacementField = JSPointerExpressionUtil.UnwrapExpression(replacement) as JSFieldAccess; if (replacementField == null) { var replacementRef = replacement as JSReferenceExpression; if (replacementRef != null) replacementField = replacementRef.Referent as JSFieldAccess; } var _affectedFields = replacement.SelfAndChildrenRecursive.OfType<JSField>(); if (replacementField != null) _affectedFields = _affectedFields.Concat(new[] { replacementField.Field }); var affectedFields = new HashSet<FieldInfo>((from jsf in _affectedFields select jsf.Field)); _affectedFields = null; if ((affectedFields.Count > 0) || (replacementField != null)) { var firstAssignment = assignments.FirstOrDefault(); var lastAccess = accesses.LastOrDefault(); bool invalidatedByLaterFieldAccess = false; foreach (var fieldAccess in FirstPass.FieldAccesses) { // Note that we only compare the FieldInfo, not the this-reference. // Otherwise, aliasing (accessing the same field through two this references) would cause us // to incorrectly eliminate a local. if (!affectedFields.Contains(fieldAccess.Field.Field)) continue; // Ignore field accesses before the replacement was initialized if (fieldAccess.NodeIndex <= replacementAssignment.NodeIndex) continue; // If the field access comes after the last use of the temporary, we don't care if ((lastAccess != null) && (fieldAccess.StatementIndex > lastAccess.StatementIndex)) continue; // It's a read, so no impact on whether this optimization is valid if (fieldAccess.IsRead) continue; if (TraceLevel >= 2) Console.WriteLine(String.Format("Cannot eliminate {0}; {1} is potentially mutated later", v, fieldAccess.Field.Field)); invalidatedByLaterFieldAccess = true; break; } if (invalidatedByLaterFieldAccess) continue; foreach (var invocation in FirstPass.Invocations) { // If the invocation comes after (or is) the last use of the temporary, we don't care if ((lastAccess != null) && (invocation.StatementIndex >= lastAccess.StatementIndex)) continue; // Same goes for the first assignment. if ((firstAssignment != null) && (invocation.StatementIndex <= firstAssignment.StatementIndex)) continue; var invocationSecondPass = GetSecondPass(invocation.Method); if ( (invocationSecondPass == null) || (invocationSecondPass.MutatedFields == null) ) { if (invocation.ThisAndVariables.Any((kvp) => kvp.Value.ToEnumerable().Contains(v.Identifier))) { if (TraceLevel >= 2) Console.WriteLine(String.Format("Cannot eliminate {0}; a method call without field mutation data ({1}) is invoked between its initialization and use with it as an argument", v, invocation.Method ?? invocation.NonJSMethod)); invalidatedByLaterFieldAccess = true; } } else if (affectedFields.Any(invocationSecondPass.FieldIsMutatedRecursively)) { if (TraceLevel >= 2) Console.WriteLine(String.Format("Cannot eliminate {0}; a method call ({1}) potentially mutates a field it references", v, invocation.Method ?? invocation.NonJSMethod)); invalidatedByLaterFieldAccess = true; } } if (invalidatedByLaterFieldAccess) continue; } if (TraceLevel >= 1) Console.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); if (!DryRun) { EliminatedVariables.Add(v); EliminateVariable(fn, v, replacement, fn.Method.QualifiedIdentifier); // We've invalidated the static analysis data so the best choice is to abort. break; } } }
public void VisitNode (JSFunctionExpression fn) { var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn.Body); SecondPass = GetSecondPass(fn.Method); if (SecondPass == null) throw new InvalidDataException("No second-pass static analysis data for function '" + fn.Method.QualifiedIdentifier + "'"); VisitChildren(fn); }
private void OptimizeFunction( SpecialIdentifiers si, HashSet<string> parameterNames, Dictionary<string, JSVariable> variables, JSFunctionExpression function ) { // Run elimination repeatedly, since eliminating one variable may make it possible to eliminate others if (EliminateTemporaries) { bool eliminated; do { var visitor = new EliminateSingleUseTemporaries( si.TypeSystem, variables, FunctionCache ); visitor.Visit(function); eliminated = visitor.EliminatedVariables.Count > 0; } while (eliminated); } new EmulateStructAssignment( si.TypeSystem, FunctionCache, si.CLR, OptimizeStructCopies ).Visit(function); new IntroduceVariableDeclarations( variables, TypeInfoProvider ).Visit(function); new IntroduceVariableReferences( si.JSIL, variables, parameterNames ).Visit(function); if (SimplifyLoops) new SimplifyLoops( si.TypeSystem ).Visit(function); // Temporary elimination makes it possible to simplify more operators, so do it last if (SimplifyOperators) new SimplifyOperators( si.JSIL, si.JS, si.TypeSystem ).Visit(function); new IntroduceEnumCasts( si.TypeSystem ).Visit(function); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { bool eliminated = false; do { var nested = new EliminateSingleUseTemporaries(TypeSystem, fn.AllVariables, FunctionSource); nested.Visit(fn); eliminated = nested.EliminatedVariables.Count > 0; } while (eliminated); return; } var nullList = new List <int>(); FirstPass = FunctionSource.GetFirstPass(fn.Method.QualifiedIdentifier); if (FirstPass == null) { throw new InvalidOperationException(String.Format( "No first pass static analysis data for method '{0}'", fn.Method.QualifiedIdentifier )); } VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) { continue; } var valueType = v.GetActualType(TypeSystem); if (TypeUtil.IsIgnoredType(valueType)) { continue; } var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); var invocations = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); } continue; } if (assignments.FirstOrDefault() == null) { if (accesses.Length == 0) { if (TraceLevel >= 1) { Debug.WriteLine(String.Format("Eliminating {0} because it is never used.", v)); } EliminatedVariables.Add(v); EliminateVariable(fn, v, new JSEliminatedVariable(v), fn.Method.QualifiedIdentifier); } else { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } } continue; } if (invocations.Length > 0) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; methods are invoked on it.", v)); } continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); } continue; } if (assignments.Length > 1) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); } continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); } continue; } var replacement = assignments.First().NewValue; if (replacement.SelfAndChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); } continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is not a constant expression.", v)); } continue; } if (TraceLevel >= 1) { Debug.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); } var transferDataTo = replacement as JSVariable; if (transferDataTo != null) { foreach (var access in accesses) { FirstPass.Accesses.Remove(access); FirstPass.Accesses.Add(new FunctionAnalysis1stPass.Access( access.ParentNodeIndices, access.StatementIndex, access.NodeIndex, transferDataTo, access.IsControlFlow )); } foreach (var assignment in assignments) { FirstPass.Assignments.Remove(assignment); FirstPass.Assignments.Add(new FunctionAnalysis1stPass.Assignment( assignment.ParentNodeIndices, assignment.StatementIndex, assignment.NodeIndex, transferDataTo, assignment.NewValue, assignment.Operator, assignment.TargetType, assignment.SourceType )); } foreach (var invocation in invocations) { FirstPass.Invocations.Remove(invocation); FirstPass.Invocations.Add(new FunctionAnalysis1stPass.Invocation( invocation.ParentNodeIndices, invocation.StatementIndex, invocation.NodeIndex, transferDataTo, invocation.Method, invocation.Variables )); } } FirstPass.Assignments.RemoveAll((a) => v.Equals(a.Target)); FirstPass.Accesses.RemoveAll((a) => v.Equals(a.Source)); EliminatedVariables.Add(v); EliminateVariable(fn, v, replacement, fn.Method.QualifiedIdentifier); } }
public void VisitNode (JSFunctionExpression function) { Function = function; VisitChildren(function); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new EmulateStructAssignment(TypeSystem, CLR); nested.Visit(fn); return; } var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn); VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { bool eliminated = false; do { var nested = new EliminateSingleUseTemporaries(TypeSystem, fn.AllVariables, FunctionSource); nested.Visit(fn); eliminated = nested.EliminatedVariables.Count > 0; } while (eliminated); return; } var nullList = new List<int>(); FirstPass = FunctionSource.GetFirstPass(fn.Identifier); if (FirstPass == null) throw new InvalidOperationException(); VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) continue; var valueType = v.GetExpectedType(TypeSystem); if (ILBlockTranslator.IsIgnoredType(valueType)) continue; var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); continue; } if (assignments.FirstOrDefault() == null) { if (accesses.Length == 0) { if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} because it is never used.", v)); EliminatedVariables.Add(v); EliminateVariable(fn, v, new JSEliminatedVariable(v)); } else { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); continue; } /* if ((from a in FirstPass.Assignments where v.Equals(a.Target) && a.IsConversion select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it undergoes type conversion.", v)); continue; } */ if (assignments.Length > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); continue; } var replacement = assignments.First().NewValue; if (replacement.AllChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is not a constant expression.", v)); continue; } if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); var transferDataTo = replacement as JSVariable; if (transferDataTo != null) { foreach (var access in accesses) { FirstPass.Accesses.Remove(access); FirstPass.Accesses.Add(new FunctionAnalysis1stPass.Access( access.StatementIndex, access.NodeIndex, transferDataTo, access.IsControlFlow )); } foreach (var assignment in assignments) { FirstPass.Assignments.Remove(assignment); FirstPass.Assignments.Add(new FunctionAnalysis1stPass.Assignment( assignment.StatementIndex, assignment.NodeIndex, transferDataTo, assignment.NewValue, assignment.Operator, assignment.TargetType, assignment.SourceType )); } } FirstPass.Assignments.RemoveAll((a) => v.Equals(a.Target)); FirstPass.Accesses.RemoveAll((a) => v.Equals(a.Source)); EliminatedVariables.Add(v); EliminateVariable(fn, v, replacement); } }
public FunctionCache (ITypeInfoSource typeInfo) { TypeInfo = typeInfo; Comparer = new QualifiedMemberIdentifier.Comparer(typeInfo); Cache = new ConcurrentCache<QualifiedMemberIdentifier, Entry>( Environment.ProcessorCount, 4096, Comparer ); PendingTransformsQueue = new ConcurrentHashQueue<QualifiedMemberIdentifier>( Math.Max(1, Environment.ProcessorCount / 4), 4096, Comparer ); ActiveTransformPipelines = new ConcurrentDictionary<QualifiedMemberIdentifier, FunctionTransformPipeline>( Math.Max(1, Environment.ProcessorCount / 4), 128, Comparer ); MethodTypes = new MethodTypeFactory(); MakeCacheEntry = (id, method) => { PendingTransformsQueue.TryEnqueue(id); return new Entry(id, Locks) { Info = method.Method, Reference = method.Reference, SecondPass = new FunctionAnalysis2ndPass(this, method.Method) }; }; MakePopulatedCacheEntry = (id, args) => { var result = new JSFunctionExpression( new JSMethod(args.Method, args.Info, MethodTypes), args.Translator.Variables, args.Parameters, args.Body, MethodTypes ); PendingTransformsQueue.TryEnqueue(id); return new Entry(id, Locks) { Info = args.Info, Reference = args.Method, Expression = result, SpecialIdentifiers = args.Translator.SpecialIdentifiers }; }; MakeNullCacheEntry = (id, args) => new Entry(id, Locks) { Info = args.Info, Reference = args.Method, Expression = null }; }
public void VisitNode(JSFunctionExpression fn) { VisitChildren(fn); foreach (var p in fn.Parameters) { if (!p.IsReference) continue; var vrat = new VariableReferenceAccessTransformer(JSIL, p); vrat.Visit(fn); } /* if (!fn.Method.Method.IsStatic) { var vrat = new VariableReferenceAccessTransformer(JSIL, Variables["this"]); vrat.Visit(fn); } */ foreach (var r in ReferencesToTransform) { var cr = GetConstructedReference(r); if (cr == null) { // We have already done the variable transform for this variable in the past. continue; } // For 'ref this', we need to replace each individual expression, because we can't // rename the this-variable. if (cr.IsThis) { var refThis = JSIL.NewReference(Variables["this"]); fn.ReplaceChildRecursive(r, refThis); continue; } var parameter = (from p in fn.Parameters where p.Identifier == cr.Identifier select p).FirstOrDefault(); if (parameter != null) { TransformParameterIntoReference(parameter, fn.Body); } else { var declaration = (from kvp in Declarations let vds = kvp.Key from ivd in vds.Declarations.Select((vd, i) => new { vd = vd, i = i }) where MatchesConstructedReference(ivd.vd.Left, cr) select new { vds = vds, vd = ivd.vd, i = ivd.i, block = kvp.Value }).FirstOrDefault(); if (declaration == null) throw new InvalidOperationException(String.Format("Could not locate declaration for {0}", cr)); TransformVariableIntoReference( (JSVariable)declaration.vd.Left, declaration.vds, declaration.i, declaration.block ); } var vrat = new VariableReferenceAccessTransformer(JSIL, cr); vrat.Visit(fn); } }
public void VisitNode(JSFunctionExpression fn) { var nullList = new List<int>(); FirstPass = GetFirstPass(fn.Method.QualifiedIdentifier); if (FirstPass == null) throw new InvalidOperationException(String.Format( "No first pass static analysis data for method '{0}'", fn.Method.QualifiedIdentifier )); VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) continue; var valueType = v.GetActualType(TypeSystem); if (TypeUtil.IsIgnoredType(valueType)) continue; var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var reassignments = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); var invocations = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); continue; } if (assignments.FirstOrDefault() == null) { if ((accesses.Length == 0) && (invocations.Length == 0) && (reassignments.Length == 0)) { if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} because it is never used.", v)); EliminatedVariables.Add(v); EliminateVariable(fn, v, new JSEliminatedVariable(v), fn.Method.QualifiedIdentifier); // We've invalidated the static analysis data so the best choice is to abort. break; } else { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } continue; } if (invocations.Length > 0) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; methods are invoked on it.", v)); continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); continue; } if (assignments.Length > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); continue; } var replacement = assignments.First().NewValue; if (replacement.SelfAndChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is not a constant expression.", v)); continue; } if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); EliminatedVariables.Add(v); EliminateVariable(fn, v, replacement, fn.Method.QualifiedIdentifier); // We've invalidated the static analysis data so the best choice is to abort. break; } }
internal JSFunctionExpression Create( MethodInfo info, MethodDefinition methodDef, MethodReference method, QualifiedMemberIdentifier identifier, ILBlockTranslator translator, IEnumerable<JSVariable> parameters, JSBlockStatement body ) { var result = new JSFunctionExpression( new JSMethod(method, info), translator.Variables, parameters, body ); var entry = new Entry { Identifier = identifier, Info = info, Reference = method, Expression = result, Variables = translator.Variables, ParameterNames = translator.ParameterNames, SpecialIdentifiers = translator.SpecialIdentifiers }; Cache.Add(identifier, entry); OptimizationQueue.Add(identifier); return result; }
public void CacheMethodsForFunction (JSFunctionExpression function) { Visit(function); }
public void EmitFunctionBody (IAstEmitter astEmitter, MethodDefinition method, JSFunctionExpression function) { astEmitter.ReferenceContext.Push(); astEmitter.ReferenceContext.EnclosingMethod = method; try { astEmitter.Emit(function); } catch (Exception exc) { throw new Exception("Error occurred while generating javascript for method '" + method.FullName + "'.", exc); } finally { astEmitter.ReferenceContext.Pop(); } EmitSemicolon(); EmitSpacer(); }