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 "checker/types/ets/etsTupleType.h"
21 #include "compiler/core/ETSGen.h"
22 #include "compiler/core/pandagen.h"
23 #include "ir/astDump.h"
24 #include "ir/srcDump.h"
25
26 namespace ark::es2panda::ir {
MemberExpression(Tag const tag,MemberExpression const & other,ArenaAllocator * allocator)27 MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other,
28 ArenaAllocator *allocator)
29 : MemberExpression(other)
30 {
31 object_ = other.object_ != nullptr ? other.object_->Clone(allocator, this)->AsExpression() : nullptr;
32 property_ = other.property_ != nullptr ? other.property_->Clone(allocator, this)->AsExpression() : nullptr;
33 }
34
IsPrivateReference() const35 bool MemberExpression::IsPrivateReference() const noexcept
36 {
37 return property_->IsIdentifier() && property_->AsIdentifier()->IsPrivateIdent();
38 }
39
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)40 void MemberExpression::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
41 {
42 if (auto *transformedNode = cb(object_); object_ != transformedNode) {
43 object_->SetTransformedNode(transformationName, transformedNode);
44 object_ = transformedNode->AsExpression();
45 }
46
47 if (auto *transformedNode = cb(property_); property_ != transformedNode) {
48 property_->SetTransformedNode(transformationName, transformedNode);
49 property_ = transformedNode->AsExpression();
50 }
51 }
52
Iterate(const NodeTraverser & cb) const53 void MemberExpression::Iterate(const NodeTraverser &cb) const
54 {
55 cb(object_);
56 cb(property_);
57 }
58
Dump(ir::AstDumper * dumper) const59 void MemberExpression::Dump(ir::AstDumper *dumper) const
60 {
61 dumper->Add({{"type", "MemberExpression"},
62 {"object", object_},
63 {"property", property_},
64 {"computed", computed_},
65 {"optional", IsOptional()}});
66 }
67
Dump(ir::SrcDumper * dumper) const68 void MemberExpression::Dump(ir::SrcDumper *dumper) const
69 {
70 ASSERT(object_ != nullptr);
71 ASSERT(property_ != nullptr);
72
73 object_->Dump(dumper);
74 if (IsOptional()) {
75 dumper->Add("?");
76 if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
77 dumper->Add(".");
78 }
79 }
80 if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
81 dumper->Add("[");
82 property_->Dump(dumper);
83 dumper->Add("]");
84 } else {
85 dumper->Add(".");
86 property_->Dump(dumper);
87 }
88 if ((parent_ != nullptr) && (parent_->IsBlockStatement() || parent_->IsBlockExpression())) {
89 dumper->Add(";");
90 dumper->Endl();
91 }
92 }
93
LoadRhs(compiler::PandaGen * pg) const94 void MemberExpression::LoadRhs(compiler::PandaGen *pg) const
95 {
96 compiler::RegScope rs(pg);
97 bool isSuper = object_->IsSuperExpression();
98 compiler::Operand prop = pg->ToPropertyKey(property_, computed_, isSuper);
99
100 if (isSuper) {
101 pg->LoadSuperProperty(this, prop);
102 } else if (IsPrivateReference()) {
103 const auto &name = property_->AsIdentifier()->Name();
104 compiler::VReg objReg = pg->AllocReg();
105 pg->StoreAccumulator(this, objReg);
106 compiler::VReg ctor = pg->AllocReg();
107 compiler::Function::LoadClassContexts(this, pg, ctor, name);
108 pg->ClassPrivateFieldGet(this, ctor, objReg, name);
109 } else {
110 pg->LoadObjProperty(this, prop);
111 }
112 }
113
CompileToRegs(compiler::PandaGen * pg,compiler::VReg object,compiler::VReg property) const114 void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const
115 {
116 object_->Compile(pg);
117 pg->StoreAccumulator(this, object);
118
119 pg->OptionalChainCheck(IsOptional(), object);
120
121 if (!computed_) {
122 pg->LoadAccumulatorString(this, property_->AsIdentifier()->Name());
123 } else {
124 property_->Compile(pg);
125 }
126
127 pg->StoreAccumulator(this, property);
128 }
129
Compile(compiler::PandaGen * pg) const130 void MemberExpression::Compile(compiler::PandaGen *pg) const
131 {
132 pg->GetAstCompiler()->Compile(this);
133 }
134
CompileToReg(compiler::PandaGen * pg,compiler::VReg objReg) const135 void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg objReg) const
136 {
137 object_->Compile(pg);
138 pg->StoreAccumulator(this, objReg);
139 pg->OptionalChainCheck(IsOptional(), objReg);
140 LoadRhs(pg);
141 }
142
Compile(compiler::ETSGen * etsg) const143 void MemberExpression::Compile(compiler::ETSGen *etsg) const
144 {
145 etsg->GetAstCompiler()->Compile(this);
146 }
147
Check(checker::TSChecker * checker)148 checker::Type *MemberExpression::Check(checker::TSChecker *checker)
149 {
150 return checker->GetAnalyzer()->Check(this);
151 }
152
GetEnumMethodVariable(checker::ETSEnumType const * const enumInterface,const util::StringView propName)153 static varbinder::LocalVariable *GetEnumMethodVariable(checker::ETSEnumType const *const enumInterface,
154 const util::StringView propName)
155 {
156 varbinder::LocalVariable *methodVar = nullptr;
157
158 const auto *const boxedClass = enumInterface->GetDecl()->BoxedClass();
159 ASSERT(boxedClass->TsType()->IsETSObjectType());
160 const auto *const obj = boxedClass->TsType()->AsETSObjectType();
161
162 std::string_view methodName = propName.Utf8();
163 if (enumInterface->IsETSStringEnumType() && (propName == checker::ETSEnumType::VALUE_OF_METHOD_NAME)) {
164 // For string enums valueOf method calls toString method
165 methodName = checker::ETSEnumType::TO_STRING_METHOD_NAME;
166 }
167
168 const auto searchFlags =
169 checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
170 methodVar = obj->GetProperty(methodName, searchFlags);
171
172 return methodVar;
173 }
174
ResolveEnumMember(checker::ETSChecker * checker,checker::ETSEnumType * type) const175 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveEnumMember(
176 checker::ETSChecker *checker, checker::ETSEnumType *type) const
177 {
178 if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) {
179 auto *const memberType = type->LookupMethod(checker, object_, property_->AsIdentifier());
180 varbinder::LocalVariable *const memberVar = GetEnumMethodVariable(type, property_->AsIdentifier()->Name());
181 return {memberType, memberVar};
182 }
183
184 auto *const literalType = type->LookupConstant(checker, object_, property_->AsIdentifier());
185 if (literalType == nullptr) {
186 return {nullptr, nullptr};
187 }
188 return {literalType, literalType->GetMemberVar()};
189 }
190
ResolveObjectMember(checker::ETSChecker * checker) const191 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
192 checker::ETSChecker *checker) const
193 {
194 auto resolveRes = checker->ResolveMemberReference(this, objType_);
195 switch (resolveRes.size()) {
196 case 0U: {
197 /* resolution failed, error already reported */
198 return {nullptr, nullptr};
199 }
200 case 1U: {
201 if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
202 auto var = resolveRes[0]->Variable()->AsLocalVariable();
203 checker->ValidatePropertyAccess(var, objType_, property_->Start());
204 return {checker->GetTypeOfVariable(var), var};
205 }
206 return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
207 }
208 case 2U: {
209 auto classMethodType = checker->GetTypeOfVariable(resolveRes[1]->Variable());
210 auto extensionMethodType = checker->GetTypeOfVariable(resolveRes[0]->Variable());
211 auto *resolvedType = extensionMethodType;
212 if (classMethodType->IsETSFunctionType()) {
213 ASSERT(extensionMethodType->IsETSFunctionType());
214 resolvedType = checker->CreateETSExtensionFuncHelperType(classMethodType->AsETSFunctionType(),
215 extensionMethodType->AsETSFunctionType());
216 }
217 return {resolvedType, nullptr};
218 }
219 default: {
220 UNREACHABLE();
221 }
222 }
223 }
224
TraverseUnionMember(checker::ETSChecker * checker,checker::ETSUnionType * unionType,checker::Type * commonPropType)225 checker::Type *MemberExpression::TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType,
226 checker::Type *commonPropType)
227
228 {
229 auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
230 if (commonPropType != nullptr && commonPropType != memberType) {
231 checker->LogTypeError("Member type must be the same for all union objects.", Start());
232 } else {
233 commonPropType = memberType;
234 }
235 };
236 for (auto *const type : unionType->ConstituentTypes()) {
237 auto *const apparent = checker->GetApparentType(type);
238 if (apparent->IsETSObjectType()) {
239 SetObjectType(apparent->AsETSObjectType());
240 addPropType(ResolveObjectMember(checker).first);
241 } else {
242 checker->LogTypeError({"Type ", unionType, " is illegal in union member expression."}, Start());
243 }
244 }
245 return commonPropType;
246 }
247
CheckUnionMember(checker::ETSChecker * checker,checker::Type * baseType)248 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
249 {
250 auto *const unionType = baseType->AsETSUnionType();
251 auto *const commonPropType = TraverseUnionMember(checker, unionType, nullptr);
252 SetObjectType(checker->GlobalETSObjectType());
253 return commonPropType;
254 }
255
AdjustType(checker::ETSChecker * checker,checker::Type * type)256 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
257 {
258 auto *const objType = checker->GetApparentType(Object()->TsType());
259 if (PropVar() != nullptr) { // access erased property type
260 uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
261 } else if (IsComputed() && objType->IsETSArrayType()) { // access erased array or tuple type
262 uncheckedType_ = checker->GuaranteedTypeForUncheckedCast(objType->AsETSArrayType()->ElementType(), type);
263 }
264 SetTsType(type == nullptr ? checker->GlobalTypeError() : type);
265 return TsType();
266 }
267
SetAndAdjustType(checker::ETSChecker * checker,checker::ETSObjectType * objectType)268 checker::Type *MemberExpression::SetAndAdjustType(checker::ETSChecker *checker, checker::ETSObjectType *objectType)
269 {
270 SetObjectType(objectType);
271 auto [resType, resVar] = ResolveObjectMember(checker);
272 if (resType == nullptr) {
273 SetTsType(checker->GlobalTypeError());
274 return checker->GlobalTypeError();
275 }
276 SetPropVar(resVar);
277 return AdjustType(checker, resType);
278 }
279
CheckArrayIndexValue(checker::ETSChecker * checker) const280 bool MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
281 {
282 std::size_t index;
283
284 auto const &number = property_->AsNumberLiteral()->Number();
285
286 if (number.IsInteger()) {
287 auto const value = number.GetLong();
288 if (value < 0) {
289 checker->LogTypeError("Index value cannot be less than zero.", property_->Start());
290 return false;
291 }
292 index = static_cast<std::size_t>(value);
293 } else if (number.IsReal()) {
294 double value = number.GetDouble();
295 double fraction = std::modf(value, &value);
296 if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
297 checker->LogTypeError("Index value cannot be less than zero or fractional.", property_->Start());
298 return false;
299 }
300 index = static_cast<std::size_t>(value);
301 } else {
302 UNREACHABLE();
303 }
304
305 if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
306 checker->LogTypeError("Index value cannot be greater than or equal to the array size.", property_->Start());
307 return false;
308 }
309
310 return true;
311 }
312
CheckIndexAccessMethod(checker::ETSChecker * checker)313 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
314 {
315 checker::PropertySearchFlags searchFlag =
316 checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL;
317 searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
318 // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
319
320 if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
321 searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
322 }
323
324 bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
325 std::string_view const methodName =
326 isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
327
328 auto *const method = objType_->GetProperty(methodName, searchFlag);
329 if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
330 checker->LogTypeError("Object type doesn't have proper index access method.", Start());
331 return nullptr;
332 }
333
334 ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()};
335 arguments.emplace_back(property_);
336 if (isSetter) {
337 // Temporary change the parent of right assignment node to check if correct "$_set" function presents.
338 // later on in lowering the entire assignment expression will be replace top the call to that method.
339 auto *value = Parent()->AsAssignmentExpression()->Right();
340 value->SetParent(this);
341 arguments.emplace_back(value);
342 }
343
344 auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
345
346 checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing",
347 checker::TypeRelationFlag::NO_THROW);
348 if (signature == nullptr) {
349 checker->LogTypeError("Cannot find index access method with the required signature.", Property()->Start());
350 return nullptr;
351 }
352 checker->ValidateSignatureAccessibility(objType_, nullptr, signature, Start(),
353 "Index access method is not visible here.");
354
355 ASSERT(signature->Function() != nullptr);
356
357 if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
358 checker->CheckThrowingStatements(this);
359 }
360
361 if (isSetter) {
362 // Restore the right assignment node's parent to keep AST invariant valid.
363 Parent()->AsAssignmentExpression()->Right()->SetParent(Parent());
364 return signature->Params()[1]->TsType();
365 }
366
367 return signature->ReturnType();
368 }
369
CheckTupleAccessMethod(checker::ETSChecker * checker,checker::Type * baseType)370 checker::Type *MemberExpression::CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)
371 {
372 ASSERT(baseType->IsETSTupleType());
373
374 auto idxIfAny = checker->GetTupleElementAccessValue(Property()->TsType(), Property()->Start());
375 if (!idxIfAny.has_value()) {
376 return nullptr;
377 }
378 auto *const tupleTypeAtIdx = baseType->AsETSTupleType()->GetTypeAtIndex(*idxIfAny);
379
380 if ((!Parent()->IsAssignmentExpression() || Parent()->AsAssignmentExpression()->Left() != this) &&
381 (!Parent()->IsUpdateExpression())) {
382 // Error never should be thrown by this call, because LUB of types can be converted to any type which
383 // LUB was calculated by casting
384 const checker::CastingContext cast(
385 checker->Relation(), {"Tuple type couldn't be converted "},
386 checker::CastingContext::ConstructorData {this, baseType->AsETSArrayType()->ElementType(), tupleTypeAtIdx,
387 Start()});
388 }
389
390 return tupleTypeAtIdx;
391 }
392
CheckComputed(checker::ETSChecker * checker,checker::Type * baseType)393 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
394 {
395 if (baseType->IsETSDynamicType()) {
396 checker->ValidateArrayIndex(property_);
397 return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
398 }
399
400 if (baseType->IsETSArrayType()) {
401 auto *dflt = baseType->AsETSArrayType()->ElementType();
402 if (!baseType->IsETSTupleType() && !checker->ValidateArrayIndex(property_)) {
403 // error already reported to log
404 return dflt;
405 }
406
407 if (baseType->IsETSTupleType() && !checker->ValidateTupleIndex(baseType->AsETSTupleType(), this)) {
408 // error reported to log
409 return dflt;
410 }
411
412 // Check if the index value is inside array bounds if it is defined explicitly
413 if (property_->IsNumberLiteral() && !CheckArrayIndexValue(checker)) {
414 // error reported to log
415 return dflt;
416 }
417
418 // NOTE: apply capture conversion on this type
419 if (baseType->IsETSTupleType()) {
420 auto *res = CheckTupleAccessMethod(checker, baseType);
421 return (res == nullptr) ? dflt : res;
422 }
423
424 return dflt;
425 }
426
427 if (baseType->IsETSObjectType()) {
428 SetObjectType(baseType->AsETSObjectType());
429 return CheckIndexAccessMethod(checker);
430 }
431 // NOTE(vpukhov): #20510 lowering
432 if (baseType->IsETSEnumType()) {
433 property_->Check(checker);
434 if (property_->TsType()->IsETSEnumType()) {
435 AddAstNodeFlags(ir::AstNodeFlags::GENERATE_GET_NAME);
436 return checker->GlobalBuiltinETSStringType();
437 }
438 }
439 checker->LogTypeError("Indexed access is not supported for such expression type.", Object()->Start());
440 return nullptr;
441 }
442
Check(checker::ETSChecker * checker)443 checker::Type *MemberExpression::Check(checker::ETSChecker *checker)
444 {
445 return checker->GetAnalyzer()->Check(this);
446 }
447
Clone(ArenaAllocator * const allocator,AstNode * const parent)448 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
449 {
450 if (auto *const clone = allocator->New<MemberExpression>(Tag {}, *this, allocator); clone != nullptr) {
451 if (parent != nullptr) {
452 clone->SetParent(parent);
453 }
454
455 clone->SetRange(Range());
456 return clone;
457 }
458
459 throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
460 }
461
462 } // namespace ark::es2panda::ir
463