public void VisitNode(JSLabelGroupStatement lgs) { var nonNull = (from kvp in lgs.Labels where !kvp.Value.IsNull select kvp.Value).ToArray(); if (nonNull.Length == 0) { var theNull = new JSNullStatement(); ParentNode.ReplaceChild(lgs, theNull); VisitReplacement(theNull); } else if ( (nonNull.Length == 1) && !nonNull[0].AllChildrenRecursive.OfType <JSGotoExpression>().Any( (g) => g.TargetLabel == nonNull[0].Label ) ) { ParentNode.ReplaceChild(lgs, nonNull[0]); VisitReplacement(nonNull[0]); } else { VisitChildren(lgs); } }
public void EnsureLabelGroupExists(Dictionary <JSStatement, JSLabelGroupStatement> labelGroups) { if (LabelGroup != null) { return; } if (!labelGroups.TryGetValue(EnclosingBlock.Block, out LabelGroup)) { int index = labelGroups.Count; var entryBlock = new JSBlockStatement { Label = String.Format("$entry{0}", index) }; labelGroups[EnclosingBlock.Block] = LabelGroup = new JSLabelGroupStatement(index, entryBlock); var bs = EnclosingBlock.Block as JSBlockStatement; if (bs != null) { entryBlock.Statements.AddRange(bs.Statements); bs.Statements.Clear(); bs.Statements.Add(LabelGroup); } else { throw new NotImplementedException("Unsupported enclosing block type"); } } }
public void VisitNode(JSLabelGroupStatement lgs) { GroupStack.Push(lgs); bool restart = false; do { restart = false; foreach (var keyNode in lgs.Labels.Keys) { if (MaybeFlatten(lgs, keyNode)) { restart = true; break; } } } while (restart); try { VisitChildren(lgs); } finally { GroupStack.Pop(); } }
protected bool MaybeFlatten(JSLabelGroupStatement lgs, LinkedListNode <string> keyNode) { var labelledStatement = lgs.Labels[keyNode.Value]; var checkStatement = labelledStatement; JSLabelGroupStatement labelledGroup = null; // If a label contains a single-statement block, recurse down to find a label group. while (checkStatement != null) { labelledGroup = checkStatement as JSLabelGroupStatement; if (labelledGroup != null) { break; } var checkBlock = checkStatement as JSBlockStatement; if (checkBlock == null) { break; } if (checkBlock.Statements.Count != 1) { break; } checkStatement = checkBlock.Statements[0]; } if ( (labelledGroup != null) && (labelledGroup.Labels.Count == 3) ) { var labels = labelledGroup.Labels.ToArray(); // Hoist the contents of the entry label into the label that contains this label group. var entryLabel = labels[0]; labelledStatement.ReplaceChildRecursive(labelledGroup, entryLabel.Value); // Hoist the single label from this label group into the containing label group, // after the label that contains this label group. var hoistedLabel = labels[1]; var hoistedKeyNode = lgs.Labels.EnqueueAfter( keyNode, hoistedLabel.Key, hoistedLabel.Value ); var exitLabel = labels[2]; // Hoist the contents of the exit label into the label that contains this label group. lgs.Labels.EnqueueAfter( hoistedKeyNode, hoistedLabel.Key + "_after", exitLabel.Value ); return(FlattenedAGroup = true); } return(false); }
public void VisitNode(JSLabelGroupStatement lgs) { var nonNull = (from kvp in lgs.Labels where !kvp.Value.IsNull select kvp.Value).ToArray(); if (nonNull.Length == 0) { var theNull = new JSNullStatement(); ParentNode.ReplaceChild(lgs, theNull); VisitReplacement(theNull); } else if ( (nonNull.Length == 1) && !nonNull[0].AllChildrenRecursive.OfType <JSGotoExpression>().Any( (g) => g.TargetLabel == nonNull[0].Label ) ) { var onlyLabel = nonNull[0]; var labelGroupExit = onlyLabel.AllChildrenRecursive.OfType <JSExitLabelGroupExpression>().FirstOrDefault(); if (labelGroupExit != null) { /* FIXME: This doesn't work * var enclosingFunction = Stack.OfType<JSFunctionExpression>().First(); * var loops = enclosingFunction.AllChildrenRecursive.OfType<JSLoopStatement>(); * * int nextLoopIndex = 0; * if (loops.Any((l) => true)) * nextLoopIndex = loops.Max((l) => l.Index.GetValueOrDefault(0)) + 1; * * var replacement = new JSDoLoop( * JSLiteral.New(false), * onlyLabel * ); * replacement.Index = nextLoopIndex; * * var newBreak = new JSBreakExpression(); * newBreak.TargetLoop = nextLoopIndex; * * onlyLabel.ReplaceChildRecursive(labelGroupExit, newBreak); * * ParentNode.ReplaceChild(lgs, replacement); * VisitReplacement(replacement); */ VisitChildren(lgs); } else { ParentNode.ReplaceChild(lgs, onlyLabel); VisitReplacement(onlyLabel); } } else { VisitChildren(lgs); } }
public void VisitNode(JSLabelGroupStatement lgs) { // HACK: Issue #827 depends on loops containing labelgroups having an index // so that continues and breaks can be targeted. foreach (var ls in Stack.OfType <JSLoopStatement>()) { UsedLoops.Add(ls.Index.Value); } VisitChildren(lgs); }
public void VisitNode(JSLabelGroupStatement lgs) { foreach (var kvp in lgs.Labels.ToArray()) { if (UsedLabels.Contains(kvp.Key)) { continue; } var labelledBlock = kvp.Value as JSBlockStatement; if (labelledBlock == null) { continue; } if (labelledBlock.Statements.Count < 1) { lgs.Labels.Remove(kvp.Key); } } }
public void VisitNode(JSLabelGroupStatement lgs) { CheckForFallthrough(lgs); var data = new LabelGroupData(); LabelGroupStack.Push(data); foreach (var key in lgs.Labels.Keys) { data.Add(key.Value, new LabelGroupLabelData()); } VisitChildren(lgs); // Scan all the labels to determine their direct exit label, if any foreach (var kvp in data) { var targetLabels = kvp.Value.ExitTargetLabels.Distinct().ToArray(); if ( (targetLabels.Length == 1) && (kvp.Value.UntargettedExitCount == 0) ) { kvp.Value.DirectExitLabel = targetLabels[0]; } else { kvp.Value.DirectExitLabel = null; } } // Scan all the labels again to determine their recursive exit label foreach (var kvp in data) { var rel = kvp.Value.RecursiveExitLabel = ComputeRecursiveExitLabel(data, kvp.Key); if (rel != null) { data[rel].TimesUsedAsRecursiveExitTarget += 1; } } // If we have one label that is the recursive exit target for all other labels, we can turn it into the exit label var recursiveExitTargets = data.Where( (kvp) => kvp.Value.TimesUsedAsRecursiveExitTarget > 0 ).ToArray(); if (recursiveExitTargets.Length == 1) { var onlyRecursiveExitTarget = recursiveExitTargets[0].Key; var exitLabel = lgs.ExitLabel; var newExitLabel = lgs.Labels[onlyRecursiveExitTarget]; var newExitLabelData = data[newExitLabel.Label]; if ( (newExitLabelData.ExitTargetLabels.Count == 0) && (newExitLabelData.UntargettedExitCount == 0) && (newExitLabel != lgs.Labels.LastOrDefault().Value) ) { if (TraceLevel >= 1) { Console.WriteLine("// Cannot mark label '{0}' as exit label because it falls through and is not the last label", onlyRecursiveExitTarget); } } else if (exitLabel != null) { if (exitLabel != newExitLabel) { if (TraceLevel >= 1) { Console.WriteLine("// Cannot mark label '{0}' as exit label because this labelgroup already has one", onlyRecursiveExitTarget); } } } else { if (TraceLevel >= 1) { Console.WriteLine("// Marking label '{0}' as exit label", onlyRecursiveExitTarget); } lgs.ExitLabel = newExitLabel; MadeChanges = true; } } if ((lgs.ExitLabel != null) && (lgs.Labels.Count > 1)) { ExtractExitLabel(lgs); } LabelGroupStack.Pop(); }
private void ExtractExitLabel(JSLabelGroupStatement lgs) { var exitLabel = lgs.ExitLabel; var originalLabelName = exitLabel.Label; if (exitLabel.AllChildrenRecursive.OfType <JSGotoExpression>().Any()) { if (TraceLevel >= 1) { Console.WriteLine("// Cannot extract exit label '{0}' from label group because it contains a goto or exit", originalLabelName); } return; } // The label before this label may have fallen through, so we need to append an ExitLabelGroup var previousLabel = lgs.BeforeExitLabel; if (previousLabel != null) { var exitStatement = new JSExpressionStatement(new JSExitLabelGroupExpression(lgs)); var previousBlock = previousLabel as JSBlockStatement; if (previousBlock != null) { previousBlock.Statements.Add(exitStatement); } else { var replacement = new JSBlockStatement( previousLabel, exitStatement ); replacement.Label = previousLabel.Label; replacement.IsControlFlow = true; previousLabel.Label = null; lgs.ReplaceChild(previousLabel, replacement); } } lgs.Labels.Remove(originalLabelName); exitLabel.Label = null; exitLabel.IsControlFlow = false; { var replacement = new JSBlockStatement( lgs, exitLabel ); exitLabel.OriginalLabel = originalLabelName; // Extract the exit label so it directly follows the label group ParentNode.ReplaceChild(lgs, replacement); // Find and convert all the gotos so that they instead break out of the label group var gotos = DeoptimizeSwitchStatements.FindGotos(lgs, originalLabelName); foreach (var g in gotos) { lgs.ReplaceChildRecursive(g, new JSExitLabelGroupExpression(lgs)); } } if (TraceLevel >= 1) { Console.WriteLine("// Extracted exit label '{0}' from label group", originalLabelName); } MadeChanges = true; }
public void EnsureLabelGroupExists(Dictionary <JSStatement, JSLabelGroupStatement> labelGroups) { if (LabelGroup != null) { return; } if (!labelGroups.TryGetValue(EnclosingBlock.Block, out LabelGroup)) { int index = labelGroups.Count; var entryBlock = new JSBlockStatement { Label = String.Format("$entry{0}", index) }; var exitBlock = new JSBlockStatement { Label = String.Format("$exit{0}", index) }; labelGroups[EnclosingBlock.Block] = LabelGroup = new JSLabelGroupStatement( index, entryBlock, exitBlock ); var bs = EnclosingBlock.Block as JSBlockStatement; if (bs != null) { bool populatingEntryBlock = true, populatingExitBlock = false; // We want to walk through the block's statements and collect them. // Statements before the labelled statement go into the entry block. // Statements after the labelled statement go into the exit block. // FIXME: Is the following rule correct? Without it, FaultBlock breaks. // If we hit another labelled statement while filling the exit block, stop filling it. for (var i = 0; i < bs.Statements.Count; i++) { var s = bs.Statements[i]; if (s.Label == Label) { populatingEntryBlock = false; populatingExitBlock = true; } else if (populatingEntryBlock) { entryBlock.Statements.Add(s); } else if (populatingExitBlock) { if (s.Label == null) { exitBlock.Statements.Add(s); } // HACK: The synthesized switch exit labels generated when hoisting a block out of // a switch statement shouldn't terminate statement collection. Without this hack, // ForeachInEnumeratorFunctionMonoBinary fails. else if (s.Label.StartsWith("$switchExit")) { ; } else { populatingExitBlock = false; } } } // FIXME: Is this valid? It might confuse the AstVisitor if bs is on the stack. bs.Statements.Clear(); bs.Statements.Add(LabelGroup); } else { throw new NotImplementedException("Unsupported enclosing block type: " + EnclosingBlock.Block.GetType().Name); } } }
public void VisitNode(JSLabelGroupStatement lgs) { Formatter.ConditionalNewLine(); Formatter.NewLine(); var labelName = "labelgroup_" + lgs.GroupIndex; var firstLabel = lgs.Labels.First().Key; foreach (var kvp in lgs.Labels) { Labels.Add(kvp.Key, new LabelInfo(lgs.GroupIndex, Labels.Count)); } Comment("LabelGroup {0} (starting at {1})", lgs.GroupIndex, firstLabel); SetCurrentLabel(firstLabel, lgs.GroupIndex); Formatter.NewLine(); Formatter.WriteSExpr( "label", (__) => { __.WriteRaw("${0} ", labelName); // HACK __.Unindent(); __.WriteSExpr( "loop", (_) => { _.WriteRaw("(label $labelgroup_{0}_dispatch ", lgs.GroupIndex); _.NewLine(); foreach (var kvp in lgs.Labels) { var labelInfo = GetLabelInfo(kvp.Key); _.ConditionalNewLine(); Comment("Begin Label {1}", lgs.GroupIndex, kvp.Key); _.WriteRaw("(if (i32.eq (get_local $currentLabel_{0}) (i32.const {1})) ", labelInfo.GroupIndex, labelInfo.LabelIndex); _.Indent(); _.NewLine(); Visit(kvp.Value); _.ConditionalNewLine(); _.NewLine(); _.Unindent(); _.WriteRaw(")"); _.NewLine(); Comment("End Label {1}", lgs.GroupIndex, kvp.Key); _.NewLine(); } _.ConditionalNewLine(); Comment("Fallthrough exit from labelgroup {0}", lgs.GroupIndex); _.WriteRaw("(break $labelgroup_{0})", lgs.GroupIndex); _.NewLine(); _.ConditionalNewLine(); _.WriteRaw(")", lgs.GroupIndex); _.NewLine(); } ); // HACK __.Indent(); } ); }
public void EnsureLabelGroupExists(Dictionary <JSStatement, JSLabelGroupStatement> labelGroups) { if (LabelGroup != null) { return; } if (!labelGroups.TryGetValue(EnclosingBlock.Block, out LabelGroup)) { int index = labelGroups.Count; var entryBlock = new JSBlockStatement { Label = String.Format("$entry{0}", index) }; var exitBlock = new JSBlockStatement { Label = String.Format("$exit{0}", index) }; labelGroups[EnclosingBlock.Block] = LabelGroup = new JSLabelGroupStatement( index, entryBlock, exitBlock ); var bs = EnclosingBlock.Block as JSBlockStatement; if (bs != null) { bool populatingEntryBlock = true, populatingExitBlock = false; // We want to walk through the block's statements and collect them. // Statements before the labelled statement go into the entry block. // Statements after the labelled statement go into the exit block. // FIXME: Is the following rule correct? Without it, FaultBlock breaks. // If we hit another labelled statement while filling the exit block, stop filling it. for (var i = 0; i < bs.Statements.Count; i++) { var s = bs.Statements[i]; if (s.Label == Label) { populatingEntryBlock = false; populatingExitBlock = true; } else if (populatingEntryBlock) { entryBlock.Statements.Add(s); } else if (populatingExitBlock) { if (s.Label == null) { exitBlock.Statements.Add(s); } else { populatingExitBlock = false; } } } bs.Statements.Clear(); bs.Statements.Add(LabelGroup); } else { throw new NotImplementedException("Unsupported enclosing block type"); } } }
public void VisitNode(JSLabelGroupStatement labelGroup) { Output.NewLine(); var stepLabel = String.Format("$labelgroup{0}", labelGroup.GroupIndex); var labelVar = String.Format("$label{0}", labelGroup.GroupIndex); var firstLabel = labelGroup.Labels.First().Key; Output.WriteRaw("var"); Output.Space(); Output.Identifier(labelVar); Output.WriteRaw(" = "); Output.Value(firstLabel); Output.Semicolon(); Output.Label(stepLabel); Output.WriteRaw("while"); Output.Space(); Output.LPar(); Output.WriteRaw("true"); Output.RPar(); Output.Space(); Output.OpenBrace(); Output.WriteRaw("switch"); Output.Space(); Output.LPar(); Output.Identifier(labelVar); Output.RPar(); Output.Space(); Output.OpenBrace(); bool isFirst = true; Func <string, bool> emitGoto = (labelName) => { if (labelName != null) { if (!labelGroup.Labels.ContainsKey(labelName)) { return(false); } Output.Identifier(labelVar); Output.WriteRaw(" = "); Output.Value(labelName); Output.Semicolon(); } Output.WriteRaw("continue"); Output.Space(); Output.Identifier(stepLabel); return(true); }; GotoStack.Push(emitGoto); bool needsTrailingBreak = true; foreach (var kvp in labelGroup.Labels) { if (!isFirst && needsTrailingBreak) { Output.Indent(); emitGoto(kvp.Key); Output.Semicolon(true); Output.Unindent(); } Output.WriteRaw("case"); Output.Space(); Output.Value(kvp.Key); Output.WriteRaw(":"); Output.Indent(); Output.NewLine(); Visit(kvp.Value); Func <JSNode, bool> isNotNull = (n) => { if (n.IsNull) { return(false); } if (n is JSNullStatement) { return(false); } if (n is JSNullExpression) { return(false); } var es = n as JSExpressionStatement; if (es != null) { if (es.Expression.IsNull) { return(false); } if (es.Expression is JSNullExpression) { return(false); } } return(true); }; var nonNullChildren = kvp.Value.Children.Where(isNotNull); var lastStatement = nonNullChildren.LastOrDefault(); JSBlockStatement lastBlockStatement; while ((lastBlockStatement = lastStatement as JSBlockStatement) != null) { if (lastBlockStatement.IsControlFlow) { break; } else { nonNullChildren = lastStatement.Children.Where(isNotNull); lastStatement = nonNullChildren.LastOrDefault(); } } var lastExpressionStatement = lastStatement as JSExpressionStatement; if ( (lastExpressionStatement != null) && ( (lastExpressionStatement.Expression is JSContinueExpression) || (lastExpressionStatement.Expression is JSBreakExpression) || (lastExpressionStatement.Expression is JSGotoExpression) ) ) { needsTrailingBreak = false; } else { needsTrailingBreak = true; } isFirst = false; Output.Unindent(); Output.NewLine(); } GotoStack.Pop(); if (needsTrailingBreak) { Output.Indent(); Output.WriteRaw("break"); Output.Space(); Output.Identifier(stepLabel); Output.Semicolon(true); Output.Unindent(); } Output.CloseBrace(); Output.CloseBrace(); }