1 /*
2 * Copyright (c) 2022 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 "ecmascript/compiler/type_recorder.h"
17
18 #include "ecmascript/jspandafile/literal_data_extractor.h"
19 #include "ecmascript/ts_types/ts_type_parser.h"
20
21 #include "libpandafile/method_data_accessor-inl.h"
22
23 namespace panda::ecmascript::kungfu {
TypeRecorder(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,TSManager * tsManager,const CString & recordName)24 TypeRecorder::TypeRecorder(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral,
25 TSManager *tsManager, const CString &recordName)
26 : argTypes_(methodLiteral->GetNumArgsWithCallField() + static_cast<size_t>(TypedArgIdx::NUM_OF_TYPED_ARGS),
27 GateType::AnyType())
28 {
29 if (!jsPandaFile->HasTSTypes(recordName)) {
30 return;
31 }
32 LoadTypes(jsPandaFile, methodLiteral, tsManager, recordName);
33 tsManager->GenerateTSHClasses();
34 }
35
LoadTypes(const JSPandaFile * jsPandaFile,const MethodLiteral * methodLiteral,TSManager * tsManager,const CString & recordName)36 void TypeRecorder::LoadTypes(const JSPandaFile *jsPandaFile, const MethodLiteral *methodLiteral,
37 TSManager *tsManager, const CString &recordName)
38 {
39 JSThread *thread = tsManager->GetThread();
40 TSTypeParser typeParser(tsManager);
41 const panda_file::File *pf = jsPandaFile->GetPandaFile();
42 panda_file::File::EntityId fieldId = methodLiteral->GetMethodId();
43 panda_file::MethodDataAccessor mda(*pf, fieldId);
44 mda.EnumerateAnnotations([&](panda_file::File::EntityId annotation_id) {
45 panda_file::AnnotationDataAccessor ada(*pf, annotation_id);
46 auto *annotationName = reinterpret_cast<const char *>(pf->GetStringData(ada.GetClassId()).data);
47 ASSERT(annotationName != nullptr);
48 if (::strcmp("L_ESTypeAnnotation;", annotationName) != 0) {
49 return;
50 }
51 uint32_t length = ada.GetCount();
52 for (uint32_t i = 0; i < length; i++) {
53 panda_file::AnnotationDataAccessor::Elem adae = ada.GetElement(i);
54 auto *elemName = reinterpret_cast<const char *>(pf->GetStringData(adae.GetNameId()).data);
55 ASSERT(elemName != nullptr);
56 if (::strcmp("_TypeOfInstruction", elemName) != 0) {
57 continue;
58 }
59
60 panda_file::ScalarValue sv = adae.GetScalarValue();
61 panda_file::File::EntityId literalOffset(sv.GetValue());
62 JSHandle<TaggedArray> typeOfInstruction =
63 LiteralDataExtractor::GetTypeLiteral(thread, jsPandaFile, literalOffset);
64
65 GlobalTSTypeRef thisGT = GlobalTSTypeRef::Default();
66 GlobalTSTypeRef funcGT = GlobalTSTypeRef::Default();
67 for (uint32_t j = 0; j < typeOfInstruction->GetLength(); j = j + 2) { // + 2 means bcOffset and typeId
68 int32_t bcOffset = typeOfInstruction->Get(j).GetInt();
69 uint32_t typeId = static_cast<uint32_t>(typeOfInstruction->Get(j + 1).GetInt());
70 GlobalTSTypeRef gt = typeParser.CreateGT(jsPandaFile, recordName, typeId);
71 if (gt.IsDefault()) {
72 continue;
73 }
74
75 // The type of a function is recorded as (-1, funcTypeId). If the function is a member of a class,
76 // the type of the class or its instance is is recorded as (-2, classTypeId). If it is a static
77 // member, the type id refers to the type of the class; otherwise, it links to the type of the
78 // instances of the class.
79 if (bcOffset == METHOD_ANNOTATION_THIS_TYPE_OFFSET) {
80 thisGT = gt;
81 continue;
82 }
83 if (bcOffset == METHOD_ANNOTATION_FUNCTION_TYPE_OFFSET) {
84 tsManager->SetFuncMethodOffset(gt, methodLiteral->GetMethodId().GetOffset());
85 funcGT = gt;
86 continue;
87 }
88 auto type = GateType(gt);
89 bcOffsetGtMap_.emplace(bcOffset, type);
90 }
91 LoadArgTypes(tsManager, funcGT, thisGT);
92 }
93 });
94 }
95
LoadArgTypes(const TSManager * tsManager,GlobalTSTypeRef funcGT,GlobalTSTypeRef thisGT)96 void TypeRecorder::LoadArgTypes(const TSManager *tsManager, GlobalTSTypeRef funcGT, GlobalTSTypeRef thisGT)
97 {
98 argTypes_[static_cast<size_t>(TypedArgIdx::FUNC)] = TryGetFuncType(funcGT);
99 argTypes_[static_cast<size_t>(TypedArgIdx::NEW_TARGET)] = TryGetNewTargetType(tsManager, thisGT);
100 argTypes_[static_cast<size_t>(TypedArgIdx::THIS_OBJECT)] = TryGetThisType(tsManager, funcGT, thisGT);
101
102 if (funcGT.IsDefault()) {
103 return;
104 }
105 size_t extraParasNum = static_cast<size_t>(TypedArgIdx::NUM_OF_TYPED_ARGS);
106 uint32_t numExplicitArgs = tsManager->GetFunctionTypeLength(funcGT);
107 for (uint32_t explicitArgId = 0; explicitArgId < numExplicitArgs; explicitArgId++) {
108 argTypes_[extraParasNum++] = GateType(tsManager->GetFuncParameterTypeGT(funcGT, explicitArgId));
109 }
110 }
111
TryGetThisType(const TSManager * tsManager,GlobalTSTypeRef funcGT,GlobalTSTypeRef thisGT) const112 GateType TypeRecorder::TryGetThisType(const TSManager *tsManager, GlobalTSTypeRef funcGT, GlobalTSTypeRef thisGT) const
113 {
114 // The parameter 'this' may be declared explicitly, e.g. foo(this: Person, num: number). In this case, the type
115 // of 'this' is recorded in the type of the function. And this type is preferred over the type derived from
116 // 'thisGT' if both are given.
117 if (!funcGT.IsDefault()) {
118 auto gt = tsManager->GetFuncThisGT(funcGT);
119 if (!gt.IsDefault()) {
120 return GateType(gt);
121 }
122 }
123 return GateType(thisGT);
124 }
125
TryGetNewTargetType(const TSManager * tsManager,GlobalTSTypeRef thisGT) const126 GateType TypeRecorder::TryGetNewTargetType(const TSManager *tsManager, GlobalTSTypeRef thisGT) const
127 {
128 if (thisGT.IsDefault()) {
129 return GateType::AnyType();
130 }
131
132 GateType thisType(thisGT);
133 if (tsManager->IsClassInstanceTypeKind(thisType)) {
134 return GateType(tsManager->GetClassType(thisGT));
135 } else {
136 return thisType;
137 }
138 }
139
TryGetFuncType(GlobalTSTypeRef funcGT) const140 GateType TypeRecorder::TryGetFuncType(GlobalTSTypeRef funcGT) const
141 {
142 if (funcGT.IsDefault()) {
143 return GateType::AnyType();
144 }
145 return GateType(funcGT);
146 }
147
GetType(const int32_t offset) const148 GateType TypeRecorder::GetType(const int32_t offset) const
149 {
150 if (bcOffsetGtMap_.find(offset) != bcOffsetGtMap_.end()) {
151 return bcOffsetGtMap_.at(offset);
152 }
153 return GateType::AnyType();
154 }
155
GetArgType(const uint32_t argIndex) const156 GateType TypeRecorder::GetArgType(const uint32_t argIndex) const
157 {
158 ASSERT(argIndex < argTypes_.size());
159 return argTypes_[argIndex];
160 }
161
UpdateType(const int32_t offset,const GateType & type) const162 GateType TypeRecorder::UpdateType(const int32_t offset, const GateType &type) const
163 {
164 auto tempType = GetType(offset);
165 if (!tempType.IsAnyType()) {
166 ASSERT(type.IsAnyType());
167 return tempType;
168 }
169 return type;
170 }
171 } // namespace panda::ecmascript
172