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 }
77 if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
78 dumper->Add("[");
79 property_->Dump(dumper);
80 dumper->Add("]");
81 } else {
82 dumper->Add(".");
83 property_->Dump(dumper);
84 }
85 if ((parent_ != nullptr) && (parent_->IsBlockStatement() || parent_->IsBlockExpression())) {
86 dumper->Add(";");
87 dumper->Endl();
88 }
89 }
90
LoadRhs(compiler::PandaGen * pg) const91 void MemberExpression::LoadRhs(compiler::PandaGen *pg) const
92 {
93 compiler::RegScope rs(pg);
94 bool isSuper = object_->IsSuperExpression();
95 compiler::Operand prop = pg->ToPropertyKey(property_, computed_, isSuper);
96
97 if (isSuper) {
98 pg->LoadSuperProperty(this, prop);
99 } else if (IsPrivateReference()) {
100 const auto &name = property_->AsIdentifier()->Name();
101 compiler::VReg objReg = pg->AllocReg();
102 pg->StoreAccumulator(this, objReg);
103 compiler::VReg ctor = pg->AllocReg();
104 compiler::Function::LoadClassContexts(this, pg, ctor, name);
105 pg->ClassPrivateFieldGet(this, ctor, objReg, name);
106 } else {
107 pg->LoadObjProperty(this, prop);
108 }
109 }
110
CompileToRegs(compiler::PandaGen * pg,compiler::VReg object,compiler::VReg property) const111 void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const
112 {
113 object_->Compile(pg);
114 pg->StoreAccumulator(this, object);
115
116 pg->OptionalChainCheck(IsOptional(), object);
117
118 if (!computed_) {
119 pg->LoadAccumulatorString(this, property_->AsIdentifier()->Name());
120 } else {
121 property_->Compile(pg);
122 }
123
124 pg->StoreAccumulator(this, property);
125 }
126
Compile(compiler::PandaGen * pg) const127 void MemberExpression::Compile(compiler::PandaGen *pg) const
128 {
129 pg->GetAstCompiler()->Compile(this);
130 }
131
CompileToReg(compiler::PandaGen * pg,compiler::VReg objReg) const132 void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg objReg) const
133 {
134 object_->Compile(pg);
135 pg->StoreAccumulator(this, objReg);
136 pg->OptionalChainCheck(IsOptional(), objReg);
137 LoadRhs(pg);
138 }
139
Compile(compiler::ETSGen * etsg) const140 void MemberExpression::Compile(compiler::ETSGen *etsg) const
141 {
142 etsg->GetAstCompiler()->Compile(this);
143 }
144
Check(checker::TSChecker * checker)145 checker::Type *MemberExpression::Check(checker::TSChecker *checker)
146 {
147 return checker->GetAnalyzer()->Check(this);
148 }
149
ResolveEnumMember(checker::ETSChecker * checker,checker::Type * type) const150 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveEnumMember(checker::ETSChecker *checker,
151 checker::Type *type) const
152 {
153 auto const *const enumInterface = [type]() -> checker::ETSEnumType const * {
154 if (type->IsETSIntEnumType()) {
155 return type->AsETSIntEnumType();
156 }
157 return type->AsETSStringEnumType();
158 }();
159
160 if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) {
161 return {enumInterface->LookupMethod(checker, object_, property_->AsIdentifier()), nullptr};
162 }
163
164 auto *const literalType = enumInterface->LookupConstant(checker, object_, property_->AsIdentifier());
165 return {literalType, literalType->GetMemberVar()};
166 }
167
ResolveObjectMember(checker::ETSChecker * checker) const168 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
169 checker::ETSChecker *checker) const
170 {
171 auto resolveRes = checker->ResolveMemberReference(this, objType_);
172 switch (resolveRes.size()) {
173 case 0U: {
174 /* resolution failed, error already reported */
175 return {nullptr, nullptr};
176 }
177 case 1U: {
178 if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
179 auto var = resolveRes[0]->Variable()->AsLocalVariable();
180 checker->ValidatePropertyAccess(var, objType_, property_->Start());
181 return {checker->GetTypeOfVariable(var), var};
182 }
183 return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
184 }
185 case 2U: {
186 auto classMethodType = checker->GetTypeOfVariable(resolveRes[1]->Variable());
187 auto extensionMethodType = checker->GetTypeOfVariable(resolveRes[0]->Variable());
188 auto *resolvedType = extensionMethodType;
189 if (classMethodType->IsETSFunctionType()) {
190 ASSERT(extensionMethodType->IsETSFunctionType());
191 resolvedType = checker->CreateETSExtensionFuncHelperType(classMethodType->AsETSFunctionType(),
192 extensionMethodType->AsETSFunctionType());
193 }
194 return {resolvedType, nullptr};
195 }
196 default: {
197 UNREACHABLE();
198 }
199 }
200 }
201
TraverseUnionMember(checker::ETSChecker * checker,checker::ETSUnionType * unionType,checker::Type * commonPropType)202 checker::Type *MemberExpression::TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType,
203 checker::Type *commonPropType)
204
205 {
206 auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
207 if (commonPropType != nullptr && commonPropType != memberType) {
208 checker->ThrowTypeError("Member type must be the same for all union objects.", Start());
209 }
210 commonPropType = memberType;
211 };
212 for (auto *const type : unionType->ConstituentTypes()) {
213 auto *const apparent = checker->GetApparentType(type);
214 if (apparent->IsETSObjectType()) {
215 SetObjectType(apparent->AsETSObjectType());
216 addPropType(ResolveObjectMember(checker).first);
217 } else if (apparent->IsETSEnumType()) {
218 addPropType(ResolveEnumMember(checker, apparent).first);
219 } else {
220 checker->ThrowTypeError({"Type ", unionType, " is illegal in union member expression."}, Start());
221 }
222 }
223 return commonPropType;
224 }
225
CheckUnionMember(checker::ETSChecker * checker,checker::Type * baseType)226 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
227 {
228 auto *const unionType = baseType->AsETSUnionType();
229 auto *const commonPropType = TraverseUnionMember(checker, unionType, nullptr);
230 SetObjectType(checker->GlobalETSObjectType());
231 return commonPropType;
232 }
233
AdjustType(checker::ETSChecker * checker,checker::Type * type)234 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
235 {
236 auto *const objType = checker->GetApparentType(Object()->TsType());
237 if (PropVar() != nullptr) { // access erased property type
238 uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
239 } else if (IsComputed() && objType->IsETSArrayType()) { // access erased array or tuple type
240 uncheckedType_ = checker->GuaranteedTypeForUncheckedCast(objType->AsETSArrayType()->ElementType(), type);
241 }
242 SetTsType(type);
243 return TsType();
244 }
245
CheckArrayIndexValue(checker::ETSChecker * checker) const246 bool MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
247 {
248 std::size_t index;
249
250 auto const &number = property_->AsNumberLiteral()->Number();
251
252 if (number.IsInteger()) {
253 auto const value = number.GetLong();
254 if (value < 0) {
255 checker->LogTypeError("Index value cannot be less than zero.", property_->Start());
256 return false;
257 }
258 index = static_cast<std::size_t>(value);
259 } else if (number.IsReal()) {
260 double value = number.GetDouble();
261 double fraction = std::modf(value, &value);
262 if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
263 checker->LogTypeError("Index value cannot be less than zero or fractional.", property_->Start());
264 return false;
265 }
266 index = static_cast<std::size_t>(value);
267 } else {
268 UNREACHABLE();
269 }
270
271 if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
272 checker->LogTypeError("Index value cannot be greater than or equal to the array size.", property_->Start());
273 return false;
274 }
275
276 return true;
277 }
278
CheckIndexAccessMethod(checker::ETSChecker * checker)279 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
280 {
281 checker::PropertySearchFlags searchFlag =
282 checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL;
283 searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
284 // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
285
286 if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
287 searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
288 }
289
290 bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
291 std::string_view const methodName =
292 isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
293
294 auto *const method = objType_->GetProperty(methodName, searchFlag);
295 if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
296 checker->ThrowTypeError("Object type doesn't have proper index access method.", Start());
297 }
298
299 ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()};
300 arguments.emplace_back(property_);
301 if (isSetter) {
302 // Temporary change the parent of right assignment node to check if correct "$_set" function presents.
303 // later on in lowering the entire assignment expression will be replace top the call to that method.
304 auto *value = Parent()->AsAssignmentExpression()->Right();
305 value->SetParent(this);
306 arguments.emplace_back(value);
307 }
308
309 auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
310
311 checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing",
312 checker::TypeRelationFlag::NO_THROW);
313 if (signature == nullptr) {
314 checker->ThrowTypeError("Cannot find index access method with the required signature.", Property()->Start());
315 }
316 checker->ValidateSignatureAccessibility(objType_, nullptr, signature, Start(),
317 "Index access method is not visible here.");
318
319 ASSERT(signature->Function() != nullptr);
320
321 if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
322 checker->CheckThrowingStatements(this);
323 }
324
325 if (isSetter) {
326 // Restore the right assignment node's parent to keep AST invariant valid.
327 Parent()->AsAssignmentExpression()->Right()->SetParent(Parent());
328 return signature->Params()[1]->TsType();
329 }
330
331 return signature->ReturnType();
332 }
333
CheckTupleAccessMethod(checker::ETSChecker * checker,checker::Type * baseType)334 checker::Type *MemberExpression::CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)
335 {
336 ASSERT(baseType->IsETSTupleType());
337
338 auto idxIfAny = checker->GetTupleElementAccessValue(Property()->TsType(), Property()->Start());
339 if (!idxIfAny.has_value()) {
340 return nullptr;
341 }
342 auto *const tupleTypeAtIdx = baseType->AsETSTupleType()->GetTypeAtIndex(*idxIfAny);
343
344 if ((!Parent()->IsAssignmentExpression() || Parent()->AsAssignmentExpression()->Left() != this) &&
345 (!Parent()->IsUpdateExpression())) {
346 // Error never should be thrown by this call, because LUB of types can be converted to any type which
347 // LUB was calculated by casting
348 const checker::CastingContext cast(
349 checker->Relation(), {"Tuple type couldn't be converted "},
350 checker::CastingContext::ConstructorData {this, baseType->AsETSArrayType()->ElementType(), tupleTypeAtIdx,
351 Start()});
352 }
353
354 return tupleTypeAtIdx;
355 }
356
CheckComputed(checker::ETSChecker * checker,checker::Type * baseType)357 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
358 {
359 if (baseType->IsETSDynamicType()) {
360 checker->ValidateArrayIndex(property_);
361 return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
362 }
363
364 if (baseType->IsETSArrayType()) {
365 auto *dflt = baseType->AsETSArrayType()->ElementType();
366 if (!baseType->IsETSTupleType() && !checker->ValidateArrayIndex(property_)) {
367 // error already reported to log
368 return dflt;
369 }
370
371 if (baseType->IsETSTupleType() && !checker->ValidateTupleIndex(baseType->AsETSTupleType(), this)) {
372 // error reported to log
373 return dflt;
374 }
375
376 // Check if the index value is inside array bounds if it is defined explicitly
377 if (property_->IsNumberLiteral() && !CheckArrayIndexValue(checker)) {
378 // error reported to log
379 return dflt;
380 }
381
382 // NOTE: apply capture conversion on this type
383 if (baseType->IsETSTupleType()) {
384 auto *res = CheckTupleAccessMethod(checker, baseType);
385 return (res == nullptr) ? dflt : res;
386 }
387
388 return dflt;
389 }
390
391 if (baseType->IsETSObjectType()) {
392 SetObjectType(baseType->AsETSObjectType());
393 return CheckIndexAccessMethod(checker);
394 }
395 if ((baseType->IsETSEnumType()) && (kind_ == MemberExpressionKind::ELEMENT_ACCESS)) {
396 property_->Check(checker);
397 if (property_->TsType()->IsETSEnumType()) {
398 AddAstNodeFlags(ir::AstNodeFlags::GENERATE_GET_NAME);
399 return checker->GlobalBuiltinETSStringType();
400 }
401 }
402 checker->ThrowTypeError("Indexed access is not supported for such expression type.", Object()->Start());
403 }
404
Check(checker::ETSChecker * checker)405 checker::Type *MemberExpression::Check(checker::ETSChecker *checker)
406 {
407 return checker->GetAnalyzer()->Check(this);
408 }
409
Clone(ArenaAllocator * const allocator,AstNode * const parent)410 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
411 {
412 if (auto *const clone = allocator->New<MemberExpression>(Tag {}, *this, allocator); clone != nullptr) {
413 if (parent != nullptr) {
414 clone->SetParent(parent);
415 }
416
417 clone->SetRange(Range());
418 return clone;
419 }
420
421 throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
422 }
423
424 } // namespace ark::es2panda::ir
425