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 { 18 Label, 19 VReg 20} from "../irnodes"; 21import { PandaGen } from "../pandagen"; 22import { Compiler } from "../compiler"; 23import { 24 DiagnosticCode, 25 DiagnosticError 26} from "../diagnostic"; 27import { LabelTarget } from "./labelTarget"; 28export class SwitchBase { 29 private stmt: ts.SwitchStatement; 30 private compiler: Compiler; 31 private pandaGen: PandaGen; 32 private caseLabels: Label[] = []; 33 private switchEndLabel: Label; 34 constructor(stmt: ts.SwitchStatement, compiler: Compiler, caseNums: number, switchEndLabel: Label) { 35 this.stmt = stmt; 36 this.compiler = compiler; 37 this.pandaGen = compiler.getPandaGen(); 38 this.switchEndLabel = switchEndLabel; 39 40 for (let i = 0; i < caseNums; i++) { 41 let caseLabel = new Label(); 42 this.caseLabels.push(caseLabel); 43 } 44 let labelTarget = new LabelTarget(stmt, switchEndLabel, undefined); 45 LabelTarget.pushLabelTarget(labelTarget); 46 LabelTarget.updateName2LabelTarget(stmt.parent, labelTarget); 47 } 48 49 setCasePosition(index: number) { 50 let caseTarget = this.stmt.caseBlock.clauses[index]; 51 this.pandaGen.label(caseTarget, this.caseLabels[index]); 52 } 53 54 compileTagOfSwitch(tagReg: VReg) { 55 this.compiler.compileExpression(this.stmt.expression); 56 this.pandaGen.storeAccumulator(this.stmt.expression, tagReg); 57 } 58 59 compileCaseStatements(index: number) { 60 this.stmt.caseBlock.clauses[index].statements.forEach(statement => { 61 this.compiler.compileStatement(statement); 62 }) 63 } 64 65 JumpIfCase(tag: VReg, index: number) { 66 let stmt = this.stmt; 67 let pandaGen = this.pandaGen; 68 let caseTarget = <ts.CaseClause>stmt.caseBlock.clauses[index]; 69 70 this.compiler.compileExpression(caseTarget.expression); 71 pandaGen.condition(caseTarget, ts.SyntaxKind.ExclamationEqualsEqualsToken, tag, this.caseLabels[index]); 72 } 73 74 JumpToDefault(defaultIndex: number) { 75 let defaultTarget = <ts.DefaultClause>this.stmt.caseBlock.clauses[defaultIndex]; 76 this.pandaGen.branch(defaultTarget, this.caseLabels[defaultIndex]); 77 } 78 79 checkDefaultNum(defaultCnt: number) { 80 if (defaultCnt > 1) { 81 throw new DiagnosticError(this.stmt, DiagnosticCode.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement); 82 } 83 } 84 85 break() { 86 this.pandaGen.branch(this.stmt, this.switchEndLabel); 87 } 88 89 end() { 90 this.pandaGen.label(this.stmt, this.switchEndLabel); 91 } 92} 93 94export function compileSwitchStatement(stmt: ts.SwitchStatement, compiler: Compiler) { 95 let pandaGen = compiler.getPandaGen(); 96 let caseNums = stmt.caseBlock.clauses.length; 97 let switchEndLabel = new Label(); 98 let switchBuilder = new SwitchBase(stmt, compiler, caseNums, switchEndLabel); 99 100 let tagReg = pandaGen.getTemp(); 101 switchBuilder.compileTagOfSwitch(tagReg); 102 compiler.pushScope(stmt); 103 let caseTargets = stmt.caseBlock.clauses; 104 let defaultIndex = 0; 105 let defaultCnt = 0; 106 107 for (let i = 0; i < caseTargets.length; i++) { 108 let caseTarget = caseTargets[i]; 109 if (ts.isDefaultClause(caseTarget)) { 110 defaultIndex = i; 111 defaultCnt++; 112 continue; 113 } 114 115 switchBuilder.JumpIfCase(tagReg, i); 116 } 117 118 switchBuilder.checkDefaultNum(defaultCnt); 119 if (defaultIndex > 0) { 120 switchBuilder.JumpToDefault(defaultIndex); 121 } else { 122 switchBuilder.break(); 123 } 124 125 for (let i = 0; i < caseTargets.length; i++) { 126 switchBuilder.setCasePosition(i); 127 switchBuilder.compileCaseStatements(i); 128 } 129 130 switchBuilder.end(); 131 pandaGen.freeTemps(tagReg); 132 LabelTarget.popLabelTarget(); 133 compiler.popScope(); 134}