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}