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 "objectExpression.h"
17
18#include "ir/base/decorator.h"
19#include "util/helpers.h"
20#include "compiler/base/literals.h"
21#include "compiler/core/pandagen.h"
22#include "compiler/core/ETSGen.h"
23#include "checker/TSchecker.h"
24#include "checker/ETSchecker.h"
25#include "checker/ets/typeRelationContext.h"
26#include "checker/ts/destructuringContext.h"
27#include "ir/astDump.h"
28#include "ir/srcDump.h"
29#include "ir/typeNode.h"
30#include "ir/base/property.h"
31#include "ir/base/scriptFunction.h"
32#include "ir/base/spreadElement.h"
33#include "ir/expressions/arrayExpression.h"
34#include "ir/expressions/assignmentExpression.h"
35#include "ir/expressions/identifier.h"
36#include "ir/statements/variableDeclarator.h"
37#include "ir/validationInfo.h"
38
39namespace ark::es2panda::ir {
40ObjectExpression::ObjectExpression([[maybe_unused]] Tag const tag, ObjectExpression const &other,
41                                   ArenaAllocator *const allocator)
42    : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator),
43      decorators_(allocator->Adapter()),
44      properties_(allocator->Adapter())
45{
46    preferredType_ = other.preferredType_;
47    isDeclaration_ = other.isDeclaration_;
48    trailingComma_ = other.trailingComma_;
49    optional_ = other.optional_;
50
51    for (auto *property : other.properties_) {
52        properties_.emplace_back(property->Clone(allocator, this)->AsExpression());
53    }
54
55    for (auto *decorator : other.decorators_) {
56        decorators_.emplace_back(decorator->Clone(allocator, this));
57    }
58}
59
60ObjectExpression *ObjectExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
61{
62    if (auto *const clone = allocator->New<ObjectExpression>(Tag {}, *this, allocator); clone != nullptr) {
63        if (parent != nullptr) {
64            clone->SetParent(parent);
65        }
66        return clone;
67    }
68    throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
69}
70
71static std::pair<ValidationInfo, bool> ValidateProperty(Property *prop, bool &foundProto)
72{
73    ValidationInfo info = prop->ValidateExpression();
74    if (prop->Kind() == PropertyKind::PROTO) {
75        if (foundProto) {
76            return {{"Duplicate __proto__ fields are not allowed in object literals", prop->Key()->Start()}, true};
77        }
78
79        foundProto = true;
80    }
81
82    return {info, false};
83}
84
85ValidationInfo ObjectExpression::ValidateExpression()
86{
87    if (optional_) {
88        return {"Unexpected token '?'.", Start()};
89    }
90
91    if (TypeAnnotation() != nullptr) {
92        return {"Unexpected token.", TypeAnnotation()->Start()};
93    }
94
95    ValidationInfo info;
96    bool foundProto = false;
97
98    for (auto *it : properties_) {
99        switch (it->Type()) {
100            case AstNodeType::OBJECT_EXPRESSION:
101            case AstNodeType::ARRAY_EXPRESSION: {
102                return {"Unexpected token.", it->Start()};
103            }
104            case AstNodeType::SPREAD_ELEMENT: {
105                info = it->AsSpreadElement()->ValidateExpression();
106                break;
107            }
108            case AstNodeType::PROPERTY: {
109                auto *prop = it->AsProperty();
110                bool ret = false;
111                std::tie(info, ret) = ValidateProperty(prop, foundProto);
112                if (ret) {
113                    return info;
114                }
115                break;
116            }
117            default: {
118                break;
119            }
120        }
121
122        if (info.Fail()) {
123            break;
124        }
125    }
126
127    return info;
128}
129
130bool ObjectExpression::ConvertibleToObjectPattern()
131{
132    // NOTE: rsipka. throw more precise messages in case of false results
133    bool restFound = false;
134    bool convResult = true;
135
136    for (auto *it : properties_) {
137        switch (it->Type()) {
138            case AstNodeType::ARRAY_EXPRESSION: {
139                convResult = it->AsArrayExpression()->ConvertibleToArrayPattern();
140                break;
141            }
142            case AstNodeType::SPREAD_ELEMENT: {
143                if (!restFound && it == properties_.back() && !trailingComma_) {
144                    convResult = it->AsSpreadElement()->ConvertibleToRest(isDeclaration_, false);
145                } else {
146                    convResult = false;
147                }
148
149                restFound = true;
150                break;
151            }
152            case AstNodeType::OBJECT_EXPRESSION: {
153                convResult = it->AsObjectExpression()->ConvertibleToObjectPattern();
154                break;
155            }
156            case AstNodeType::ASSIGNMENT_EXPRESSION: {
157                convResult = it->AsAssignmentExpression()->ConvertibleToAssignmentPattern();
158                break;
159            }
160            case AstNodeType::META_PROPERTY_EXPRESSION:
161            case AstNodeType::CHAIN_EXPRESSION:
162            case AstNodeType::SEQUENCE_EXPRESSION: {
163                convResult = false;
164                break;
165            }
166            case AstNodeType::PROPERTY: {
167                convResult = it->AsProperty()->ConvertibleToPatternProperty();
168                break;
169            }
170            default: {
171                break;
172            }
173        }
174
175        if (!convResult) {
176            break;
177        }
178    }
179
180    SetType(AstNodeType::OBJECT_PATTERN);
181    return convResult;
182}
183
184void ObjectExpression::SetDeclaration()
185{
186    isDeclaration_ = true;
187}
188
189void ObjectExpression::SetOptional(bool optional)
190{
191    optional_ = optional;
192}
193
194void ObjectExpression::TransformChildren(const NodeTransformer &cb, std::string_view transformationName)
195{
196    for (auto *&it : decorators_) {
197        if (auto *transformedNode = cb(it); it != transformedNode) {
198            it->SetTransformedNode(transformationName, transformedNode);
199            it = transformedNode->AsDecorator();
200        }
201    }
202
203    for (auto *&it : properties_) {
204        if (auto *transformedNode = cb(it); it != transformedNode) {
205            it->SetTransformedNode(transformationName, transformedNode);
206            it = transformedNode->AsExpression();
207        }
208    }
209
210    if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) {
211        if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
212            typeAnnotation->SetTransformedNode(transformationName, transformedNode);
213            SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode));
214        }
215    }
216}
217
218void ObjectExpression::Iterate(const NodeTraverser &cb) const
219{
220    for (auto *it : decorators_) {
221        cb(it);
222    }
223
224    for (auto *it : properties_) {
225        cb(it);
226    }
227
228    if (TypeAnnotation() != nullptr) {
229        cb(TypeAnnotation());
230    }
231}
232
233void ObjectExpression::Dump(ir::AstDumper *dumper) const
234{
235    dumper->Add({{"type", (type_ == AstNodeType::OBJECT_EXPRESSION) ? "ObjectExpression" : "ObjectPattern"},
236                 {"decorators", AstDumper::Optional(decorators_)},
237                 {"properties", properties_},
238                 {"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
239                 {"optional", AstDumper::Optional(optional_)}});
240}
241
242void ObjectExpression::Dump(ir::SrcDumper *dumper) const
243{
244    dumper->Add("{");
245    if (!properties_.empty()) {
246        dumper->IncrIndent();
247        dumper->Endl();
248        for (auto property : properties_) {
249            property->Dump(dumper);
250            dumper->Add(",");
251            if (property == properties_.back()) {
252                dumper->DecrIndent();
253            }
254            dumper->Endl();
255        }
256    }
257    dumper->Add("}");
258}
259
260void ObjectExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const
261{
262    pg->GetAstCompiler()->Compile(this);
263}
264
265checker::Type *ObjectExpression::CheckPattern(checker::TSChecker *checker)
266{
267    checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
268
269    bool isOptional = false;
270
271    for (auto it = properties_.rbegin(); it != properties_.rend(); it++) {
272        if ((*it)->IsRestElement()) {
273            ASSERT((*it)->AsRestElement()->Argument()->IsIdentifier());
274            util::StringView indexInfoName("x");
275            auto *newIndexInfo =
276                checker->Allocator()->New<checker::IndexInfo>(checker->GlobalAnyType(), indexInfoName, false);
277            desc->stringIndexInfo = newIndexInfo;
278            continue;
279        }
280
281        ASSERT((*it)->IsProperty());
282        auto *prop = (*it)->AsProperty();
283
284        if (prop->IsComputed()) {
285            continue;
286        }
287
288        varbinder::LocalVariable *foundVar = desc->FindProperty(prop->Key()->AsIdentifier()->Name());
289        checker::Type *patternParamType = checker->GlobalAnyType();
290        varbinder::Variable *bindingVar = nullptr;
291
292        if (prop->IsShorthand()) {
293            switch (prop->Value()->Type()) {
294                case ir::AstNodeType::IDENTIFIER: {
295                    const ir::Identifier *ident = prop->Value()->AsIdentifier();
296                    ASSERT(ident->Variable());
297                    bindingVar = ident->Variable();
298                    break;
299                }
300                case ir::AstNodeType::ASSIGNMENT_PATTERN: {
301                    auto *assignmentPattern = prop->Value()->AsAssignmentPattern();
302                    patternParamType = assignmentPattern->Right()->Check(checker);
303                    ASSERT(assignmentPattern->Left()->AsIdentifier()->Variable());
304                    bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
305                    isOptional = true;
306                    break;
307                }
308                default: {
309                    UNREACHABLE();
310                }
311            }
312        } else {
313            switch (prop->Value()->Type()) {
314                case ir::AstNodeType::IDENTIFIER: {
315                    bindingVar = prop->Value()->AsIdentifier()->Variable();
316                    break;
317                }
318                case ir::AstNodeType::ARRAY_PATTERN: {
319                    patternParamType = prop->Value()->AsArrayPattern()->CheckPattern(checker);
320                    break;
321                }
322                case ir::AstNodeType::OBJECT_PATTERN: {
323                    patternParamType = prop->Value()->AsObjectPattern()->CheckPattern(checker);
324                    break;
325                }
326                case ir::AstNodeType::ASSIGNMENT_PATTERN: {
327                    isOptional = CheckAssignmentPattern(prop, bindingVar, patternParamType, checker, foundVar);
328                    break;
329                }
330                default: {
331                    UNREACHABLE();
332                }
333            }
334        }
335
336        if (bindingVar != nullptr) {
337            bindingVar->SetTsType(patternParamType);
338        }
339
340        if (foundVar != nullptr) {
341            continue;
342        }
343
344        varbinder::LocalVariable *patternVar = varbinder::Scope::CreateVar(
345            checker->Allocator(), prop->Key()->AsIdentifier()->Name(), varbinder::VariableFlags::PROPERTY, *it);
346        patternVar->SetTsType(patternParamType);
347
348        if (isOptional) {
349            patternVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
350        }
351
352        desc->properties.insert(desc->properties.begin(), patternVar);
353    }
354
355    checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
356    returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS);
357    return returnType;
358}
359
360bool ObjectExpression::CheckAssignmentPattern(Property *prop, varbinder::Variable *&bindingVar,
361                                              checker::Type *&patternParamType, checker::TSChecker *checker,
362                                              varbinder::LocalVariable *foundVar)
363{
364    auto *assignmentPattern = prop->Value()->AsAssignmentPattern();
365
366    if (assignmentPattern->Left()->IsIdentifier()) {
367        bindingVar = assignmentPattern->Left()->AsIdentifier()->Variable();
368        patternParamType = checker->GetBaseTypeOfLiteralType(assignmentPattern->Right()->Check(checker));
369        return true;
370    }
371
372    if (assignmentPattern->Left()->IsArrayPattern()) {
373        auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
374        auto destructuringContext = checker::ArrayDestructuringContext(
375            {checker, assignmentPattern->Left()->AsArrayPattern(), false, true, nullptr, assignmentPattern->Right()});
376
377        if (foundVar != nullptr) {
378            destructuringContext.SetInferredType(
379                checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()}));
380        }
381
382        destructuringContext.Start();
383        patternParamType = destructuringContext.InferredType();
384        return true;
385    }
386
387    ASSERT(assignmentPattern->Left()->IsObjectPattern());
388    auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
389    auto destructuringContext = checker::ObjectDestructuringContext(
390        {checker, assignmentPattern->Left()->AsObjectPattern(), false, true, nullptr, assignmentPattern->Right()});
391
392    if (foundVar != nullptr) {
393        destructuringContext.SetInferredType(
394            checker->CreateUnionType({foundVar->TsType(), destructuringContext.InferredType()}));
395    }
396
397    destructuringContext.Start();
398    patternParamType = destructuringContext.InferredType();
399    return true;
400}
401
402checker::Type *ObjectExpression::Check(checker::TSChecker *checker)
403{
404    return checker->GetAnalyzer()->Check(this);
405}
406
407void ObjectExpression::Compile(compiler::ETSGen *etsg) const
408{
409    etsg->GetAstCompiler()->Compile(this);
410}
411
412checker::Type *ObjectExpression::Check(checker::ETSChecker *checker)
413{
414    return checker->GetAnalyzer()->Check(this);
415}
416}  // namespace ark::es2panda::ir
417