• 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 { 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