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 JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(index);
95
96 if (valueType.IsNumberType()) {
97 valueType = GateType::NumberType();
98 }
99
100 TSTypeAccessor typeAccessor(tsManager_, classType_);
101 typeAccessor.UpdateNonStaticProp(propKey, valueType.GetGTRef());
102 }
103
CollectInitializationInfo(GateRef gate,ThisUsage thisUsage)104 void InitializationAnalysis::CollectInitializationInfo(GateRef gate, ThisUsage thisUsage)
105 {
106 if (thisUsage == ThisUsage::INDEFINITE_THIS) {
107 GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver
108 if (!CheckIsThisObject(receiver)) {
109 return;
110 }
111 }
112
113 uint16_t index = acc_.GetConstantValue(acc_.GetValueIn(gate, 1)); // 1: stringId
114 if (!CheckSimpleCFG(gate, index)) {
115 return;
116 }
117 JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(index);
118 TSTypeAccessor typeAccessor(tsManager_, classType_);
119 typeAccessor.MarkPropertyInitialized(propKey);
120 }
121
CheckIsThisObject(GateRef receiver) const122 bool InitializationAnalysis::CheckIsThisObject(GateRef receiver) const
123 {
124 return IsThisFromArg(receiver) || IsThisFromSuperCall(receiver);
125 }
126
IsThisFromSuperCall(GateRef gate) const127 bool InitializationAnalysis::IsThisFromSuperCall(GateRef gate) const
128 {
129 auto op = acc_.GetOpCode(gate);
130 if (op != OpCode::JS_BYTECODE) {
131 return false;
132 }
133 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
134 switch (ecmaOpcode) {
135 case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
136 case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
137 case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
138 case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
139 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8: {
140 return true;
141 }
142 default: {
143 break;
144 }
145 }
146 return false;
147 }
148
CheckSimpleCFG(GateRef gate,const uint16_t index) const149 bool InitializationAnalysis::CheckSimpleCFG(GateRef gate, const uint16_t index) const
150 {
151 while (!acc_.IsStateRoot(gate)) {
152 if (CheckSimpleGate(gate, index)) {
153 return false;
154 }
155 gate = acc_.GetState(gate);
156 }
157 return true;
158 }
159
CheckSimpleGate(GateRef gate,const uint16_t index) const160 bool InitializationAnalysis::CheckSimpleGate(GateRef gate, const uint16_t index) const
161 {
162 OpCode opCode = acc_.GetOpCode(gate);
163 switch (opCode) {
164 case OpCode::IF_TRUE:
165 case OpCode::IF_FALSE: {
166 return true;
167 }
168 case OpCode::JS_BYTECODE: {
169 return CheckSimpleJSGate(gate, index);
170 }
171 default: {
172 break;
173 }
174 }
175 return false;
176 }
177
CheckSimpleJSGate(GateRef gate,const uint16_t index) const178 bool InitializationAnalysis::CheckSimpleJSGate(GateRef gate, const uint16_t index) const
179 {
180 ASSERT(acc_.GetOpCode(gate) == OpCode::JS_BYTECODE);
181 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
182 switch (ecmaOpcode) {
183 case EcmaOpcode::LDOBJBYNAME_IMM8_ID16:
184 case EcmaOpcode::LDOBJBYNAME_IMM16_ID16: {
185 return CheckLdObjByName(gate, index, ThisUsage::INDEFINITE_THIS);
186 }
187 case EcmaOpcode::LDTHISBYNAME_IMM8_ID16:
188 case EcmaOpcode::LDTHISBYNAME_IMM16_ID16: {
189 return CheckLdObjByName(gate, index, ThisUsage::DEFINITE_THIS);
190 }
191 case EcmaOpcode::LDTHISBYVALUE_IMM8:
192 case EcmaOpcode::LDTHISBYVALUE_IMM16: {
193 return true;
194 }
195 case EcmaOpcode::LDOBJBYVALUE_IMM8_V8:
196 case EcmaOpcode::LDOBJBYVALUE_IMM16_V8:
197 case EcmaOpcode::LDOBJBYINDEX_IMM8_IMM16:
198 case EcmaOpcode::LDOBJBYINDEX_IMM16_IMM16:
199 case EcmaOpcode::WIDE_LDOBJBYINDEX_PREF_IMM32: {
200 return CheckLdObjByIndexOrValue(gate);
201 }
202 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
203 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
204 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
205 case EcmaOpcode::STTHISBYNAME_IMM16_ID16:
206 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM8:
207 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM16: {
208 return false;
209 }
210 default: {
211 return CheckThisAsValueIn(gate);
212 }
213 }
214 return false;
215 }
216
CheckLdObjByName(GateRef gate,const uint16_t index,ThisUsage thisUsage) const217 bool InitializationAnalysis::CheckLdObjByName(GateRef gate, const uint16_t index, ThisUsage thisUsage) const
218 {
219 if (thisUsage == ThisUsage::INDEFINITE_THIS) {
220 GateRef receiver = acc_.GetValueIn(gate, 2); // 2: index of receiver
221 if (!CheckIsThisObject(receiver)) {
222 return false;
223 }
224 }
225
226 auto constData = acc_.GetValueIn(gate, 1); // 1: stringId
227 uint16_t stringId = acc_.GetConstantValue(constData);
228 return stringId == index;
229 }
230
CheckLdObjByIndexOrValue(GateRef gate) const231 bool InitializationAnalysis::CheckLdObjByIndexOrValue(GateRef gate) const
232 {
233 GateRef receiver = acc_.GetValueIn(gate, 1); // 1: index of receiver
234 return CheckIsThisObject(receiver);
235 }
236
MarkSuperClass()237 void InitializationAnalysis::MarkSuperClass()
238 {
239 TSTypeAccessor typeAccessor(tsManager_, classType_);
240 typeAccessor.MarkClassHasSuperCallInConstructor();
241 }
242
StoreThisObject()243 void InitializationAnalysis::StoreThisObject()
244 {
245 ArgumentAccessor argAcc(circuit_);
246 thisObject_ = argAcc.GetCommonArgGate(CommonArgIdx::THIS_OBJECT);
247 auto type = acc_.GetGateType(thisObject_);
248 if (tsManager_->IsClassInstanceTypeKind(type)) {
249 classType_ = GateType(tsManager_->GetClassType(type));
250 }
251 }
252
CheckThisAsValueIn(GateRef gate) const253 bool InitializationAnalysis::CheckThisAsValueIn(GateRef gate) const
254 {
255 ASSERT(acc_.GetOpCode(gate) == OpCode::JS_BYTECODE);
256 uint32_t numIns = acc_.GetNumValueIn(gate);
257 for (uint32_t i = 0; i < numIns; ++i) {
258 if (CheckIsThisObject(acc_.GetValueIn(gate, i))) {
259 return true;
260 }
261 }
262 return false;
263 }
264
CollectThisEscapedInfo(GateRef gate)265 void InitializationAnalysis::CollectThisEscapedInfo(GateRef gate)
266 {
267 ASSERT(gate != Circuit::NullGate());
268 std::vector<GateRef> valueUses;
269 acc_.GetValueUses(gate, valueUses);
270 for (const auto useGate : valueUses) {
271 if (!HasEscapedThis(useGate)) {
272 continue;
273 }
274 TSTypeAccessor typeAccessor(tsManager_, classType_);
275 typeAccessor.MarkClassHasEscapedThisInConstructor();
276 return;
277 }
278 }
279
HasEscapedThis(GateRef gate) const280 bool InitializationAnalysis::HasEscapedThis(GateRef gate) const
281 {
282 if (acc_.GetOpCode(gate) != OpCode::JS_BYTECODE) {
283 return false;
284 }
285 EcmaOpcode ecmaOpcode = acc_.GetByteCodeOpcode(gate);
286 switch (ecmaOpcode) {
287 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
288 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
289 case EcmaOpcode::STTHISBYNAME_IMM8_ID16:
290 case EcmaOpcode::STTHISBYNAME_IMM16_ID16:
291 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM8:
292 case EcmaOpcode::THROW_IFSUPERNOTCORRECTCALL_PREF_IMM16: {
293 return false;
294 }
295 default: {
296 break;
297 }
298 }
299 return true;
300 }
301
MarkHasAnalysedInitialization()302 void InitializationAnalysis::MarkHasAnalysedInitialization()
303 {
304 TSTypeAccessor typeAccessor(tsManager_, classType_);
305 typeAccessor.MarkClassHasAnalysedInitialization();
306 }
307
CheckCall() const308 bool InitializationAnalysis::CheckCall() const
309 {
310 TSTypeAccessor typeAccessor(tsManager_, classType_);
311 return typeAccessor.ClassHasEscapedThisInConstructor();
312 }
313
Print() const314 void InitializationAnalysis::Print() const
315 {
316 LOG_COMPILER(INFO) << " ";
317 LOG_COMPILER(INFO) << "\033[34m" << "================="
318 << " After initialization analysis "
319 << "[" << GetMethodName() << "] "
320 << "=================" << "\033[0m";
321 TSTypeAccessor typeAccessor(tsManager_, classType_);
322 LOG_COMPILER(INFO) << "In the constructor of class " << typeAccessor.GetClassTypeName()
323 << " of record " << std::string(recordName_)
324 << ", the following propertiess will be initialized.";
325 LOG_COMPILER(INFO) << (typeAccessor.GetInitializedProperties());
326 if (typeAccessor.ClassHasEscapedThisInConstructor()) {
327 LOG_COMPILER(INFO) << "This-object will be called by some functions in the constructor.";
328 }
329 LOG_COMPILER(INFO) << "\033[34m" << "=========================== End ===========================" << "\033[0m";
330 }
331 } // namespace panda::ecmascript
332