• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as ts from "typescript";
17import * as jshelpers from "../jshelpers";
18import { DiagnosticCode, DiagnosticError } from "../diagnostic";
19import { TryStatement } from "./tryStatement";
20import { Label } from "../irnodes";
21
22export class LabelTarget {
23    private static name2LabelTarget: Map<string, LabelTarget> = new Map<string, LabelTarget>();
24    private static labelTargetStack: LabelTarget[] = [];
25    private static curLoopLabelTarget: LabelTarget | undefined = undefined;
26    private node: ts.Node;
27    private breakTargetLabel: Label;
28    private continueTargetLabel: Label | undefined;
29    private preLoopLabelTarget: LabelTarget | undefined = undefined;
30    private hasLoopEnv: boolean;
31    private loopEnvLevel: number;
32    private tryStatement: TryStatement | undefined;
33
34    constructor(node: ts.Node, breakTargetLabel: Label, continueTargetLabel: Label | undefined, hasLoopEnv: boolean = false) {
35        this.node = node;
36        this.breakTargetLabel = breakTargetLabel;
37        this.continueTargetLabel = continueTargetLabel;
38        this.hasLoopEnv = hasLoopEnv;
39        this.loopEnvLevel = hasLoopEnv ? 1 : 0;
40        this.tryStatement = TryStatement.getCurrentTryStatement();
41        if (continueTargetLabel) {
42            this.preLoopLabelTarget = LabelTarget.curLoopLabelTarget;
43            LabelTarget.curLoopLabelTarget = this;
44        }
45    }
46
47    containLoopEnv(): boolean {
48        return this.hasLoopEnv;
49    }
50
51    getBreakTargetLabel(): Label {
52        return this.breakTargetLabel;
53    }
54
55    getContinueTargetLabel(): any {
56        return this.continueTargetLabel;
57    }
58
59    getLoopEnvLevel(): number {
60        return this.loopEnvLevel;
61    }
62
63    getTryStatement(): TryStatement {
64        return this.tryStatement;
65    }
66
67    getPreLoopLabelTarget(): LabelTarget {
68        return this.preLoopLabelTarget;
69    }
70
71    getCorrespondingNode(): ts.Node {
72        return this.node;
73    }
74
75    private increaseLoopEnvLevel(): void {
76        this.loopEnvLevel += 1;
77    }
78
79    private decreaseLoopEnvLevel(): void {
80        this.loopEnvLevel -= 1;
81    }
82
83    private static isLabelTargetsEmpty(): boolean {
84        if (LabelTarget.labelTargetStack.length === 0) {
85            return true;
86        }
87        return false;
88    }
89
90    static getCloseLabelTarget(): LabelTarget | undefined {
91        if (!LabelTarget.isLabelTargetsEmpty()) {
92            return LabelTarget.labelTargetStack[LabelTarget.labelTargetStack.length - 1];
93        }
94        return undefined;
95    }
96
97    static pushLabelTarget(labelTarget: LabelTarget): void {
98        if (labelTarget.hasLoopEnv) {
99            if (TryStatement.getCurrentTryStatement()) {
100                TryStatement.getCurrentTryStatement().increaseLoopEnvLevel();
101            }
102            LabelTarget.labelTargetStack.forEach(lt => lt.increaseLoopEnvLevel());
103        }
104        LabelTarget.labelTargetStack.push(labelTarget);
105    }
106
107    static popLabelTarget(): void {
108        if (!LabelTarget.isLabelTargetsEmpty()) {
109            let popedLabelTarget = LabelTarget.labelTargetStack.pop();
110            if (popedLabelTarget.containLoopEnv()) {
111                if (TryStatement.getCurrentTryStatement()) {
112                    TryStatement.getCurrentTryStatement().decreaseLoopEnvLevel();
113                }
114                LabelTarget.labelTargetStack.forEach(lt => lt.decreaseLoopEnvLevel());
115            }
116            if (popedLabelTarget.getContinueTargetLabel()) {
117                LabelTarget.curLoopLabelTarget = popedLabelTarget.getPreLoopLabelTarget();
118            }
119        }
120    }
121
122    static updateName2LabelTarget(node: ts.Node, labelTarget: LabelTarget): void {
123        while (node.kind === ts.SyntaxKind.LabeledStatement) {
124            let labeledStmt = <ts.LabeledStatement>node;
125            let labelName = jshelpers.getTextOfIdentifierOrLiteral(labeledStmt.label);
126
127            // make sure saved label is different
128            if (LabelTarget.name2LabelTarget.has(labelName)) {
129                throw new DiagnosticError(node, DiagnosticCode.Duplicate_label_0);
130            }
131
132            LabelTarget.name2LabelTarget.set(labelName, labelTarget);
133            node = node.parent;
134        }
135    }
136
137    static deleteName2LabelTarget(labelName: string): void {
138        LabelTarget.name2LabelTarget.delete(labelName);
139    }
140
141    static getCurLoopLabelTarget(): LabelTarget {
142        return LabelTarget.curLoopLabelTarget;
143    }
144
145    static getLabelTarget(stmt: ts.BreakOrContinueStatement): LabelTarget {
146        let labelTarget: LabelTarget;
147        if (stmt.label) {
148            let labelName = jshelpers.getTextOfIdentifierOrLiteral(stmt.label);
149            labelTarget = LabelTarget.name2LabelTarget.get(labelName)!;
150        } else {
151            labelTarget =
152                ts.isContinueStatement(stmt) ? LabelTarget.getCurLoopLabelTarget() : LabelTarget.getCloseLabelTarget()!;
153        }
154        return labelTarget;
155    }
156}