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 { PandaGen } from "src/pandagen"; 17import * as ts from "typescript"; 18import { Compiler } from "../compiler"; 19import { compileDestructuring } from "../compilerUtils"; 20import { DiagnosticCode, DiagnosticError } from "../diagnostic"; 21import { getObjAndProp } from "../expression/memberAccessExpression"; 22import { findInnerExprOfParenthesis } from "../expression/parenthesizedExpression"; 23import { VReg } from "../irnodes"; 24import * as jshelpers from "../jshelpers"; 25import { Scope } from "../scope"; 26import { VarDeclarationKind, Variable } from "../variable"; 27import { isBindingOrAssignmentPattern } from "./util"; 28 29enum ReferenceKind { MEMBER_ACCESS, LOCAL_OR_GLOBAL, DESTRUCTURING }; 30export class LReference { 31 private node: ts.Node; 32 private compiler: Compiler; 33 private refKind: ReferenceKind; 34 private isDeclaration: boolean; 35 private obj: VReg | undefined = undefined; 36 private prop: VReg | undefined = undefined; 37 private propLiteral: string | number | undefined = undefined; 38 readonly variable: { scope: Scope | undefined, level: number, v: Variable | undefined } | undefined; 39 private destructuringTarget: ts.BindingOrAssignmentPattern | undefined; 40 41 constructor( 42 node: ts.Node, 43 compiler: Compiler, 44 isDeclaration: boolean, 45 refKind: ReferenceKind, 46 variable: { scope: Scope | undefined, level: number, v: Variable | undefined } | undefined) { 47 this.node = node; 48 this.compiler = compiler; 49 this.isDeclaration = isDeclaration; 50 this.refKind = refKind; 51 52 if (refKind === ReferenceKind.DESTRUCTURING) { 53 this.destructuringTarget = <ts.BindingOrAssignmentPattern>node; 54 } else if (refKind === ReferenceKind.LOCAL_OR_GLOBAL) { 55 this.variable = variable!; 56 } else if (refKind === ReferenceKind.MEMBER_ACCESS) { 57 this.obj = compiler.getPandaGen().getTemp(); 58 this.prop = compiler.getPandaGen().getTemp(); 59 } 60 } 61 62 getValue(): void { 63 let pandaGen = this.compiler.getPandaGen(); 64 switch (this.refKind) { 65 case ReferenceKind.MEMBER_ACCESS: 66 let prop: VReg | number | string; 67 if (this.propLiteral === undefined) { 68 prop = <VReg>this.prop!; 69 } else { 70 prop = this.propLiteral; 71 } 72 pandaGen.loadObjProperty(this.node, <VReg>this.obj, prop); 73 return; 74 case ReferenceKind.LOCAL_OR_GLOBAL: 75 this.compiler.loadTarget(this.node, this.variable!); 76 return; 77 case ReferenceKind.DESTRUCTURING: 78 throw new Error("Destructuring target can't be loaded"); 79 default: 80 throw new Error("Invalid LReference kind to GetValue"); 81 } 82 } 83 84 setValue(): void { 85 let pandaGen = this.compiler.getPandaGen(); 86 switch (this.refKind) { 87 case ReferenceKind.MEMBER_ACCESS: { 88 let prop: VReg | number | string; 89 if (this.propLiteral === undefined) { 90 prop = <VReg>this.prop!; 91 } else { 92 prop = this.propLiteral; 93 } 94 if (jshelpers.isSuperProperty(<ts.ElementAccessExpression | ts.PropertyAccessExpression>this.node)) { 95 let thisReg = pandaGen.getTemp(); 96 this.compiler.getThis(this.node, thisReg); 97 pandaGen.storeSuperProperty(this.node, thisReg, prop); 98 pandaGen.freeTemps(thisReg); 99 } else { 100 pandaGen.storeObjProperty(this.node, <VReg>this.obj, prop); 101 } 102 pandaGen.freeTemps(...[<VReg>this.obj, <VReg>this.prop]); 103 return; 104 } 105 case ReferenceKind.LOCAL_OR_GLOBAL: 106 this.compiler.storeTarget(this.node, this.variable!, this.isDeclaration); 107 return; 108 case ReferenceKind.DESTRUCTURING: 109 compileDestructuring(<ts.BindingOrAssignmentPattern>this.destructuringTarget, pandaGen, this.compiler); 110 return; 111 default: 112 throw new Error("Invalid LReference kind to SetValue"); 113 } 114 } 115 116 setObjectAndProperty(pandaGen: PandaGen, obj: VReg, prop: VReg | number | string): void { 117 if (!jshelpers.isSuperProperty(this.node)) { 118 pandaGen.moveVreg(this.node, <VReg>this.obj, obj); 119 } 120 121 if (prop instanceof VReg) { 122 pandaGen.moveVreg(this.node, <VReg>this.prop, prop); 123 return; 124 } 125 126 this.propLiteral = <string | number>prop; 127 } 128 129 static generateLReference(compiler: Compiler, node: ts.Node, isDeclaration: boolean): LReference { 130 let pandaGen = compiler.getPandaGen(); 131 132 let realNode: ts.Node = node; 133 134 realNode = ts.skipPartiallyEmittedExpressions(node); 135 136 if (ts.isParenthesizedExpression(realNode)) { 137 realNode = findInnerExprOfParenthesis(realNode); 138 } 139 140 if (ts.isIdentifier(realNode)) { 141 let name = jshelpers.getTextOfIdentifierOrLiteral(<ts.Identifier>realNode); 142 let variable = compiler.getCurrentScope().find(name); 143 if (!variable.v) { 144 // @ts-ignore 145 if (ts.isGeneratedIdentifier(realNode)) { 146 variable.v = compiler.getCurrentScope().add(name, VarDeclarationKind.VAR); 147 } else { 148 variable.v = compiler.getCurrentScope().add(name, VarDeclarationKind.NONE); 149 } 150 } 151 152 return new LReference(realNode, compiler, isDeclaration, ReferenceKind.LOCAL_OR_GLOBAL, variable); 153 } 154 155 if (ts.isPropertyAccessExpression(realNode) || ts.isElementAccessExpression(realNode)) { 156 let lref = new LReference(realNode, compiler, false, ReferenceKind.MEMBER_ACCESS, undefined); 157 let objReg = pandaGen.getTemp(); 158 let propReg = pandaGen.getTemp(); 159 let { obj: object, prop: property } = getObjAndProp(realNode, objReg, propReg, compiler); 160 lref.setObjectAndProperty(pandaGen, object, property); 161 pandaGen.freeTemps(objReg, propReg); 162 return lref; 163 } 164 165 if (ts.isVariableDeclarationList(realNode)) { 166 let decls = realNode.declarations; 167 if (decls.length != 1) { 168 throw new Error("Malformed variable declaration"); 169 } 170 return LReference.generateLReference(compiler, decls[0].name, true); 171 } 172 173 if (isBindingOrAssignmentPattern(realNode)) { 174 return new LReference(realNode, compiler, isDeclaration, ReferenceKind.DESTRUCTURING, undefined); 175 } 176 177 throw new DiagnosticError( 178 node, 179 DiagnosticCode.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access 180 ); 181 } 182} 183 184