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 <binder/binder.h>
17#include <ir/base/scriptFunction.h>
18#include <ir/expression.h>
19#include <ir/expressions/arrayExpression.h>
20#include <ir/expressions/callExpression.h>
21#include <ir/expressions/functionExpression.h>
22#include <ir/expressions/identifier.h>
23#include <ir/expressions/memberExpression.h>
24#include <ir/statements/expressionStatement.h>
25#include <ir/statements/blockStatement.h>
26
27#include <string_view>
28#include <vector>
29
30#include "parserImpl.h"
31
32namespace panda::es2panda::parser {
33static std::vector<std::string_view> cjsMandatoryParams = {binder::Binder::CJS_MANDATORY_PARAM_EXPORTS,
34                                                           binder::Binder::CJS_MANDATORY_PARAM_REQUIRE,
35                                                           binder::Binder::CJS_MANDATORY_PARAM_MODULE,
36                                                           binder::Binder::CJS_MANDATORY_PARAM_FILENAME,
37                                                           binder::Binder::CJS_MANDATORY_PARAM_DIRNAME};
38
39void ParserImpl::AddCommonjsParams(ArenaVector<ir::Expression *> &params)
40{
41    for (auto paramName : cjsMandatoryParams) {
42        ir::Expression *param = AllocNode<ir::Identifier>(paramName);
43        param->AsIdentifier()->SetReference();
44        Binder()->AddParamDecl(param);
45        params.push_back(param);
46    }
47}
48
49void ParserImpl::AddReflectApplyArgs(ArenaVector<ir::Expression *> &args, ir::FunctionExpression *wrapper)
50{
51    ASSERT(wrapper != nullptr);
52    // wrapper
53    args.push_back(wrapper);
54    // thisValue
55    ir::Expression *thisValue = AllocNode<ir::Identifier>(binder::Binder::CJS_MANDATORY_PARAM_EXPORTS);
56    thisValue->AsIdentifier()->SetReference();
57    args.push_back(thisValue);
58    // wrapper's arguments
59    ArenaVector<ir::Expression *> elements(Allocator()->Adapter());
60    for (auto argName : cjsMandatoryParams) {
61        ir::Expression *arg = AllocNode<ir::Identifier>(argName);
62        arg->AsIdentifier()->SetReference();
63        elements.push_back(arg);
64    }
65    ir::ArrayExpression *wrapperArgsArray =
66        AllocNode<ir::ArrayExpression>(ir::AstNodeType::ARRAY_EXPRESSION, std::move(elements), false);
67    args.push_back(wrapperArgsArray);
68}
69
70void ParserImpl::ParseCommonjs()
71{
72    // create FunctionExpression as callee
73    ir::FunctionExpression *wrapper = nullptr;
74    {
75        FunctionContext functionContext(this, ParserStatus::FUNCTION | ParserStatus::ALLOW_NEW_TARGET);
76        FunctionParameterContext funcParamContext(&context_, Binder());
77        auto *funcParamScope = funcParamContext.LexicalScope().GetScope();
78
79        ArenaVector<ir::Expression *> params(Allocator()->Adapter());
80        AddCommonjsParams(params);
81
82        auto functionCtx = binder::LexicalScope<binder::FunctionScope>(Binder());
83        auto *functionScope = functionCtx.GetScope();
84        functionScope->BindParamScope(funcParamScope);
85        funcParamScope->BindFunctionScope(functionScope);
86
87        ParseProgram(ScriptKind::COMMONJS);
88
89        auto *funcNode =
90            AllocNode<ir::ScriptFunction>(functionScope, std::move(params), nullptr, program_.Ast(), nullptr,
91                                          functionContext.Flags(), false, Extension() == ScriptExtension::TS);
92        functionScope->BindNode(funcNode);
93        funcParamScope->BindNode(funcNode);
94
95        wrapper = AllocNode<ir::FunctionExpression>(funcNode);
96    }
97
98    // create CallExpression
99    ArenaVector<ir::Expression *> arguments(Allocator()->Adapter());
100    AddReflectApplyArgs(arguments, wrapper);
101
102    auto *apply = AllocNode<ir::Identifier>("apply");
103    auto *reflect = AllocNode<ir::Identifier>("Reflect");
104    auto *reflectApply = AllocNode<ir::MemberExpression>(reflect, apply,
105        ir::MemberExpression::MemberExpressionKind::PROPERTY_ACCESS, false, false);
106
107    auto *callExpr = AllocNode<ir::CallExpression>(reflectApply, std::move(arguments), nullptr, false);
108    // create ExpressionStatement
109    auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(callExpr);
110
111    ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
112    statements.push_back(exprStatementNode);
113
114    auto *blockStmt = AllocNode<ir::BlockStatement>(Binder()->GetScope(), std::move(statements));
115    Binder()->GetScope()->BindNode(blockStmt);
116
117    program_.SetAst(blockStmt);
118}
119}  // namespace panda::es2panda::parser