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
16#include "variableDeclarator.h"
17
18#include <compiler/base/lreference.h>
19#include <compiler/core/pandagen.h>
20#include <ir/astDump.h>
21#include <ir/expression.h>
22#include <ir/typeNode.h>
23#include <ir/statements/variableDeclaration.h>
24#include <ir/expressions/arrayExpression.h>
25#include <ir/expressions/identifier.h>
26#include <ir/expressions/objectExpression.h>
27#include <typescript/checker.h>
28#include <typescript/core/destructuringContext.h>
29
30namespace panda::es2panda::ir {
31
32void VariableDeclarator::Iterate(const NodeTraverser &cb) const
33{
34    cb(id_);
35
36    if (init_) {
37        cb(init_);
38    }
39}
40
41void VariableDeclarator::Dump(ir::AstDumper *dumper) const
42{
43    dumper->Add({{"type", "VariableDeclarator"},
44                 {"id", id_},
45                 {"definite", AstDumper::Optional(definite_)},
46                 {"init", AstDumper::Nullable(init_)}});
47}
48
49void VariableDeclarator::Compile(compiler::PandaGen *pg) const
50{
51    const ir::VariableDeclaration *decl = parent_->AsVariableDeclaration();
52    if (decl->Declare()) {
53        return;
54    }
55
56    compiler::LReference lref = compiler::LReference::CreateLRef(pg, id_, true);
57
58    if (init_) {
59        init_->Compile(pg);
60    } else {
61        if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) {
62            return;
63        }
64        if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::LET && !decl->Parent()->IsCatchClause()) {
65            pg->LoadConst(this, compiler::Constant::JS_UNDEFINED);
66        }
67    }
68
69    lref.SetValue();
70}
71
72static void CheckSimpleVariableDeclaration(checker::Checker *checker, const ir::VariableDeclarator *declarator)
73{
74    CHECK_NOT_NULL(declarator);
75    binder::Variable *bindingVar = declarator->Id()->AsIdentifier()->Variable();
76    CHECK_NOT_NULL(bindingVar);
77    checker::Type *previousType = bindingVar->TsType();
78    const ir::Expression *typeAnnotation = declarator->Id()->AsIdentifier()->TypeAnnotation();
79    const ir::Expression *initializer = declarator->Init();
80    bool isConst = declarator->Parent()->AsVariableDeclaration()->Kind() ==
81                   ir::VariableDeclaration::VariableDeclarationKind::CONST;
82
83    if (isConst) {
84        checker->AddStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
85    }
86
87    if (typeAnnotation) {
88        typeAnnotation->Check(checker);
89    }
90
91    if (typeAnnotation && initializer) {
92        checker::Type *annotationType = typeAnnotation->AsTypeNode()->GetType(checker);
93        checker->ElaborateElementwise(annotationType, initializer, declarator->Id()->Start());
94        bindingVar->SetTsType(annotationType);
95    } else if (typeAnnotation) {
96        bindingVar->SetTsType(typeAnnotation->AsTypeNode()->GetType(checker));
97    } else if (initializer) {
98        checker::Type *initializerType = checker->CheckTypeCached(initializer);
99
100        if (!isConst) {
101            initializerType = checker->GetBaseTypeOfLiteralType(initializerType);
102        }
103
104        bindingVar->SetTsType(initializerType);
105    } else {
106        checker->ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."},
107                                declarator->Id()->Start());
108    }
109
110    if (previousType) {
111        checker->IsTypeIdenticalTo(bindingVar->TsType(), previousType,
112                                   {"Subsequent variable declaration must have the same type. Variable '",
113                                    bindingVar->Name(), "' must be of type '", previousType, "', but here has type '",
114                                    bindingVar->TsType(), "'."},
115                                   declarator->Id()->Start());
116    }
117
118    checker->RemoveStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
119}
120
121checker::Type *VariableDeclarator::Check(checker::Checker *checker) const
122{
123    auto found = checker->NodeCache().find(this);
124    if (found != checker->NodeCache().end()) {
125        return nullptr;
126    }
127
128    if (id_->IsIdentifier()) {
129        CheckSimpleVariableDeclaration(checker, this);
130        checker->NodeCache().insert({this, nullptr});
131        return nullptr;
132    }
133
134    if (id_->IsArrayPattern()) {
135        auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
136        checker::ArrayDestructuringContext(checker, id_, false, id_->AsArrayPattern()->TypeAnnotation() == nullptr,
137                                           id_->AsArrayPattern()->TypeAnnotation(), init_)
138            .Start();
139
140        checker->NodeCache().insert({this, nullptr});
141        return nullptr;
142    }
143
144    ASSERT(id_->IsObjectPattern());
145    auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
146    checker::ObjectDestructuringContext(checker, id_, false, id_->AsObjectPattern()->TypeAnnotation() == nullptr,
147                                        id_->AsObjectPattern()->TypeAnnotation(), init_)
148        .Start();
149
150    checker->NodeCache().insert({this, nullptr});
151    return nullptr;
152}
153
154void VariableDeclarator::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
155{
156    id_ = std::get<ir::AstNode *>(cb(id_))->AsExpression();
157
158    if (init_) {
159        init_ = std::get<ir::AstNode *>(cb(init_))->AsExpression();
160    }
161}
162
163}  // namespace panda::es2panda::ir
164