1/**
2 * Copyright (c) 2021-2024 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 "property.h"
17
18#include "es2panda.h"
19#include "checker/TSchecker.h"
20#include "compiler/core/ETSGen.h"
21#include "compiler/core/pandagen.h"
22#include "ir/astDump.h"
23#include "ir/srcDump.h"
24
25namespace ark::es2panda::ir {
26Property::Property([[maybe_unused]] Tag const tag, Property const &other, Expression *const key,
27                   Expression *const value)
28    : Property(other)
29{
30    key_ = key;
31    value_ = value;
32}
33
34Property *Property::Clone(ArenaAllocator *const allocator, AstNode *const parent)
35{
36    auto *const key = key_ != nullptr ? key_->Clone(allocator, nullptr)->AsExpression() : nullptr;
37    auto *const value = value_ != nullptr ? value_->Clone(allocator, nullptr)->AsExpression() : nullptr;
38
39    if (auto *const clone = allocator->New<Property>(Tag {}, *this, key, value); clone != nullptr) {
40        if (key != nullptr) {
41            key->SetParent(clone);
42        }
43        if (value != nullptr) {
44            value->SetParent(clone);
45        }
46        if (parent != nullptr) {
47            clone->SetParent(parent);
48        }
49        return clone;
50    }
51
52    throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
53}
54
55bool Property::ConvertibleToPatternProperty()
56{
57    // Object pattern can't contain getter or setter
58    if (IsAccessor() || isMethod_) {
59        return false;
60    }
61
62    switch (value_->Type()) {
63        case AstNodeType::OBJECT_EXPRESSION: {
64            return value_->AsObjectExpression()->ConvertibleToObjectPattern();
65        }
66        case AstNodeType::ARRAY_EXPRESSION: {
67            return value_->AsArrayExpression()->ConvertibleToArrayPattern();
68        }
69        case AstNodeType::ASSIGNMENT_EXPRESSION: {
70            return value_->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
71        }
72        case AstNodeType::IDENTIFIER:
73        case AstNodeType::MEMBER_EXPRESSION:
74        case AstNodeType::ARRAY_PATTERN:
75        case AstNodeType::OBJECT_PATTERN:
76        case AstNodeType::ASSIGNMENT_PATTERN: {
77            break;
78        }
79        default: {
80            if (isShorthand_) {
81                break;
82            }
83
84            return false;
85        }
86    }
87
88    return true;
89}
90
91ValidationInfo Property::ValidateExpression()
92{
93    ValidationInfo info;
94
95    if (!IsComputed() && !IsMethod() && !IsAccessor() && !IsShorthand()) {
96        bool currentIsProto = false;
97
98        if (key_->IsIdentifier()) {
99            currentIsProto = key_->AsIdentifier()->Name().Is("__proto__");
100        } else if (key_->IsStringLiteral()) {
101            currentIsProto = key_->AsStringLiteral()->Str().Is("__proto__");
102        }
103
104        if (currentIsProto) {
105            kind_ = PropertyKind::PROTO;
106        }
107    }
108
109    if (value_ != nullptr) {
110        if (value_->IsAssignmentPattern()) {
111            return {"Invalid shorthand property initializer.", value_->Start()};
112        }
113
114        if (value_->IsObjectExpression()) {
115            info = value_->AsObjectExpression()->ValidateExpression();
116        } else if (value_->IsArrayExpression()) {
117            info = value_->AsArrayExpression()->ValidateExpression();
118        }
119    }
120
121    return info;
122}
123
124void Property::TransformChildren(const NodeTransformer &cb, std::string_view transformationName)
125{
126    if (auto *transformedNode = cb(key_); key_ != transformedNode) {
127        key_->SetTransformedNode(transformationName, transformedNode);
128        key_ = transformedNode->AsExpression();
129    }
130
131    if (auto *transformedNode = cb(value_); value_ != transformedNode) {
132        value_->SetTransformedNode(transformationName, transformedNode);
133        value_ = transformedNode->AsExpression();
134    }
135}
136
137void Property::Iterate(const NodeTraverser &cb) const
138{
139    cb(key_);
140    cb(value_);
141}
142
143void Property::Dump(ir::AstDumper *dumper) const
144{
145    const char *kind = nullptr;
146
147    switch (kind_) {
148        case PropertyKind::INIT: {
149            kind = "init";
150            break;
151        }
152        case PropertyKind::PROTO: {
153            kind = "proto";
154            break;
155        }
156        case PropertyKind::GET: {
157            kind = "get";
158            break;
159        }
160        case PropertyKind::SET: {
161            kind = "set";
162            break;
163        }
164        default: {
165            UNREACHABLE();
166        }
167    }
168
169    dumper->Add({{"type", "Property"},
170                 {"method", isMethod_},
171                 {"shorthand", isShorthand_},
172                 {"computed", isComputed_},
173                 {"key", key_},
174                 {"value", value_},
175                 {"kind", kind}});
176}
177
178void Property::Compile(compiler::PandaGen *pg) const
179{
180    pg->GetAstCompiler()->Compile(this);
181}
182
183void Property::Compile(compiler::ETSGen *etsg) const
184{
185    etsg->GetAstCompiler()->Compile(this);
186}
187
188void Property::Dump(ir::SrcDumper *dumper) const
189{
190    if (kind_ == PropertyKind::INIT) {
191        key_->Dump(dumper);
192        dumper->Add(": ");
193        value_->Dump(dumper);
194    }
195}
196
197checker::Type *Property::Check(checker::TSChecker *checker)
198{
199    return checker->GetAnalyzer()->Check(this);
200}
201
202checker::Type *Property::Check(checker::ETSChecker *checker)
203{
204    return checker->GetAnalyzer()->Check(this);
205}
206}  // namespace ark::es2panda::ir
207