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 "memberExpression.h"
17
18 #include "checker/TSchecker.h"
19 #include "checker/ets/castingContext.h"
20 #include "compiler/core/ETSGen.h"
21 #include "compiler/core/pandagen.h"
22 #include "ir/astDump.h"
23 #include "ir/srcDump.h"
24
25 namespace panda::es2panda::ir {
MemberExpression(Tag const tag,MemberExpression const & other,Expression * const object,Expression * const property)26 MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other,
27 Expression *const object, Expression *const property)
28 : MemberExpression(other)
29 {
30 object_ = object;
31 if (object_ != nullptr) {
32 object_->SetParent(this);
33 }
34
35 property_ = property;
36 if (property_ != nullptr) {
37 property_->SetParent(this);
38 }
39 }
40
IsPrivateReference() const41 bool MemberExpression::IsPrivateReference() const noexcept
42 {
43 return property_->IsIdentifier() && property_->AsIdentifier()->IsPrivateIdent();
44 }
45
TransformChildren(const NodeTransformer & cb)46 void MemberExpression::TransformChildren(const NodeTransformer &cb)
47 {
48 object_ = cb(object_)->AsExpression();
49 property_ = cb(property_)->AsExpression();
50 }
51
Iterate(const NodeTraverser & cb) const52 void MemberExpression::Iterate(const NodeTraverser &cb) const
53 {
54 cb(object_);
55 cb(property_);
56 }
57
Dump(ir::AstDumper * dumper) const58 void MemberExpression::Dump(ir::AstDumper *dumper) const
59 {
60 dumper->Add({{"type", "MemberExpression"},
61 {"object", object_},
62 {"property", property_},
63 {"computed", computed_},
64 {"optional", IsOptional()}});
65 }
66
Dump(ir::SrcDumper * dumper) const67 void MemberExpression::Dump(ir::SrcDumper *dumper) const
68 {
69 ASSERT(object_ != nullptr);
70 ASSERT(property_ != nullptr);
71
72 object_->Dump(dumper);
73 if (IsOptional()) {
74 dumper->Add("?");
75 }
76 if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
77 dumper->Add("[");
78 property_->Dump(dumper);
79 dumper->Add("]");
80 } else {
81 dumper->Add(".");
82 property_->Dump(dumper);
83 }
84 if ((parent_ != nullptr) && (parent_->IsBlockStatement() || parent_->IsBlockExpression())) {
85 dumper->Add(";");
86 dumper->Endl();
87 }
88 }
89
LoadRhs(compiler::PandaGen * pg) const90 void MemberExpression::LoadRhs(compiler::PandaGen *pg) const
91 {
92 compiler::RegScope rs(pg);
93 bool isSuper = object_->IsSuperExpression();
94 compiler::Operand prop = pg->ToPropertyKey(property_, computed_, isSuper);
95
96 if (isSuper) {
97 pg->LoadSuperProperty(this, prop);
98 } else if (IsPrivateReference()) {
99 const auto &name = property_->AsIdentifier()->Name();
100 compiler::VReg objReg = pg->AllocReg();
101 pg->StoreAccumulator(this, objReg);
102 compiler::VReg ctor = pg->AllocReg();
103 compiler::Function::LoadClassContexts(this, pg, ctor, name);
104 pg->ClassPrivateFieldGet(this, ctor, objReg, name);
105 } else {
106 pg->LoadObjProperty(this, prop);
107 }
108 }
109
CompileToRegs(compiler::PandaGen * pg,compiler::VReg object,compiler::VReg property) const110 void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const
111 {
112 object_->Compile(pg);
113 pg->StoreAccumulator(this, object);
114
115 pg->OptionalChainCheck(IsOptional(), object);
116
117 if (!computed_) {
118 pg->LoadAccumulatorString(this, property_->AsIdentifier()->Name());
119 } else {
120 property_->Compile(pg);
121 }
122
123 pg->StoreAccumulator(this, property);
124 }
125
Compile(compiler::PandaGen * pg) const126 void MemberExpression::Compile(compiler::PandaGen *pg) const
127 {
128 pg->GetAstCompiler()->Compile(this);
129 }
130
CompileToReg(compiler::PandaGen * pg,compiler::VReg objReg) const131 void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg objReg) const
132 {
133 object_->Compile(pg);
134 pg->StoreAccumulator(this, objReg);
135 pg->OptionalChainCheck(IsOptional(), objReg);
136 LoadRhs(pg);
137 }
138
Compile(compiler::ETSGen * etsg) const139 void MemberExpression::Compile(compiler::ETSGen *etsg) const
140 {
141 etsg->GetAstCompiler()->Compile(this);
142 }
143
Check(checker::TSChecker * checker)144 checker::Type *MemberExpression::Check(checker::TSChecker *checker)
145 {
146 return checker->GetAnalyzer()->Check(this);
147 }
148
ResolveEnumMember(checker::ETSChecker * checker,checker::Type * type) const149 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveEnumMember(checker::ETSChecker *checker,
150 checker::Type *type) const
151 {
152 auto const *const enumInterface = [type]() -> checker::ETSEnumInterface const * {
153 if (type->IsETSEnumType()) {
154 return type->AsETSEnumType();
155 }
156 return type->AsETSStringEnumType();
157 }();
158
159 if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) {
160 return {enumInterface->LookupMethod(checker, object_, property_->AsIdentifier()), nullptr};
161 }
162
163 auto *const literalType = enumInterface->LookupConstant(checker, object_, property_->AsIdentifier());
164 return {literalType, literalType->GetMemberVar()};
165 }
166
ResolveObjectMember(checker::ETSChecker * checker) const167 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
168 checker::ETSChecker *checker) const
169 {
170 auto resolveRes = checker->ResolveMemberReference(this, objType_);
171 switch (resolveRes.size()) {
172 case 1U: {
173 if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
174 auto var = resolveRes[0]->Variable()->AsLocalVariable();
175 checker->ValidatePropertyAccess(var, objType_, property_->Start());
176 return {checker->GetTypeOfVariable(var), var};
177 }
178 return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
179 }
180 case 2U: {
181 // ETSExtensionFuncHelperType(class_method_type, extension_method_type)
182 auto *resolvedType = checker->CreateETSExtensionFuncHelperType(
183 checker->GetTypeOfVariable(resolveRes[1]->Variable())->AsETSFunctionType(),
184 checker->GetTypeOfVariable(resolveRes[0]->Variable())->AsETSFunctionType());
185 return {resolvedType, nullptr};
186 }
187 default: {
188 UNREACHABLE();
189 }
190 }
191 }
192
CheckUnionMember(checker::ETSChecker * checker,checker::Type * baseType)193 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
194 {
195 auto *const unionType = baseType->AsETSUnionType();
196 checker::Type *commonPropType = nullptr;
197 auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
198 if (commonPropType != nullptr && commonPropType != memberType) {
199 checker->ThrowTypeError("Member type must be the same for all union objects.", Start());
200 }
201 commonPropType = memberType;
202 };
203 for (auto *const type : unionType->ConstituentTypes()) {
204 auto *const apparent = checker->GetApparentType(type);
205 if (apparent->IsETSObjectType()) {
206 SetObjectType(apparent->AsETSObjectType());
207 addPropType(ResolveObjectMember(checker).first);
208 } else if (apparent->IsETSEnumType() || baseType->IsETSStringEnumType()) {
209 addPropType(ResolveEnumMember(checker, apparent).first);
210 } else {
211 UNREACHABLE();
212 }
213 }
214 SetObjectType(unionType->GetLeastUpperBoundType()->AsETSObjectType());
215 return commonPropType;
216 }
217
AdjustType(checker::ETSChecker * checker,checker::Type * type)218 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
219 {
220 SetOptionalType(type);
221 if (PropVar() != nullptr) {
222 uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
223 }
224 if (IsOptional() && checker->MayHaveNulllikeValue(Object()->TsType())) {
225 checker->Relation()->SetNode(this);
226 type = checker->CreateOptionalResultType(type);
227 checker->Relation()->SetNode(nullptr);
228 }
229 SetTsType(type);
230 return TsType();
231 }
232
CheckArrayIndexValue(checker::ETSChecker * checker) const233 void MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
234 {
235 std::size_t index;
236
237 auto const &number = property_->AsNumberLiteral()->Number();
238
239 if (number.IsInteger()) {
240 auto const value = number.GetLong();
241 if (value < 0) {
242 checker->ThrowTypeError("Index value cannot be less than zero.", property_->Start());
243 }
244 index = static_cast<std::size_t>(value);
245 } else if (number.IsReal()) {
246 double value = number.GetDouble();
247 double fraction = std::modf(value, &value);
248 if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
249 checker->ThrowTypeError("Index value cannot be less than zero or fractional.", property_->Start());
250 }
251 index = static_cast<std::size_t>(value);
252 } else {
253 UNREACHABLE();
254 }
255
256 if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
257 checker->ThrowTypeError("Index value cannot be greater than or equal to the array size.", property_->Start());
258 }
259
260 if (object_->IsIdentifier() &&
261 object_->AsIdentifier()->Variable()->Declaration()->Node()->Parent()->IsVariableDeclarator()) {
262 auto const varDecl =
263 object_->AsIdentifier()->Variable()->Declaration()->Node()->Parent()->AsVariableDeclarator();
264 if (varDecl->Init() != nullptr && varDecl->Init()->IsArrayExpression() &&
265 varDecl->Init()->AsArrayExpression()->Elements().size() <= index) {
266 checker->ThrowTypeError("Index value cannot be greater than or equal to the array size.",
267 property_->Start());
268 }
269 }
270 }
271
CheckIndexAccessMethod(checker::ETSChecker * checker)272 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
273 {
274 checker::PropertySearchFlags searchFlag =
275 checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL;
276 searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
277 // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
278
279 if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
280 searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
281 }
282
283 bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
284 std::string_view const methodName =
285 isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
286
287 auto *const method = objType_->GetProperty(methodName, searchFlag);
288 if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
289 checker->ThrowTypeError("Object type doesn't have proper index access method.", Start());
290 }
291
292 ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()};
293 arguments.emplace_back(property_);
294 if (isSetter) {
295 arguments.emplace_back(Parent()->AsAssignmentExpression()->Right());
296 }
297
298 auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
299
300 checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing",
301 checker::TypeRelationFlag::NO_THROW);
302 if (signature == nullptr) {
303 checker->ThrowTypeError("Cannot find index access method with the required signature.", Property()->Start());
304 }
305 checker->ValidateSignatureAccessibility(objType_, nullptr, signature, Start(),
306 "Index access method is not visible here.");
307
308 ASSERT(signature->Function() != nullptr);
309
310 if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
311 checker->CheckThrowingStatements(this);
312 }
313
314 return isSetter ? signature->Params()[1]->TsType() : signature->ReturnType();
315 }
316
CheckTupleAccessMethod(checker::ETSChecker * checker,checker::Type * baseType)317 checker::Type *MemberExpression::CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)
318 {
319 ASSERT(baseType->IsETSTupleType());
320
321 auto *const tupleTypeAtIdx =
322 baseType->AsETSTupleType()->GetTypeAtIndex(checker->GetTupleElementAccessValue(Property()->TsType()));
323
324 if ((!Parent()->IsAssignmentExpression() || Parent()->AsAssignmentExpression()->Left() != this) &&
325 (!Parent()->IsUpdateExpression())) {
326 // Error never should be thrown by this call, because LUB of types can be converted to any type which
327 // LUB was calculated by casting
328 const checker::CastingContext cast(checker->Relation(), this, baseType->AsETSArrayType()->ElementType(),
329 tupleTypeAtIdx, Start(), {"Tuple type couldn't be converted "});
330
331 // NOTE(mmartin): this can be replaced with the general type mapper, once implemented
332 if ((GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U) {
333 auto *const savedNode = checker->Relation()->GetNode();
334 if (savedNode == nullptr) {
335 checker->Relation()->SetNode(this);
336 }
337
338 SetTupleConvertedType(checker->PrimitiveTypeAsETSBuiltinType(tupleTypeAtIdx));
339
340 checker->Relation()->SetNode(savedNode);
341 }
342
343 if (tupleTypeAtIdx->IsETSObjectType() && baseType->AsETSArrayType()->ElementType()->IsETSObjectType()) {
344 SetTupleConvertedType(tupleTypeAtIdx);
345 }
346 }
347
348 return tupleTypeAtIdx;
349 }
350
CheckComputed(checker::ETSChecker * checker,checker::Type * baseType)351 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
352 {
353 if (baseType->IsETSArrayType() || baseType->IsETSDynamicType()) {
354 checker->ValidateArrayIndex(property_);
355
356 if (baseType->IsETSTupleType()) {
357 checker->ValidateTupleIndex(baseType->AsETSTupleType(), this);
358 } else if (baseType->IsETSArrayType() && property_->IsNumberLiteral()) {
359 // Check if the index value is inside array bounds if it is defined explicitly
360 CheckArrayIndexValue(checker);
361 }
362
363 // NOTE: apply capture conversion on this type
364 if (baseType->IsETSArrayType()) {
365 if (baseType->IsETSTupleType()) {
366 return CheckTupleAccessMethod(checker, baseType);
367 }
368
369 if (object_->IsArrayExpression() && property_->IsNumberLiteral()) {
370 auto const number = property_->AsNumberLiteral()->Number().GetLong();
371 return object_->AsArrayExpression()->Elements()[number]->Check(checker);
372 }
373
374 return baseType->AsETSArrayType()->ElementType();
375 }
376
377 // Dynamic
378 return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
379 }
380
381 if (baseType->IsETSObjectType()) {
382 SetObjectType(baseType->AsETSObjectType());
383 return CheckIndexAccessMethod(checker);
384 }
385
386 checker->ThrowTypeError("Indexed access is not supported for such expression type.", Object()->Start());
387 }
388
Check(checker::ETSChecker * checker)389 checker::Type *MemberExpression::Check(checker::ETSChecker *checker)
390 {
391 return checker->GetAnalyzer()->Check(this);
392 }
393
394 // NOLINTNEXTLINE(google-default-arguments)
Clone(ArenaAllocator * const allocator,AstNode * const parent)395 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
396 {
397 auto *const object = object_ != nullptr ? object_->Clone(allocator)->AsExpression() : nullptr;
398 auto *const property = property_ != nullptr ? property_->Clone(allocator)->AsExpression() : nullptr;
399
400 if (auto *const clone =
401 allocator->New<MemberExpression>(object, property, kind_, computed_, MaybeOptionalExpression::IsOptional());
402 clone != nullptr) {
403 if (object != nullptr) {
404 object->SetParent(clone);
405 }
406 if (property != nullptr) {
407 property->SetParent(clone);
408 }
409 if (parent != nullptr) {
410 clone->SetParent(parent);
411 }
412 return clone;
413 }
414
415 throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
416 }
417
418 } // namespace panda::es2panda::ir
419