| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| library dart2js.resolution.label_scope; |
| |
| import '../elements/elements.dart' show |
| JumpTarget, |
| LabelDefinition; |
| import '../util/util.dart' show |
| Link; |
| |
| abstract class LabelScope { |
| LabelScope get outer; |
| LabelDefinition lookup(String label); |
| } |
| |
| class LabeledStatementLabelScope implements LabelScope { |
| final LabelScope outer; |
| final Map<String, LabelDefinition> labels; |
| LabeledStatementLabelScope(this.outer, this.labels); |
| LabelDefinition lookup(String labelName) { |
| LabelDefinition label = labels[labelName]; |
| if (label != null) return label; |
| return outer.lookup(labelName); |
| } |
| } |
| |
| class SwitchLabelScope implements LabelScope { |
| final LabelScope outer; |
| final Map<String, LabelDefinition> caseLabels; |
| |
| SwitchLabelScope(this.outer, this.caseLabels); |
| |
| LabelDefinition lookup(String labelName) { |
| LabelDefinition result = caseLabels[labelName]; |
| if (result != null) return result; |
| return outer.lookup(labelName); |
| } |
| } |
| |
| class EmptyLabelScope implements LabelScope { |
| const EmptyLabelScope(); |
| LabelDefinition lookup(String label) => null; |
| LabelScope get outer { |
| throw 'internal error: empty label scope has no outer'; |
| } |
| } |
| |
| class StatementScope { |
| LabelScope labels; |
| Link<JumpTarget> breakTargetStack; |
| Link<JumpTarget> continueTargetStack; |
| // Used to provide different numbers to statements if one is inside the other. |
| // Can be used to make otherwise duplicate labels unique. |
| int nestingLevel = 0; |
| |
| StatementScope() |
| : labels = const EmptyLabelScope(), |
| breakTargetStack = const Link<JumpTarget>(), |
| continueTargetStack = const Link<JumpTarget>(); |
| |
| LabelDefinition lookupLabel(String label) { |
| return labels.lookup(label); |
| } |
| |
| JumpTarget currentBreakTarget() => |
| breakTargetStack.isEmpty ? null : breakTargetStack.head; |
| |
| JumpTarget currentContinueTarget() => |
| continueTargetStack.isEmpty ? null : continueTargetStack.head; |
| |
| void enterLabelScope(Map<String, LabelDefinition> elements) { |
| labels = new LabeledStatementLabelScope(labels, elements); |
| nestingLevel++; |
| } |
| |
| void exitLabelScope() { |
| nestingLevel--; |
| labels = labels.outer; |
| } |
| |
| void enterLoop(JumpTarget element) { |
| breakTargetStack = breakTargetStack.prepend(element); |
| continueTargetStack = continueTargetStack.prepend(element); |
| nestingLevel++; |
| } |
| |
| void exitLoop() { |
| nestingLevel--; |
| breakTargetStack = breakTargetStack.tail; |
| continueTargetStack = continueTargetStack.tail; |
| } |
| |
| void enterSwitch(JumpTarget breakElement, |
| Map<String, LabelDefinition> continueElements) { |
| breakTargetStack = breakTargetStack.prepend(breakElement); |
| labels = new SwitchLabelScope(labels, continueElements); |
| nestingLevel++; |
| } |
| |
| void exitSwitch() { |
| nestingLevel--; |
| breakTargetStack = breakTargetStack.tail; |
| labels = labels.outer; |
| } |
| } |