1 /*
2 * Copyright (c) 2023 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_inference/initialization_analysis.h"
17 #include "ecmascript/ts_types/ts_type_accessor.h"
18
19 namespace panda::ecmascript::kungfu {
Run()20 void InitializationAnalysis::Run()
21 {
22 if (classType_.IsAnyType()) {
23 return;
24 }
25
26 CollectThisEscapedInfo(thisObject_);
27 std::vector<GateRef> gateList;
28 circuit_->GetAllGates(gateList);
29 for (const auto &gate : gateList) {
30 auto op = acc_.GetOpCode(gate);
31 if (op == OpCode::JS_BYTECODE) {
32 Analyse(gate);
33 }
34 }
35 MarkHasAnalysedInitialization();
36
37 if (IsLogEnabled()) {
38 Print();
39 }
40 }
41
Analyse(GateRef gate)42 void InitializationAnalysis::Analyse(GateRef gate)
43 {
44 ASSERT(acc_.GetOpCode(gate) == OpCode::JS_BYTECODE);
45 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
46 switch (ecmaOpcode) {
47 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
48 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8: {
49 CollectInitializationType(gate, ThisUsage::INDEFINITE_THIS);
50 CollectInitializationInfo(gate, ThisUsage::INDEFINITE_THIS);
51 break;
52 }
53 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
54 case EcmaOpcode::STTHISBYNAME_IMM16_ID16: {
55 CollectInitializationType(gate, ThisUsage::DEFINITE_THIS);
56 CollectInitializationInfo(gate, ThisUsage::DEFINITE_THIS);
57 break;
58 }
59 case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
60 case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
61 case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
62 case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
63 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8: {
64 MarkSuperClass();
65 CollectThisEscapedInfo(gate);
66 break;
67 }
68 default: {
69 break;
70 }
71 }
72 }
73
CollectInitializationType(GateRef gate,ThisUsage thisUsage)74 void InitializationAnalysis::CollectInitializationType(GateRef gate, ThisUsage thisUsage)
75 {
76 GateRef value = acc_.GetValueIn(gate, 3); // 3: index of value
77 GateType valueType = acc_.GetGateType(value);
78 if (valueType.IsAnyType()) {
79 return;
80 }
81 if (thisUsage == ThisUsage::INDEFINITE_THIS) {
82 GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver
83 GateType receiverType = acc_.GetGateType(receiver);
84 auto receiverGT = receiverType.GetGTRef();
85 if (tsManager_->IsClassInstanceTypeKind(receiverType)) {
86 receiverGT = tsManager_->GetClassType(receiverType);
87 }
88 if (!CheckIsThisObject(receiver) && receiverGT != classType_.GetGTRef()) {
89 return;
90 }
91 }
92
93 uint16_t index = acc_.GetConstantValue(acc_.GetValueIn(gate, 1)); // 1: stringId
94 auto methodOffset = acc_.TryGetMethodOffset(gate);
95 JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(methodOffset, index);
96
97 if (valueType.IsNumberType()) {
98 valueType = GateType::NumberType();
99 }
100
101 TSTypeAccessor typeAccessor(tsManager_, classType_);
102 typeAccessor.UpdateNonStaticProp(propKey, valueType.GetGTRef());
103 }
104
CollectInitializationInfo(GateRef gate,ThisUsage thisUsage)105 void InitializationAnalysis::CollectInitializationInfo(GateRef gate, ThisUsage thisUsage)
106 {
107 if (thisUsage == ThisUsage::INDEFINITE_THIS) {
108 GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver
109 if (!CheckIsThisObject(receiver)) {
110 return;
111 }
112 }
113
114 uint16_t index = acc_.GetConstantValue(acc_.GetValueIn(gate, 1)); // 1: stringId
115 if (!CheckSimpleCFG(gate, index)) {
116 return;
117 }
118 auto methodOffset = acc_.TryGetMethodOffset(gate);
119 JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(methodOffset, index);
120 TSTypeAccessor typeAccessor(tsManager_, classType_);
121 typeAccessor.MarkPropertyInitialized(propKey);
122 }
123
CheckIsThisObject(GateRef receiver) const124 bool InitializationAnalysis::CheckIsThisObject(GateRef receiver) const
125 {
126 return IsThisFromArg(receiver) || IsThisFromSuperCall(receiver);
127 }
128
IsThisFromSuperCall(GateRef gate) const129 bool InitializationAnalysis::IsThisFromSuperCall(GateRef gate) const
130 {
131 auto op = acc_.GetOpCode(gate);
132 if (op != OpCode::JS_BYTECODE) {
133 return false;
134 }
135 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
136 switch (ecmaOpcode) {
137 case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
138 case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
139 case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
140 case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
141 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8: {
142 return true;
143 }
144 default: {
145 break;
146 }
147 }
148 return false;
149 }
150
CheckSimpleCFG(GateRef gate,const uint16_t index) const151 bool InitializationAnalysis::CheckSimpleCFG(GateRef gate, const uint16_t index) const
152 {
153 while (!acc_.IsStateRoot(gate)) {
154 if (CheckSimpleGate(gate, index)) {
155 return false;
156 }
157 gate = acc_.GetState(gate);
158 }
159 return true;
160 }
161
CheckSimpleGate(GateRef gate,const uint16_t index) const162 bool InitializationAnalysis::CheckSimpleGate(GateRef gate, const uint16_t index) const
163 {
164 OpCode opCode = acc_.GetOpCode(gate);
165 switch (opCode) {
166 case OpCode::IF_TRUE:
167 case OpCode::IF_FALSE: {
168 return true;
169 }
170 case OpCode::JS_BYTECODE: {
171 return CheckSimpleJSGate(gate, index);
172 }
173 default: {
174 break;
175 }
176 }
177 return false;
178 }
179
CheckSimpleJSGate(GateRef gate,const uint16_t index) const180 bool InitializationAnalysis::CheckSimpleJSGate(GateRef gate, const uint16_t index) const
181 {
182 ASSERT(acc_.GetOpCode(gate) == OpCode::JS_BYTECODE);
183 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
184 switch (ecmaOpcode) {
185 case EcmaOpcode::LDOBJBYNAME_IMM8_ID16:
186 case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: {
187 return CheckLdObjByName(gate, index, ThisUsage::INDEFINITE_THIS);
188 }
189 case EcmaOpcode::LDTHISBYNAME_IMM8_ID16:
190 case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: {
191 return CheckLdObjByName(gate, index, ThisUsage::DEFINITE_THIS);
192 }
193 case EcmaOpcode::LDTHISBYVALUE_IMM8:
194 case EcmaOpcode::LDTHISBYVALUE_IMM16: {
195 return true;
196 }
197 case EcmaOpcode::LDOBJBYVALUE_IMM8_V8:
198 case EcmaOpcode::LDOBJBYVALUE_IMM16_V8:
199 case EcmaOpcode::LDOBJBYINDEX_IMM8_IMM16:
200 case EcmaOpcode::LDOBJBYINDEX_IMM16_IMM16:
201 case EcmaOpcode::WIDE_LDOBJBYINDEX_PREF_IMM32: {
202 return CheckLdObjByIndexOrValue(gate);
203 }
204 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
205 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
206 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
207 case EcmaOpcode::STTHISBYNAME_IMM16_ID16:
208 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM8:
209 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM16: {
210 return false;
211 }
212 default: {
213 return CheckThisAsValueIn(gate);
214 }
215 }
216 return false;
217 }
218
CheckLdObjByName(GateRef gate,const uint16_t index,ThisUsage thisUsage) const219 bool InitializationAnalysis::CheckLdObjByName(GateRef gate, const uint16_t index, ThisUsage thisUsage) const
220 {
221 if (thisUsage == ThisUsage::INDEFINITE_THIS) {
222 GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver
223 if (!CheckIsThisObject(receiver)) {
224 return false;
225 }
226 }
227
228 auto constData = acc_.GetValueIn(gate, 1); // 1: stringId
229 uint16_t stringId = acc_.GetConstantValue(constData);
230 return stringId == index;
231 }
232
CheckLdObjByIndexOrValue(GateRef gate) const233 bool InitializationAnalysis::CheckLdObjByIndexOrValue(GateRef gate) const
234 {
235 GateRef receiver = acc_.GetValueIn(gate, 1); // 1: index of receiver
236 return CheckIsThisObject(receiver);
237 }
238
MarkSuperClass()239 void InitializationAnalysis::MarkSuperClass()
240 {
241 TSTypeAccessor typeAccessor(tsManager_, classType_);
242 typeAccessor.MarkClassHasSuperCallInConstructor();
243 }
244
StoreThisObject()245 void InitializationAnalysis::StoreThisObject()
246 {
247 ArgumentAccessor argAcc(circuit_);
248 thisObject_ = argAcc.GetCommonArgGate(CommonArgIdx::THIS_OBJECT);
249 auto type = acc_.GetGateType(thisObject_);
250 if (tsManager_->IsClassInstanceTypeKind(type)) {
251 classType_ = GateType(tsManager_->GetClassType(type));
252 }
253 }
254
CheckThisAsValueIn(GateRef gate) const255 bool InitializationAnalysis::CheckThisAsValueIn(GateRef gate) const
256 {
257 ASSERT(acc_.GetOpCode(gate) == OpCode::JS_BYTECODE);
258 uint32_t numIns = acc_.GetNumValueIn(gate);
259 for (uint32_t i = 0; i < numIns; ++i) {
260 if (CheckIsThisObject(acc_.GetValueIn(gate, i))) {
261 return true;
262 }
263 }
264 return false;
265 }
266
CollectThisEscapedInfo(GateRef gate)267 void InitializationAnalysis::CollectThisEscapedInfo(GateRef gate)
268 {
269 ASSERT(gate != Circuit::NullGate());
270 std::vector<GateRef> valueUses;
271 acc_.GetValueUses(gate, valueUses);
272 for (const auto useGate : valueUses) {
273 if (!HasEscapedThis(useGate)) {
274 continue;
275 }
276 TSTypeAccessor typeAccessor(tsManager_, classType_);
277 typeAccessor.MarkClassHasEscapedThisInConstructor();
278 return;
279 }
280 }
281
HasEscapedThis(GateRef gate) const282 bool InitializationAnalysis::HasEscapedThis(GateRef gate) const
283 {
284 if (acc_.GetOpCode(gate) != OpCode::JS_BYTECODE) {
285 return false;
286 }
287 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
288 switch (ecmaOpcode) {
289 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
290 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
291 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
292 case EcmaOpcode::STTHISBYNAME_IMM16_ID16:
293 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM8:
294 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM16: {
295 return false;
296 }
297 default: {
298 break;
299 }
300 }
301 return true;
302 }
303
MarkHasAnalysedInitialization()304 void InitializationAnalysis::MarkHasAnalysedInitialization()
305 {
306 TSTypeAccessor typeAccessor(tsManager_, classType_);
307 typeAccessor.MarkClassHasAnalysedInitialization();
308 }
309
CheckCall() const310 bool InitializationAnalysis::CheckCall() const
311 {
312 TSTypeAccessor typeAccessor(tsManager_, classType_);
313 return typeAccessor.ClassHasEscapedThisInConstructor();
314 }
315
Print() const316 void InitializationAnalysis::Print() const
317 {
318 LOG_COMPILER(INFO) << " ";
319 LOG_COMPILER(INFO) << "\033[34m" << "================="
320 << " After initialization analysis "
321 << "[" << GetMethodName() << "] "
322 << "=================" << "\033[0m";
323 TSTypeAccessor typeAccessor(tsManager_, classType_);
324 LOG_COMPILER(INFO) << "In the constructor of class " << typeAccessor.GetClassTypeName()
325 << " of record " << std::string(recordName_)
326 << ", the following propertiess will be initialized.";
327 LOG_COMPILER(INFO) << (typeAccessor.GetInitializedProperties());
328 if (typeAccessor.ClassHasEscapedThisInConstructor()) {
329 LOG_COMPILER(INFO) << "This-object will be called by some functions in the constructor.";
330 }
331 LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m";
332 }
333 } // namespace panda::ecmascript
334