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_inference/method_type_infer.h"
17 #include "ecmascript/jspandafile/js_pandafile_manager.h"
18 #include "ecmascript/ts_types/ts_type_accessor.h"
19 #include "ecmascript/ts_types/ts_type_parser.h"
20
21 namespace panda::ecmascript::kungfu {
MethodTypeInfer(BytecodeCircuitBuilder * builder,Circuit * circuit,PassContext * ctx,size_t methodId,bool enableLog,const std::string & name,const CString & recordName,MethodInfo * methodInfo,const MethodLiteral * methodLiteral,bool enableGlobalTypeInfer,bool hasType)22 MethodTypeInfer::MethodTypeInfer(BytecodeCircuitBuilder *builder, Circuit *circuit, PassContext *ctx, size_t methodId,
23 bool enableLog, const std::string &name, const CString &recordName,
24 MethodInfo *methodInfo, const MethodLiteral *methodLiteral,
25 bool enableGlobalTypeInfer, bool hasType)
26 : builder_(builder), circuit_(circuit), gateAccessor_(circuit), tsManager_(ctx->GetTSManager()), ctx_(ctx),
27 lexEnvManager_(ctx->GetLexEnvManager()), methodId_(methodId), enableLog_(enableLog), methodName_(name),
28 recordName_(recordName), methodInfo_(methodInfo), methodLiteral_(methodLiteral),
29 inQueue_(circuit_->GetGateCount(), true), enableGlobalTypeInfer_(enableGlobalTypeInfer), hasType_(hasType)
30 {
31 if (enableGlobalTypeInfer_ && methodInfo->IsNamespace()) {
32 uint32_t methodOffset = methodLiteral_->GetMethodId().GetOffset();
33 if (tsManager_->HasInferredNamespaceType(methodOffset)) {
34 SetNamespaceArgType(tsManager_->GetNamespaceObjType(methodOffset));
35 }
36 }
37
38 std::vector<GateRef> gateList;
39 circuit_->GetAllGates(gateList);
40 for (auto gate : gateList) {
41 if (gateAccessor_.GetOpCode(gate) == OpCode::FRAME_ARGS) {
42 continue;
43 }
44 pendingQueue_.push(gate);
45 }
46 // init jsgateToBytecode
47 BytecodeIterator iterator(builder_, 0, builder_->GetLastBcIndex());
48 iterator.GotoStart();
49 while (!iterator.Done()) {
50 auto index = iterator.Index();
51 auto gates = builder_->GetGatesByBcIndex(index);
52 for (auto gate : gates) {
53 jsgateToBytecode_[gate] = index;
54 }
55 ++iterator;
56 }
57 }
58
CheckAndPrint()59 void MethodTypeInfer::CheckAndPrint()
60 {
61 VerifyTypePercent();
62
63 if (tsManager_->AssertTypes()) {
64 Verify();
65 }
66
67 if (IsLogEnabled()) {
68 PrintTypeAnnotation();
69 PrintByteCodesWithTypes();
70 PrintCircuitWithTypes();
71 }
72 }
73
Enqueue(GateRef gate)74 void MethodTypeInfer::Enqueue(GateRef gate)
75 {
76 auto gateId = gateAccessor_.GetId(gate);
77 if (!inQueue_[gateId]) {
78 inQueue_[gateId] = true;
79 pendingQueue_.push(gate);
80 }
81 }
82
TraverseInfer()83 std::pair<GateType, uint32_t> MethodTypeInfer::TraverseInfer()
84 {
85 // main type infer for all gates
86 while (!pendingQueue_.empty()) {
87 auto curGate = pendingQueue_.front();
88 inQueue_[gateAccessor_.GetId(curGate)] = false;
89 pendingQueue_.pop();
90 auto uses = gateAccessor_.ConstUses(curGate);
91 for (auto useIt = uses.begin(); useIt != uses.end(); useIt++) {
92 if (Infer(*useIt)) {
93 Enqueue(*useIt);
94 }
95 if (enableGlobalTypeInfer_ && IsNamespace(*useIt)) {
96 return SetAndReturnNamespaceObjType(*useIt);
97 }
98 }
99 if (pendingQueue_.empty() && needUpdateForLoopPhi_) {
100 // only for loop begin phi
101 UpdateQueueForLoopPhi();
102 needUpdateForLoopPhi_ = false;
103 }
104 }
105 return std::make_pair(GateType::AnyType(), 0); // 0: defaule value
106 }
107
UpdateQueueForLoopPhi()108 void MethodTypeInfer::UpdateQueueForLoopPhi()
109 {
110 for (auto it = loopPhiState_.begin(); it != loopPhiState_.end(); it++) {
111 auto curGate = it->first;
112 auto loopType = gateAccessor_.GetGateType(curGate);
113 auto loopBackGate = gateAccessor_.GetValueIn(curGate, 1);
114 auto loopBackType = gateAccessor_.GetGateType(loopBackGate);
115 // if loopBack Gate is finally AnyType, loop-begin phi gate should be changed to any
116 if (!loopType.IsAnyType() && loopBackType.IsAnyType()) {
117 gateAccessor_.SetGateType(curGate, GateType::AnyType());
118 loopPhiState_[curGate] = InferState::ANY_INFERED;
119 pendingQueue_.push(curGate);
120 inQueue_[gateAccessor_.GetId(curGate)] = true;
121 }
122 // if loopBack Gate is finally not same number Type, loop-begin phi gate should be promoted
123 if (loopType.IsNumberType() && loopBackType.IsNumberType() && loopType != loopBackType) {
124 gateAccessor_.SetGateType(curGate, GateType::NumberType());
125 loopPhiState_[curGate] = InferState::NUMBER_INFERED;
126 pendingQueue_.push(curGate);
127 inQueue_[gateAccessor_.GetId(curGate)] = true;
128 }
129 }
130 }
131
UpdateType(GateRef gate,const GateType type,bool savePreType)132 bool MethodTypeInfer::UpdateType(GateRef gate, const GateType type, bool savePreType)
133 {
134 GateType preType = gateAccessor_.GetGateType(gate);
135 needInferGates_.insert(gate);
136 // When the type after type inference is any and you want to save previous type, it wolud not update.
137 if (savePreType && type.IsAnyType()) {
138 return false;
139 }
140
141 if (type != preType) {
142 gateAccessor_.SetGateType(gate, HandleTypeCompatibility(preType, type));
143 return true;
144 }
145 return false;
146 }
147
UpdateType(GateRef gate,const GlobalTSTypeRef & typeRef,bool savePreType)148 bool MethodTypeInfer::UpdateType(GateRef gate, const GlobalTSTypeRef &typeRef, bool savePreType)
149 {
150 auto type = GateType(typeRef);
151 return UpdateType(gate, type, savePreType);
152 }
153
HandleTypeCompatibility(const GateType preType,const GateType type) const154 GateType MethodTypeInfer::HandleTypeCompatibility(const GateType preType, const GateType type) const
155 {
156 if (tsManager_->IsArrayTypeKind(preType) && tsManager_->IsBuiltinInstanceType(BuiltinTypeId::ARRAY, type)) {
157 return preType;
158 }
159 return type;
160 }
161
IsNewLexEnv(EcmaOpcode opcode) const162 bool MethodTypeInfer::IsNewLexEnv(EcmaOpcode opcode) const
163 {
164 switch (opcode) {
165 case EcmaOpcode::NEWLEXENV_IMM8:
166 case EcmaOpcode::NEWLEXENVWITHNAME_IMM8_ID16:
167 case EcmaOpcode::WIDE_NEWLEXENV_PREF_IMM16:
168 case EcmaOpcode::WIDE_NEWLEXENVWITHNAME_PREF_IMM16_ID16:
169 return true;
170 default:
171 return false;
172 }
173 }
174
ShouldInfer(const GateRef gate) const175 bool MethodTypeInfer::ShouldInfer(const GateRef gate) const
176 {
177 auto opcode = gateAccessor_.GetOpCode(gate);
178 // handle phi gates
179 if ((opcode == OpCode::VALUE_SELECTOR) ||
180 (opcode == OpCode::LOOP_EXIT_VALUE)) {
181 return true;
182 }
183 /* Handle constant gates (like ldnull and ldtrue), return gates and gates generated by ecma.* bytecodes (not
184 * including jump and newlexenv). Jump instructions are skipped because they have no intrinsic type information.
185 * And newlexenv instructions are used to create runtime lexical env objects which have no TS types associated. As
186 * for the type inference on lexical variables, their type information is recorded in objects of class
187 * panda::ecmascript::kungfu::LexEnv which are created during the building of IR. So in the type inference,
188 * newlexenv is ignored.
189 */
190 if (opcode != OpCode::CONSTANT &&
191 opcode != OpCode::RETURN && opcode != OpCode::JS_BYTECODE) {
192 return false;
193 }
194 if (jsgateToBytecode_.find(gate) == jsgateToBytecode_.end()) {
195 return false;
196 }
197 auto &bytecodeInfo = GetByteCodeInfo(gate);
198 return !bytecodeInfo.IsJump() && !IsNewLexEnv(bytecodeInfo.GetOpcode());
199 }
200
Infer(GateRef gate)201 bool MethodTypeInfer::Infer(GateRef gate)
202 {
203 if (!ShouldInfer(gate)) {
204 return false;
205 }
206 if (gateAccessor_.GetOpCode(gate) == OpCode::LOOP_EXIT_VALUE) {
207 return UpdateType(gate, gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate)));
208 }
209 if (gateAccessor_.GetOpCode(gate) == OpCode::VALUE_SELECTOR) {
210 return InferPhiGate(gate);
211 }
212 // infer ecma.* bytecode gates
213 auto &bytecodeInfo = GetByteCodeInfo(gate);
214 switch (bytecodeInfo.GetOpcode()) {
215 case EcmaOpcode::LDNAN:
216 case EcmaOpcode::LDINFINITY:
217 case EcmaOpcode::TONUMBER_IMM8:
218 case EcmaOpcode::NEG_IMM8:
219 case EcmaOpcode::EXP_IMM8_V8:
220 case EcmaOpcode::STARRAYSPREAD_V8_V8:
221 return SetNumberType(gate);
222 case EcmaOpcode::SHL2_IMM8_V8:
223 case EcmaOpcode::ASHR2_IMM8_V8:
224 case EcmaOpcode::SHR2_IMM8_V8:
225 case EcmaOpcode::NOT_IMM8:
226 case EcmaOpcode::AND2_IMM8_V8:
227 case EcmaOpcode::OR2_IMM8_V8:
228 case EcmaOpcode::XOR2_IMM8_V8:
229 return SetIntType(gate);
230 case EcmaOpcode::LDBIGINT_ID16:
231 return SetBigIntType(gate);
232 case EcmaOpcode::LDTRUE:
233 case EcmaOpcode::LDFALSE:
234 case EcmaOpcode::EQ_IMM8_V8:
235 case EcmaOpcode::NOTEQ_IMM8_V8:
236 case EcmaOpcode::LESS_IMM8_V8:
237 case EcmaOpcode::LESSEQ_IMM8_V8:
238 case EcmaOpcode::GREATER_IMM8_V8:
239 case EcmaOpcode::GREATEREQ_IMM8_V8:
240 case EcmaOpcode::ISIN_IMM8_V8:
241 case EcmaOpcode::INSTANCEOF_IMM8_V8:
242 case EcmaOpcode::STRICTNOTEQ_IMM8_V8:
243 case EcmaOpcode::STRICTEQ_IMM8_V8:
244 case EcmaOpcode::ISTRUE:
245 case EcmaOpcode::ISFALSE:
246 case EcmaOpcode::SETOBJECTWITHPROTO_IMM8_V8:
247 case EcmaOpcode::SETOBJECTWITHPROTO_IMM16_V8:
248 case EcmaOpcode::DELOBJPROP_V8:
249 return SetBooleanType(gate);
250 case EcmaOpcode::LDUNDEFINED:
251 return InferLdUndefined(gate);
252 case EcmaOpcode::LDNULL:
253 return InferLdNull(gate);
254 case EcmaOpcode::LDAI_IMM32:
255 return InferLdai(gate);
256 case EcmaOpcode::FLDAI_IMM64:
257 return InferFLdai(gate);
258 case EcmaOpcode::LDSYMBOL:
259 return InferLdSymbol(gate);
260 case EcmaOpcode::THROW_PREF_NONE:
261 return InferThrow(gate);
262 case EcmaOpcode::TYPEOF_IMM8:
263 case EcmaOpcode::TYPEOF_IMM16:
264 return InferTypeOf(gate);
265 case EcmaOpcode::ADD2_IMM8_V8:
266 return InferAdd2(gate);
267 case EcmaOpcode::SUB2_IMM8_V8:
268 return InferSub2(gate);
269 case EcmaOpcode::MUL2_IMM8_V8:
270 return InferMul2(gate);
271 case EcmaOpcode::MOD2_IMM8_V8:
272 return InferMod2(gate);
273 case EcmaOpcode::DIV2_IMM8_V8:
274 return InferDiv2(gate);
275 case EcmaOpcode::INC_IMM8:
276 case EcmaOpcode::DEC_IMM8:
277 return InferIncDec(gate);
278 case EcmaOpcode::LDOBJBYINDEX_IMM8_IMM16:
279 case EcmaOpcode::LDOBJBYINDEX_IMM16_IMM16:
280 case EcmaOpcode::WIDE_LDOBJBYINDEX_PREF_IMM32:
281 return InferLdObjByIndex(gate);
282 case EcmaOpcode::STGLOBALVAR_IMM16_ID16:
283 case EcmaOpcode::TRYSTGLOBALBYNAME_IMM8_ID16:
284 case EcmaOpcode::TRYSTGLOBALBYNAME_IMM16_ID16:
285 return SetStGlobalBcType(gate, true);
286 case EcmaOpcode::STTOGLOBALRECORD_IMM16_ID16:
287 case EcmaOpcode::STCONSTTOGLOBALRECORD_IMM16_ID16:
288 return SetStGlobalBcType(gate, false);
289 case EcmaOpcode::LDGLOBALVAR_IMM16_ID16:
290 return InferLdGlobalVar(gate);
291 case EcmaOpcode::RETURNUNDEFINED:
292 return InferReturnUndefined(gate);
293 case EcmaOpcode::RETURN:
294 return InferReturn(gate);
295 case EcmaOpcode::LDOBJBYNAME_IMM8_ID16:
296 case EcmaOpcode::LDOBJBYNAME_IMM16_ID16:
297 return InferLdObjByName(gate);
298 case EcmaOpcode::LDA_STR_ID16:
299 return InferLdStr(gate);
300 case EcmaOpcode::TONUMERIC_IMM8:
301 return InferToNumberic(gate);
302 case EcmaOpcode::CALLARG0_IMM8:
303 case EcmaOpcode::CALLARG1_IMM8_V8:
304 case EcmaOpcode::CALLARGS2_IMM8_V8_V8:
305 case EcmaOpcode::CALLARGS3_IMM8_V8_V8_V8:
306 case EcmaOpcode::CALLRANGE_IMM8_IMM8_V8:
307 case EcmaOpcode::WIDE_CALLRANGE_PREF_IMM16_V8:
308 case EcmaOpcode::APPLY_IMM8_V8_V8:
309 return InferCallFunction(gate);
310 case EcmaOpcode::CALLTHIS0_IMM8_V8:
311 case EcmaOpcode::CALLTHIS1_IMM8_V8_V8:
312 case EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8:
313 case EcmaOpcode::CALLTHIS3_IMM8_V8_V8_V8_V8:
314 case EcmaOpcode::CALLTHISRANGE_IMM8_IMM8_V8:
315 case EcmaOpcode::WIDE_CALLTHISRANGE_PREF_IMM16_V8:
316 case EcmaOpcode::CALLRUNTIME_CALLINIT_PREF_IMM8_V8:
317 return InferCallMethod(gate);
318 case EcmaOpcode::LDOBJBYVALUE_IMM8_V8:
319 case EcmaOpcode::LDOBJBYVALUE_IMM16_V8:
320 return InferLdObjByValue(gate);
321 case EcmaOpcode::GETNEXTPROPNAME_V8:
322 return InferGetNextPropName(gate);
323 case EcmaOpcode::DEFINEGETTERSETTERBYVALUE_V8_V8_V8_V8:
324 return InferDefineGetterSetterByValue(gate);
325 case EcmaOpcode::NEWOBJRANGE_IMM8_IMM8_V8:
326 case EcmaOpcode::NEWOBJRANGE_IMM16_IMM8_V8:
327 case EcmaOpcode::WIDE_NEWOBJRANGE_PREF_IMM16_V8:
328 case EcmaOpcode::NEWOBJAPPLY_IMM8_V8:
329 case EcmaOpcode::NEWOBJAPPLY_IMM16_V8:
330 return InferNewObject(gate);
331 case EcmaOpcode::SUPERCALLTHISRANGE_IMM8_IMM8_V8:
332 case EcmaOpcode::WIDE_SUPERCALLTHISRANGE_PREF_IMM16_V8:
333 case EcmaOpcode::SUPERCALLARROWRANGE_IMM8_IMM8_V8:
334 case EcmaOpcode::WIDE_SUPERCALLARROWRANGE_PREF_IMM16_V8:
335 case EcmaOpcode::SUPERCALLSPREAD_IMM8_V8:
336 return InferSuperCall(gate);
337 case EcmaOpcode::LDSUPERBYNAME_IMM8_ID16:
338 case EcmaOpcode::LDSUPERBYNAME_IMM16_ID16:
339 return InferSuperPropertyByName(gate);
340 case EcmaOpcode::LDSUPERBYVALUE_IMM8_V8:
341 case EcmaOpcode::LDSUPERBYVALUE_IMM16_V8:
342 return InferSuperPropertyByValue(gate);
343 case EcmaOpcode::TRYLDGLOBALBYNAME_IMM8_ID16:
344 case EcmaOpcode::TRYLDGLOBALBYNAME_IMM16_ID16:
345 return InferTryLdGlobalByName(gate);
346 case EcmaOpcode::LDLEXVAR_IMM4_IMM4:
347 case EcmaOpcode::LDLEXVAR_IMM8_IMM8:
348 case EcmaOpcode::WIDE_LDLEXVAR_PREF_IMM16_IMM16:
349 return InferLdLexVarDyn(gate);
350 case EcmaOpcode::STLEXVAR_IMM4_IMM4:
351 case EcmaOpcode::STLEXVAR_IMM8_IMM8:
352 case EcmaOpcode::WIDE_STLEXVAR_PREF_IMM16_IMM16:
353 return InferStLexVarDyn(gate);
354 case EcmaOpcode::GETITERATOR_IMM8:
355 case EcmaOpcode::GETITERATOR_IMM16:
356 return InferGetIterator(gate);
357 case EcmaOpcode::STMODULEVAR_IMM8:
358 case EcmaOpcode::WIDE_STMODULEVAR_PREF_IMM16:
359 return InferStModuleVar(gate);
360 case EcmaOpcode::LDLOCALMODULEVAR_IMM8:
361 case EcmaOpcode::WIDE_LDLOCALMODULEVAR_PREF_IMM16:
362 return InferLdLocalModuleVar(gate);
363 case EcmaOpcode::LDEXTERNALMODULEVAR_IMM8:
364 case EcmaOpcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:
365 return InferLdExternalModuleVar(gate);
366 case EcmaOpcode::STOBJBYNAME_IMM8_ID16_V8:
367 case EcmaOpcode::STOBJBYNAME_IMM16_ID16_V8:
368 return InferStObjByName(gate);
369 default:
370 break;
371 }
372 return false;
373 }
374
InferPhiGate(GateRef gate)375 bool MethodTypeInfer::InferPhiGate(GateRef gate)
376 {
377 ASSERT(gateAccessor_.GetOpCode(gate) == OpCode::VALUE_SELECTOR);
378 CVector<GlobalTSTypeRef> typeList;
379 std::set<GlobalTSTypeRef> numberTypeSet;
380 auto ins = gateAccessor_.ConstIns(gate);
381 for (auto it = ins.begin(); it != ins.end(); it++) {
382 // assuming that VALUE_SELECTOR is NO_DEPEND and NO_ROOT
383 if (gateAccessor_.GetOpCode(*it) == OpCode::MERGE) {
384 continue;
385 }
386 if (gateAccessor_.GetOpCode(*it) == OpCode::LOOP_BEGIN) {
387 return InferLoopBeginPhiGate(gate);
388 }
389 auto valueInType = gateAccessor_.GetGateType(*it);
390 if (valueInType.IsAnyType()) {
391 return UpdateType(gate, valueInType, false);
392 }
393 if (valueInType.IsNumberType()) {
394 numberTypeSet.insert(valueInType.GetGTRef());
395 } else {
396 typeList.emplace_back(valueInType.GetGTRef());
397 }
398 }
399 // deduplicate
400 std::sort(typeList.begin(), typeList.end());
401 auto deduplicateIndex = std::unique(typeList.begin(), typeList.end());
402 typeList.erase(deduplicateIndex, typeList.end());
403 if (numberTypeSet.size() == 1) {
404 typeList.emplace_back(*(numberTypeSet.begin()));
405 } else if (numberTypeSet.size() > 1) {
406 typeList.emplace_back(GateType::NumberType().GetGTRef());
407 }
408 if (typeList.size() > 1) {
409 auto unionType = tsManager_->GetOrCreateUnionType(typeList);
410 return UpdateType(gate, unionType, false);
411 }
412 auto type = typeList.at(0);
413 return UpdateType(gate, type, false);
414 }
415
SetIntType(GateRef gate)416 bool MethodTypeInfer::SetIntType(GateRef gate)
417 {
418 auto intType = GateType::IntType();
419 return UpdateType(gate, intType);
420 }
421
SetNumberType(GateRef gate)422 bool MethodTypeInfer::SetNumberType(GateRef gate)
423 {
424 auto numberType = GateType::NumberType();
425 return UpdateType(gate, numberType);
426 }
427
SetBigIntType(GateRef gate)428 bool MethodTypeInfer::SetBigIntType(GateRef gate)
429 {
430 auto bigIntType = GateType::BigIntType();
431 return UpdateType(gate, bigIntType);
432 }
433
SetBooleanType(GateRef gate)434 bool MethodTypeInfer::SetBooleanType(GateRef gate)
435 {
436 auto booleanType = GateType::BooleanType();
437 return UpdateType(gate, booleanType);
438 }
439
InferLdUndefined(GateRef gate)440 bool MethodTypeInfer::InferLdUndefined(GateRef gate)
441 {
442 auto undefinedType = GateType::UndefinedType();
443 return UpdateType(gate, undefinedType);
444 }
445
InferLdNull(GateRef gate)446 bool MethodTypeInfer::InferLdNull(GateRef gate)
447 {
448 auto nullType = GateType::NullType();
449 return UpdateType(gate, nullType);
450 }
451
InferLdai(GateRef gate)452 bool MethodTypeInfer::InferLdai(GateRef gate)
453 {
454 auto intType = GateType::IntType();
455 return UpdateType(gate, intType);
456 }
457
InferFLdai(GateRef gate)458 bool MethodTypeInfer::InferFLdai(GateRef gate)
459 {
460 auto doubleType = GateType::DoubleType();
461 return UpdateType(gate, doubleType);
462 }
463
InferLdSymbol(GateRef gate)464 bool MethodTypeInfer::InferLdSymbol(GateRef gate)
465 {
466 auto symbolType = GateType::SymbolType();
467 return UpdateType(gate, symbolType);
468 }
469
InferThrow(GateRef gate)470 bool MethodTypeInfer::InferThrow(GateRef gate)
471 {
472 ASSERT(gateAccessor_.GetNumValueIn(gate) == 1);
473 auto gateType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
474 return UpdateType(gate, gateType);
475 }
476
InferTypeOf(GateRef gate)477 bool MethodTypeInfer::InferTypeOf(GateRef gate)
478 {
479 ASSERT(gateAccessor_.GetNumValueIn(gate) == 1);
480 auto gateType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
481 return UpdateType(gate, gateType);
482 }
483
484 /*
485 * Type Infer rule(satisfy commutative law):
486 * number + number = number
487 * int + number = number
488 * double + number = double
489 * int + int = int
490 * int + double = double
491 * double + double = double
492 * string + string = string
493 */
InferAdd2(GateRef gate)494 bool MethodTypeInfer::InferAdd2(GateRef gate)
495 {
496 // 2: number of value inputs
497 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
498 auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
499 auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
500 if (firInType.IsStringType() || secInType.IsStringType()) {
501 return UpdateType(gate, GateType::StringType());
502 }
503 if ((firInType.IsNumberType() && secInType.IsDoubleType()) ||
504 (firInType.IsDoubleType() && secInType.IsNumberType())) {
505 return UpdateType(gate, GateType::DoubleType());
506 }
507 if ((firInType.IsIntType() && secInType.IsIntType())) {
508 return UpdateType(gate, GateType::IntType());
509 }
510 if (firInType.IsNumberType() && secInType.IsNumberType()) {
511 return UpdateType(gate, GateType::NumberType());
512 }
513 return UpdateType(gate, GateType::AnyType());
514 }
515
516 /*
517 * Type Infer rule(satisfy commutative law):
518 * number - number = number
519 * int - number = number
520 * double - number = double
521 * int - int = int
522 * int - double = double
523 * double - double = double
524 */
InferSub2(GateRef gate)525 bool MethodTypeInfer::InferSub2(GateRef gate)
526 {
527 // 2: number of value inputs
528 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
529 auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
530 auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
531 if ((firInType.IsNumberType() && secInType.IsDoubleType()) ||
532 (firInType.IsDoubleType() && secInType.IsNumberType())) {
533 return UpdateType(gate, GateType::DoubleType());
534 }
535 if ((firInType.IsIntType() && secInType.IsIntType())) {
536 return UpdateType(gate, GateType::IntType());
537 }
538 if (firInType.IsNumberType() && secInType.IsNumberType()) {
539 return UpdateType(gate, GateType::NumberType());
540 }
541 return UpdateType(gate, GateType::AnyType());
542 }
543
544 /*
545 * Type Infer rule(satisfy commutative law):
546 * number * number = number
547 * int * number = number
548 * double * number = double
549 * int * int = int
550 * int * double = double
551 * double * double = double
552 */
InferMul2(GateRef gate)553 bool MethodTypeInfer::InferMul2(GateRef gate)
554 {
555 // 2: number of value inputs
556 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
557 auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
558 auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
559 if ((firInType.IsNumberType() && secInType.IsDoubleType()) ||
560 (firInType.IsDoubleType() && secInType.IsNumberType())) {
561 return UpdateType(gate, GateType::DoubleType());
562 }
563 if ((firInType.IsIntType() && secInType.IsIntType())) {
564 return UpdateType(gate, GateType::IntType());
565 }
566 return UpdateType(gate, GateType::NumberType());
567 }
568
569 /*
570 * Type Infer rule(satisfy commutative law):
571 * number % number = number
572 * int % number = number
573 * double % number = double
574 * int % int = int
575 * int % double = double
576 * double % double = double
577 */
InferMod2(GateRef gate)578 bool MethodTypeInfer::InferMod2(GateRef gate)
579 {
580 // 2: number of value inputs
581 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
582 auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
583 auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
584 if ((firInType.IsNumberType() && secInType.IsDoubleType()) ||
585 (firInType.IsDoubleType() && secInType.IsNumberType())) {
586 return UpdateType(gate, GateType::DoubleType());
587 }
588 if ((firInType.IsIntType() && secInType.IsIntType())) {
589 return UpdateType(gate, GateType::IntType());
590 }
591 return UpdateType(gate, GateType::NumberType());
592 }
593
594 /*
595 * Type Infer rule(satisfy commutative law):
596 * in type lowering, both elements will be changed to float64 firstly.
597 * number / number = double
598 * int / number = double
599 * double / number = double
600 * int / int = double
601 * int / double = double
602 * double / double = double
603 * any / any = number
604 * any / number = number
605 * number / any = number
606 */
InferDiv2(GateRef gate)607 bool MethodTypeInfer::InferDiv2(GateRef gate)
608 {
609 // 2: number of value inputs
610 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
611 auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
612 auto secInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
613 if (firInType.IsNumberType() && secInType.IsNumberType()) {
614 return UpdateType(gate, GateType::DoubleType());
615 }
616 return UpdateType(gate, GateType::NumberType());
617 }
618
619 /*
620 * Type Infer rule:
621 * number++ = number
622 * number-- = number
623 * int++ = int
624 * int-- = int
625 * double++ = double
626 * double-- = double
627 */
InferIncDec(GateRef gate)628 bool MethodTypeInfer::InferIncDec(GateRef gate)
629 {
630 ASSERT(gateAccessor_.GetNumValueIn(gate) == 1);
631 auto firInType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
632 if (firInType.IsDoubleType()) {
633 return UpdateType(gate, GateType::DoubleType());
634 }
635 if (firInType.IsIntType()) {
636 return UpdateType(gate, GateType::IntType());
637 }
638 if (firInType.IsNumberType()) {
639 return UpdateType(gate, GateType::NumberType());
640 }
641 return UpdateType(gate, GateType::AnyType());
642 }
643
InferToNumberic(GateRef gate)644 bool MethodTypeInfer::InferToNumberic(GateRef gate)
645 {
646 GateRef src = gateAccessor_.GetValueIn(gate, 0);
647 GateType srcType = gateAccessor_.GetGateType(src);
648 if (srcType.IsNumberType()) {
649 return UpdateType(gate, srcType);
650 }
651 return UpdateType(gate, GateType::NumberType());
652 }
653
InferLdObjByIndex(GateRef gate)654 bool MethodTypeInfer::InferLdObjByIndex(GateRef gate)
655 {
656 // 2: number of value inputs
657 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
658 auto inValueType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
659 inValueType = tsManager_->TryNarrowUnionType(inValueType);
660 if (tsManager_->IsArrayTypeKind(inValueType)) {
661 auto type = tsManager_->GetArrayParameterTypeGT(inValueType);
662 return UpdateType(gate, type);
663 }
664
665 if (tsManager_->IsIntTypedArrayType(inValueType)) {
666 return UpdateType(gate, GateType::IntType());
667 }
668
669 if (tsManager_->IsDoubleTypedArrayType(inValueType)) {
670 return UpdateType(gate, GateType::DoubleType());
671 }
672
673 if (tsManager_->IsTypedArrayType(inValueType)) {
674 return UpdateType(gate, GateType::NumberType());
675 }
676
677 if (ShouldInferWithLdObjByValue(inValueType)) {
678 uint64_t key = gateAccessor_.GetConstantValue((gateAccessor_.GetValueIn(gate, 0))); // 0: index of key
679 auto type = GetPropType(inValueType, key);
680 return UpdateType(gate, type);
681 }
682 return UpdateType(gate, GateType::AnyType());
683 }
684
SetStGlobalBcType(GateRef gate,bool hasIC)685 bool MethodTypeInfer::SetStGlobalBcType(GateRef gate, bool hasIC)
686 {
687 auto &byteCodeInfo = GetByteCodeInfo(gate);
688 uint16_t stringId = 0;
689 GateType inValueType;
690 if (hasIC) {
691 // 2: number of value inputs
692 ASSERT(byteCodeInfo.inputs.size() == 2);
693 stringId = std::get<ConstDataId>(byteCodeInfo.inputs[1]).GetId();
694 // 3: number of value inputs
695 ASSERT(gateAccessor_.GetNumValueIn(gate) == 3);
696 // 2: value input
697 inValueType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 2));
698 } else {
699 ASSERT(byteCodeInfo.inputs.size() == 1);
700 stringId = std::get<ConstDataId>(byteCodeInfo.inputs[0]).GetId();
701 // 2: number of value inputs
702 ASSERT(gateAccessor_.GetNumValueIn(gate) == 2);
703 inValueType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
704 }
705 if (!hasType_ && inValueType.IsUndefinedType()) {
706 inValueType = GateType::AnyType();
707 }
708 if (stringIdToGateType_.find(stringId) != stringIdToGateType_.end()) {
709 stringIdToGateType_[stringId] = inValueType;
710 } else {
711 stringIdToGateType_.emplace(stringId, inValueType);
712 }
713 return UpdateType(gate, inValueType);
714 }
715
InferLdGlobalVar(GateRef gate)716 bool MethodTypeInfer::InferLdGlobalVar(GateRef gate)
717 {
718 auto &byteCodeInfo = GetByteCodeInfo(gate);
719 ASSERT(byteCodeInfo.inputs.size() == 2); // 2: number of value inputs
720 auto stringId = std::get<ConstDataId>(byteCodeInfo.inputs[1]).GetId();
721 auto iter = stringIdToGateType_.find(stringId);
722 if (iter != stringIdToGateType_.end()) {
723 return UpdateType(gate, iter->second);
724 }
725 return UpdateType(gate, GateType::AnyType());
726 }
727
InferReturnUndefined(GateRef gate)728 bool MethodTypeInfer::InferReturnUndefined(GateRef gate)
729 {
730 auto undefinedType = GateType::UndefinedType();
731 return UpdateType(gate, undefinedType);
732 }
733
InferReturn(GateRef gate)734 bool MethodTypeInfer::InferReturn(GateRef gate)
735 {
736 ASSERT(gateAccessor_.GetNumValueIn(gate) == 1);
737 auto gateType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
738 return UpdateType(gate, gateType);
739 }
740
InferLdObjByName(GateRef gate)741 bool MethodTypeInfer::InferLdObjByName(GateRef gate)
742 {
743 // 3: number of value inputs
744 ASSERT(gateAccessor_.GetNumValueIn(gate) == 3);
745 auto objType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 2)); // 2: the third parameter is receiver
746 if (objType.IsAnyType()) {
747 return UpdateType(gate, GateType::AnyType());
748 }
749 objType = tsManager_->TryNarrowUnionType(objType);
750 if (ShouldConvertToBuiltinArray(objType)) {
751 GlobalTSTypeRef builtinGt = ConvertPrimitiveToBuiltin(objType);
752 auto builtinInstanceType = tsManager_->CreateClassInstanceType(builtinGt);
753 objType = GateType(builtinInstanceType);
754 }
755 if (tsManager_->IsPrimitiveTypeKind(objType)) {
756 GlobalTSTypeRef builtinGt = ConvertPrimitiveToBuiltin(objType);
757 if (builtinGt.IsBuiltinModule()) {
758 auto builtinInstanceType = tsManager_->CreateClassInstanceType(builtinGt);
759 objType = GateType(builtinInstanceType);
760 }
761 }
762 // If this object has no gt type, we cannot get its internal property type
763 if (ShouldInferWithLdObjByName(objType)) {
764 uint16_t index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 1));
765 return GetObjPropWithName(gate, objType, index);
766 }
767 return UpdateType(gate, GateType::AnyType());
768 }
769
InferStObjByName(GateRef gate)770 bool MethodTypeInfer::InferStObjByName(GateRef gate)
771 {
772 GateRef value = gateAccessor_.GetValueIn(gate, 3); // 3: index of value
773 GateType valueType = gateAccessor_.GetGateType(value);
774 if (valueType.IsAnyType()) {
775 return false;
776 }
777
778 GateRef receiver = gateAccessor_.GetValueIn(gate, 2); // 2: index of receiver
779 GateType receiverType = gateAccessor_.GetGateType(receiver);
780
781 uint16_t index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 1)); // 1: index of key
782 auto methodOffset = gateAccessor_.TryGetMethodOffset(gate);
783 JSTaggedValue propKey = tsManager_->GetStringFromConstantPool(methodOffset, index);
784 if (tsManager_->IsNamespaceTypeKind(receiverType)) {
785 if (tsManager_->AddNamespacePropType(receiverType, propKey, valueType)) {
786 Enqueue(receiver);
787 }
788 return true;
789 }
790
791 if (valueType.IsNumberType()) {
792 valueType = GateType::NumberType();
793 }
794
795 if (tsManager_->IsClassTypeKind(receiverType)) {
796 TSTypeAccessor typeAccessor(tsManager_, receiverType);
797 typeAccessor.UpdateStaticProp(propKey, valueType.GetGTRef());
798 return true;
799 }
800 return false;
801 }
802
InferNewObject(GateRef gate)803 bool MethodTypeInfer::InferNewObject(GateRef gate)
804 {
805 auto objType = gateAccessor_.GetGateType(gate);
806 if (!tsManager_->IsClassInstanceTypeKind(objType) && !tsManager_->IsArrayTypeKind(objType)) {
807 ASSERT(gateAccessor_.GetNumValueIn(gate) > 0);
808 auto classType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
809 if (tsManager_->IsClassTypeKind(classType)) {
810 auto classInstanceType = tsManager_->CreateClassInstanceType(classType);
811 return UpdateType(gate, classInstanceType);
812 }
813 }
814 return UpdateType(gate, GateType::AnyType());
815 }
816
InferLdStr(GateRef gate)817 bool MethodTypeInfer::InferLdStr(GateRef gate)
818 {
819 auto stringType = GateType::StringType();
820 return UpdateType(gate, stringType);
821 }
822
GetObjPropWithName(GateRef gate,GateType objType,uint64_t index)823 bool MethodTypeInfer::GetObjPropWithName(GateRef gate, GateType objType, uint64_t index)
824 {
825 auto methodOffset = gateAccessor_.TryGetMethodOffset(gate);
826 JSTaggedValue name = tsManager_->GetStringFromConstantPool(methodOffset, index);
827 if (tsManager_->IsBuiltinInstanceType(BuiltinTypeId::ARRAY, objType) || tsManager_->IsTypedArrayType(objType)) {
828 auto thread = tsManager_->GetThread();
829 JSTaggedValue lengthKey = thread->GlobalConstants()->GetLengthString();
830 if (JSTaggedValue::SameValue(name, lengthKey)) {
831 return SetIntType(gate);
832 }
833 if (tsManager_->IsTypedArrayType(objType)) {
834 uint32_t eleIdx = 0;
835 if (EcmaStringAccessor(name).ToElementIndex(&eleIdx)) {
836 return UpdateType(gate, GateType::NumberType());
837 }
838 }
839 }
840 auto type = GetPropType(objType, name);
841 if (tsManager_->IsGetterSetterFunc(type)) {
842 auto returnGt = tsManager_->GetFuncReturnValueTypeGT(type);
843 return UpdateType(gate, returnGt);
844 }
845 return UpdateType(gate, type);
846 }
847
InferCallMethod(GateRef gate)848 bool MethodTypeInfer::InferCallMethod(GateRef gate)
849 {
850 // 1: last one elem is function
851 size_t funcIndex = gateAccessor_.GetNumValueIn(gate) - 1;
852 auto funcType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, funcIndex));
853 if (tsManager_->IsFunctionTypeKind(funcType)) {
854 // forEach CallBack
855 TSTypeAccessor typeAccessor(tsManager_, funcType);
856 if (typeAccessor.GetFunctionName() == "forEach") {
857 auto funcGate = gateAccessor_.GetValueIn(gate, funcIndex);
858 auto &bytecodeInfo = GetByteCodeInfo(funcGate);
859 if (bytecodeInfo.GetOpcode() == EcmaOpcode::LDOBJBYNAME_IMM8_ID16 ||
860 bytecodeInfo.GetOpcode() == EcmaOpcode::LDOBJBYNAME_IMM16_ID16) {
861 // 2: the third parameter is receiver
862 auto objType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(funcGate, 2));
863 // get callBack function type
864 auto callBackType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
865 TSTypeAccessor newTypeAccessor(tsManager_, callBackType);
866 newTypeAccessor.UpdateForEachCBPara(objType);
867 }
868 }
869
870 // normal Call
871 auto returnType = tsManager_->GetFuncReturnValueTypeGT(funcType);
872 GateRef thisObj = gateAccessor_.GetValueIn(gate, 0); // 0: index of thisObject
873 auto thisObjType = gateAccessor_.GetGateType(thisObj);
874 return UpdateType(gate, HandleTypeCompatibility(thisObjType, GateType(returnType)));
875 } else if (tsManager_->IsIteratorInstanceTypeKind(funcType)) {
876 GlobalTSTypeRef elementGT = tsManager_->GetIteratorInstanceElementGt(funcType);
877 GlobalTSTypeRef iteratorResultInstanceType = tsManager_->GetOrCreateTSIteratorInstanceType(
878 TSRuntimeType::ITERATOR_RESULT, elementGT);
879 return UpdateType(gate, iteratorResultInstanceType);
880 }
881 return UpdateType(gate, GateType::AnyType());
882 }
883
InferCallFunction(GateRef gate)884 bool MethodTypeInfer::InferCallFunction(GateRef gate)
885 {
886 // 1: last one elem is function
887 size_t funcIndex = gateAccessor_.GetNumValueIn(gate) - 1;
888 auto funcType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, funcIndex));
889 if (tsManager_->IsFunctionTypeKind(funcType)) {
890 auto returnType = tsManager_->GetFuncReturnValueTypeGT(funcType);
891 return UpdateType(gate, returnType);
892 }
893 /* According to the ECMAScript specification, user-defined classes can only be instantiated by constructing (with
894 * new keyword). However, a few builtin types can be called like a function. Upon the results of calling and
895 * constructing, there are 4 categories of builtin types:
896 *
897 * Category 1: non-callable, objects of such a type can only be created by constructing.
898 * Category 2: non-constructable, such types can only be called.
899 * Category 3: simple, calling and constructing are equivalent.
900 * Category 4: complex, a type can be called and constructed, but the results differ.
901 *
902 * Constructing a builtin type always create objects of the type if supported. So in this function, we focus on the
903 * builtin types which are callable. While the majority of the callable builtin types have the same calling behavior
904 * as constructing, here are some special cases:
905 *
906 * | Type | Call | Category |
907 * | ------- | ----------------- | -------- |
908 * | BigInt | primitive bigint | 2 |
909 * | Boolean | primitive boolean | 4 |
910 * | Date | primitive string | 4 |
911 * | Number | primitive number | 4 |
912 * | String | primitive string | 4 |
913 *
914 * See the list of builtin types' constructors at:
915 * https://tc39.es/ecma262/2021/#sec-constructor-properties-of-the-global-object
916 */
917 if (tsManager_->IsBuiltinObjectType(funcType)) {
918 // For simplicity, calling and constructing are considered equivalent.
919 if (tsManager_->IsBuiltinClassType(BuiltinTypeId::ARRAY, funcType)) {
920 return UpdateType(gate, tsManager_->CreateArrayType());
921 }
922 return UpdateType(gate, tsManager_->CreateClassInstanceType(funcType));
923 }
924 return UpdateType(gate, GateType::AnyType());
925 }
926
InferLdObjByValue(GateRef gate)927 bool MethodTypeInfer::InferLdObjByValue(GateRef gate)
928 {
929 auto objType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 1));
930 objType = tsManager_->TryNarrowUnionType(objType);
931 // handle array
932 if (tsManager_->IsArrayTypeKind(objType)) {
933 auto elementType = tsManager_->GetArrayParameterTypeGT(objType);
934 return UpdateType(gate, elementType);
935 }
936 if (tsManager_->IsIntTypedArrayType(objType)) {
937 return UpdateType(gate, GateType::IntType());
938 }
939 if (tsManager_->IsDoubleTypedArrayType(objType)) {
940 return UpdateType(gate, GateType::DoubleType());
941 }
942 // handle object
943 if (ShouldInferWithLdObjByValue(objType)) {
944 auto valueGate = gateAccessor_.GetValueIn(gate, 2); // 2: value input slot
945 if (gateAccessor_.GetOpCode(valueGate) == OpCode::CONSTANT) {
946 if (tsManager_->IsTypedArrayType(objType)) {
947 return UpdateType(gate, GateType::NumberType());
948 }
949 uint64_t value = gateAccessor_.GetConstantValue(valueGate);
950 auto type = GetPropType(objType, value);
951 return UpdateType(gate, type);
952 }
953 if (IsByteCodeGate(valueGate) && GetByteCodeInfo(valueGate).IsBc(EcmaOpcode::LDA_STR_ID16)) {
954 auto index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(valueGate, 0));
955 return GetObjPropWithName(gate, objType, index);
956 }
957 }
958 return UpdateType(gate, GateType::AnyType());
959 }
960
InferGetNextPropName(GateRef gate)961 bool MethodTypeInfer::InferGetNextPropName(GateRef gate)
962 {
963 auto stringType = GateType::StringType();
964 return UpdateType(gate, stringType);
965 }
966
InferDefineGetterSetterByValue(GateRef gate)967 bool MethodTypeInfer::InferDefineGetterSetterByValue(GateRef gate)
968 {
969 // 0 : the index of obj
970 auto objType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
971 return UpdateType(gate, objType);
972 }
973
InferSuperCall(GateRef gate)974 bool MethodTypeInfer::InferSuperCall(GateRef gate)
975 {
976 ArgumentAccessor argAcc(circuit_);
977 auto newTarget = argAcc.GetFrameArgsIn(gate, FrameArgIdx::NEW_TARGET);
978 auto classType = gateAccessor_.GetGateType(newTarget);
979 if (tsManager_->IsClassTypeKind(classType)) {
980 auto classInstanceType = tsManager_->CreateClassInstanceType(classType);
981 return UpdateType(gate, classInstanceType);
982 }
983 return UpdateType(gate, GateType::AnyType());
984 }
985
InferSuperPropertyByName(GateRef gate)986 bool MethodTypeInfer::InferSuperPropertyByName(GateRef gate)
987 {
988 uint16_t index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
989 return GetSuperProp(gate, index);
990 }
991
InferSuperPropertyByValue(GateRef gate)992 bool MethodTypeInfer::InferSuperPropertyByValue(GateRef gate)
993 {
994 auto valueGate = gateAccessor_.GetValueIn(gate, 1);
995 if (IsByteCodeGate(valueGate) && GetByteCodeInfo(valueGate).IsBc(EcmaOpcode::LDA_STR_ID16)) {
996 auto index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(valueGate, 0));
997 return GetSuperProp(gate, index);
998 }
999 if (gateAccessor_.GetOpCode(valueGate) == OpCode::CONSTANT) {
1000 auto index = gateAccessor_.GetConstantValue(valueGate);
1001
1002 return GetSuperProp(gate, index, false);
1003 }
1004 return UpdateType(gate, GateType::AnyType());
1005 }
1006
GetSuperProp(GateRef gate,uint64_t index,bool isString)1007 bool MethodTypeInfer::GetSuperProp(GateRef gate, uint64_t index, bool isString)
1008 {
1009 ArgumentAccessor argAcc(circuit_);
1010 auto func = argAcc.GetFrameArgsIn(gate, FrameArgIdx::FUNC);
1011 auto newTarget = argAcc.GetFrameArgsIn(gate, FrameArgIdx::NEW_TARGET);
1012 auto funcType = gateAccessor_.GetGateType(func);
1013 auto classType = gateAccessor_.GetGateType(newTarget);
1014 if (funcType.IsAnyType() || classType.IsAnyType() || classType.IsUndefinedType()) {
1015 return UpdateType(gate, GateType::AnyType());
1016 }
1017
1018 bool isStatic = tsManager_->IsStaticFunc(funcType.GetGTRef());
1019 auto propType = isStatic ? PropertyType::STATIC : PropertyType::NORMAL;
1020 auto methodOffset = gateAccessor_.TryGetMethodOffset(gate);
1021 GlobalTSTypeRef type = isString ?
1022 tsManager_->GetSuperPropType(classType.GetGTRef(),
1023 tsManager_->GetStringFromConstantPool(methodOffset, index), propType) :
1024 tsManager_->GetSuperPropType(classType.GetGTRef(), index, propType);
1025 if (tsManager_->IsGetterSetterFunc(type)) {
1026 auto returnGt = tsManager_->GetFuncReturnValueTypeGT(type);
1027 return UpdateType(gate, returnGt);
1028 }
1029 return UpdateType(gate, type);
1030 }
1031
InferGetIterator(GateRef gate)1032 bool MethodTypeInfer::InferGetIterator(GateRef gate)
1033 {
1034 ASSERT(gateAccessor_.GetNumValueIn(gate) == 1);
1035 GateType inValueType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 0));
1036
1037 GlobalTSTypeRef elementGt = GlobalTSTypeRef::Default();
1038 if (tsManager_->IsArrayTypeKind(inValueType)) {
1039 elementGt = tsManager_->GetArrayParameterTypeGT(inValueType);
1040 } else if (inValueType.IsStringType()) {
1041 elementGt.SetType(GateType::StringType().Value());
1042 } else {
1043 return UpdateType(gate, GateType::AnyType());
1044 }
1045 GlobalTSTypeRef iteratorInstanceType = tsManager_->GetOrCreateTSIteratorInstanceType(
1046 TSRuntimeType::ITERATOR, elementGt);
1047 return UpdateType(gate, iteratorInstanceType);
1048 }
1049
InferTryLdGlobalByName(GateRef gate)1050 bool MethodTypeInfer::InferTryLdGlobalByName(GateRef gate)
1051 {
1052 // todo by hongtao, should consider function of .d.ts
1053 auto &byteCodeInfo = GetByteCodeInfo(gate);
1054 ASSERT(byteCodeInfo.inputs.size() == 2); // 2: number of parameter
1055 auto stringId = std::get<ConstDataId>(byteCodeInfo.inputs[1]).GetId();
1056 auto iter = stringIdToGateType_.find(stringId);
1057 if (iter != stringIdToGateType_.end()) {
1058 return UpdateType(gate, iter->second);
1059 }
1060 return UpdateType(gate, GateType::AnyType());
1061 }
1062
InferLdLexVarDyn(GateRef gate)1063 bool MethodTypeInfer::InferLdLexVarDyn(GateRef gate)
1064 {
1065 auto level = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
1066 auto slot = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 1));
1067 auto type = lexEnvManager_->GetLexEnvElementType(methodId_, level, slot);
1068 if (!hasType_ && type.IsUndefinedType()) {
1069 type = GateType::AnyType();
1070 }
1071 return UpdateType(gate, type);
1072 }
1073
InferStLexVarDyn(GateRef gate)1074 bool MethodTypeInfer::InferStLexVarDyn(GateRef gate)
1075 {
1076 auto level = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
1077 auto slot = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 1));
1078 auto type = lexEnvManager_->GetLexEnvElementType(methodId_, level, slot);
1079 if (type.IsAnyType() || type.IsUndefinedType()) {
1080 auto valueType = gateAccessor_.GetGateType(gateAccessor_.GetValueIn(gate, 3));
1081 if (!valueType.IsAnyType()) {
1082 lexEnvManager_->SetLexEnvElementType(methodId_, level, slot, valueType);
1083 return true;
1084 }
1085 }
1086 return false;
1087 }
1088
InferStModuleVar(GateRef gate)1089 bool MethodTypeInfer::InferStModuleVar(GateRef gate)
1090 {
1091 auto index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
1092 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1093 auto defineGate = gateAccessor_.GetValueIn(gate, 1);
1094 auto defineType = gateAccessor_.GetGateType(defineGate);
1095 if (!defineType.IsAnyType()) {
1096 tsManager_->AddTypeToModuleVarGtMap(jsPandaFile, recordName_, index, defineType.GetGTRef());
1097 return true;
1098 }
1099 return false;
1100 }
1101
InferLdLocalModuleVar(GateRef gate)1102 bool MethodTypeInfer::InferLdLocalModuleVar(GateRef gate)
1103 {
1104 auto index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
1105 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1106 if (!tsManager_->HasExportGT(jsPandaFile, recordName_, index)) {
1107 return UpdateType(gate, GateType::AnyType());
1108 }
1109 auto type = tsManager_->GetGTFromModuleMap(jsPandaFile, recordName_, index);
1110 return UpdateType(gate, type);
1111 }
1112
InferLdExternalModuleVar(GateRef gate)1113 bool MethodTypeInfer::InferLdExternalModuleVar(GateRef gate)
1114 {
1115 auto index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
1116 auto loadType = gateAccessor_.GetGateType(gate);
1117 auto bcInfoCollector = tsManager_->GetBytecodeInfoCollector();
1118 ASSERT(bcInfoCollector != nullptr);
1119 const auto &bcInfo = bcInfoCollector->GetBytecodeInfo();
1120 const auto &importRecordsInfos = bcInfo.GetImportRecordsInfos();
1121 auto iter = importRecordsInfos.find(recordName_);
1122 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1123 CString resolvedRecord = "";
1124 uint32_t resolvedIndex = 0;
1125 if (loadType.IsAnyType() && iter != importRecordsInfos.end()) {
1126 const auto &importIdToExportRecord = iter->second.GetImportIdToExportRecord();
1127 if (importIdToExportRecord.find(index) != importIdToExportRecord.end()) {
1128 std::tie(resolvedRecord, resolvedIndex) = importIdToExportRecord.at(index);
1129 if (tsManager_->HasExportGT(jsPandaFile, resolvedRecord, resolvedIndex)) {
1130 return UpdateType(gate, tsManager_->GetGTFromModuleMap(jsPandaFile, resolvedRecord, resolvedIndex));
1131 }
1132 }
1133 }
1134 // if we can't find type in exportRecords, we will try to find type using resolved index binding directly.
1135 // However, this compilation order is not guaranteed, so the export type may not have been infered.
1136 if (UNLIKELY(loadType.IsAnyType())) {
1137 auto thread = tsManager_->GetEcmaVM()->GetJSThread();
1138 ModuleManager *moduleManager = thread->GetCurrentEcmaContext()->GetModuleManager();
1139 [[maybe_unused]] EcmaHandleScope scope(thread);
1140 JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName_);
1141 JSTaggedValue moduleEnvironment = currentModule->GetEnvironment();
1142 if (moduleEnvironment.IsUndefined()) {
1143 return UpdateType(gate, GateType::AnyType());
1144 }
1145 ASSERT(moduleEnvironment.IsTaggedArray());
1146 JSHandle<TaggedArray> moduleArray(thread, moduleEnvironment);
1147 JSTaggedValue resolvedBinding = moduleArray->Get(index);
1148 // if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module.
1149 if (!resolvedBinding.IsResolvedIndexBinding()) {
1150 return UpdateType(gate, GateType::AnyType());
1151 }
1152 ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
1153 resolvedRecord = ModuleManager::GetRecordName(binding->GetModule());
1154 resolvedIndex = static_cast<uint32_t>(binding->GetIndex());
1155 if (tsManager_->HasExportGT(jsPandaFile, resolvedRecord, resolvedIndex)) {
1156 return UpdateType(gate, tsManager_->GetGTFromModuleMap(jsPandaFile, resolvedRecord, resolvedIndex));
1157 }
1158 }
1159 return UpdateType(gate, GateType::AnyType());
1160 }
1161
InferLoopBeginPhiGate(GateRef gate)1162 bool MethodTypeInfer::InferLoopBeginPhiGate(GateRef gate)
1163 {
1164 // loop-begin phi gate has 3 ins: loop_begin(stateWire), loopInGate(valueWire), loopBackGate(valueWire)
1165 auto loopInGate = gateAccessor_.GetValueIn(gate);
1166 auto loopInType = gateAccessor_.GetGateType(loopInGate);
1167 // loop-begin phi will be initialized as loopInTytpe
1168 // type of loop-back phi should be infered correctly only after loop-begin has actual type
1169 // if loop-in phi is actual any type, loop-begin phi must be any
1170 if (loopPhiState_.find(gate) == loopPhiState_.end()) {
1171 if (!loopInType.IsAnyType()) {
1172 loopPhiState_[gate] = InferState::NORMAL_INFERED;
1173 }
1174 return UpdateType(gate, loopInType, false);
1175 }
1176 // if loop phi has been marked as ANY_INFERED, it's in the second round infer for loop
1177 if (loopPhiState_[gate] == InferState::ANY_INFERED) {
1178 return UpdateType(gate, GateType::AnyType(), false);
1179 }
1180 // if loopInType and loopBackType both have non-any type, we need special treatment for the situation
1181 // in which loopInType and loopBackType both are numberType(int/double/number).
1182 // However, we should avoid excessive type promotion which may cause endless loop in few IR situations.
1183 if (loopPhiState_[gate] == InferState::NUMBER_INFERED) {
1184 return UpdateType(gate, GateType::NumberType(), false);
1185 }
1186 return UpdateType(gate, loopInType, false);
1187 }
1188
ConvertPrimitiveToBuiltin(const GateType & gateType)1189 GlobalTSTypeRef MethodTypeInfer::ConvertPrimitiveToBuiltin(const GateType &gateType)
1190 {
1191 GlobalTSTypeRef builtinGt = GlobalTSTypeRef::Default();
1192 if (!tsManager_->IsBuiltinsDTSEnabled()) {
1193 return builtinGt;
1194 }
1195
1196 const JSPandaFile *builtinjsPandaFile = tsManager_->GetBuiltinPandaFile();
1197 if (builtinjsPandaFile == nullptr) {
1198 LOG_COMPILER(FATAL) << "load lib_ark_builtins.d.ts failed";
1199 }
1200 const CString &builtinsRecordName = tsManager_->GetBuiltinRecordName();
1201 TSTypeParser typeParser(tsManager_);
1202
1203 if (tsManager_->IsArrayTypeKind(gateType)) {
1204 return typeParser.CreateGT(builtinjsPandaFile, builtinsRecordName,
1205 static_cast<uint32_t>(BuiltinTypeId::ARRAY));
1206 }
1207
1208 const GlobalTSTypeRef gt = GlobalTSTypeRef(gateType.Value());
1209 uint32_t l = gt.GetLocalId();
1210 switch (l) {
1211 case static_cast<uint32_t>(TSPrimitiveType::SYMBOL):
1212 builtinGt = typeParser.CreateGT(builtinjsPandaFile, builtinsRecordName,
1213 static_cast<uint32_t>(BuiltinTypeId::SYMBOL));
1214 break;
1215 case static_cast<uint32_t>(TSPrimitiveType::INT):
1216 case static_cast<uint32_t>(TSPrimitiveType::DOUBLE):
1217 case static_cast<uint32_t>(TSPrimitiveType::NUMBER):
1218 builtinGt = typeParser.CreateGT(builtinjsPandaFile, builtinsRecordName,
1219 static_cast<uint32_t>(BuiltinTypeId::NUMBER));
1220 break;
1221 case static_cast<uint32_t>(TSPrimitiveType::BOOLEAN):
1222 builtinGt = typeParser.CreateGT(builtinjsPandaFile, builtinsRecordName,
1223 static_cast<uint32_t>(BuiltinTypeId::BOOLEAN));
1224 break;
1225 case static_cast<uint32_t>(TSPrimitiveType::STRING):
1226 builtinGt = typeParser.CreateGT(builtinjsPandaFile, builtinsRecordName,
1227 static_cast<uint32_t>(BuiltinTypeId::STRING));
1228 break;
1229 default:
1230 builtinGt = GlobalTSTypeRef::Default();
1231 }
1232 return builtinGt;
1233 }
1234
GetPropType(const GateType type,const JSTaggedValue propertyName) const1235 GlobalTSTypeRef MethodTypeInfer::GetPropType(const GateType type, const JSTaggedValue propertyName) const
1236 {
1237 GlobalTSTypeRef objGT(type.Value());
1238 GlobalTSTypeRef propGT = tsManager_->GetPropType(objGT, propertyName);
1239 if (!propGT.IsDefault()) {
1240 return propGT;
1241 }
1242 return tsManager_->GetIndexSignType(objGT, GateType::StringType());
1243 }
1244
GetPropType(const GateType type,const uint64_t key) const1245 GlobalTSTypeRef MethodTypeInfer::GetPropType(const GateType type, const uint64_t key) const
1246 {
1247 GlobalTSTypeRef objGT(type.Value());
1248 GlobalTSTypeRef propGT = tsManager_->GetPropType(objGT, key);
1249 if (!propGT.IsDefault()) {
1250 return propGT;
1251 }
1252 return tsManager_->GetIndexSignType(objGT, GateType::NumberType());
1253 }
1254
1255 // In TS, a namespace can be thought of as a formalization of the IIFE pattern.
1256 // The function has only one parameter, which corresponds to the namespace object.
SetNamespaceArgType(GateType type)1257 void MethodTypeInfer::SetNamespaceArgType(GateType type)
1258 {
1259 ArgumentAccessor argAcc(circuit_, methodLiteral_);
1260 // the last position is where the only parameter of the function are placed
1261 auto gate = argAcc.ArgsAt(argAcc.ArgsCount() - 1);
1262 gateAccessor_.SetGateType(gate, type);
1263 }
1264
1265 // When a IIFE which corresponds to namespaces declaration being called,
1266 // A namespace type will be set to the namespace object.
SetAndReturnNamespaceObjType(GateRef gate)1267 std::pair<GateType, uint32_t> MethodTypeInfer::SetAndReturnNamespaceObjType(GateRef gate)
1268 {
1269 GateRef func = gateAccessor_.GetValueIn(gate, 1); // 1: index of func
1270 uint16_t id = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(func, 0)); // 0: index of methodId
1271 GateRef obj = gateAccessor_.GetValueIn(gate, 0); // 0: index of obj
1272 // the obj must be phi gate due to the conversion of syntax sugar of namespace
1273 ASSERT(gateAccessor_.IsValueSelector(obj));
1274 GlobalTSTypeRef gt = TryGetNamespaceType(obj);
1275
1276 uint32_t methodId = ctx_->GetJSPandaFile()->ResolveMethodIndex(methodLiteral_->GetMethodId(), id).GetOffset();
1277 uint32_t length = gateAccessor_.GetNumValueIn(obj);
1278 for (uint32_t i = 0; i < length; i++) {
1279 GateRef namespaceObj = gateAccessor_.GetValueIn(obj, i);
1280 if (!IsByteCodeGate(namespaceObj)) {
1281 continue;
1282 }
1283 auto &bytecodeInfo = GetByteCodeInfo(namespaceObj);
1284 if (!bytecodeInfo.IsBc(EcmaOpcode::CREATEEMPTYOBJECT)) {
1285 continue;
1286 }
1287
1288 if (gt.IsDefault()) {
1289 gt = tsManager_->CreateNamespaceType();
1290 }
1291 gateAccessor_.SetGateType(namespaceObj, GateType(gt));
1292 return std::make_pair(GateType(gt), methodId);
1293 }
1294
1295 return std::make_pair((GateType(gt)), methodId);
1296 }
1297
TryGetNamespaceType(GateRef gate) const1298 GlobalTSTypeRef MethodTypeInfer::TryGetNamespaceType(GateRef gate) const
1299 {
1300 ASSERT(gateAccessor_.IsValueSelector(gate));
1301 uint32_t length = gateAccessor_.GetNumValueIn(gate);
1302 for (uint32_t i = 0; i < length; i++) {
1303 GateRef namespaceObj = gateAccessor_.GetValueIn(gate, i);
1304 GateType type = gateAccessor_.GetGateType(namespaceObj);
1305 GlobalTSTypeRef namespaceGT(type.Value());
1306 if (tsManager_->IsNamespaceTypeKind(namespaceGT)) {
1307 return namespaceGT;
1308 }
1309 }
1310 return GlobalTSTypeRef::Default();
1311 }
1312
IsNamespace(GateRef gate) const1313 bool MethodTypeInfer::IsNamespace(GateRef gate) const
1314 {
1315 if (IsByteCodeGate(gate)) {
1316 auto &bytecodeInfo = GetByteCodeInfo(gate);
1317 if (bytecodeInfo.IsBc(EcmaOpcode::CALLARG1_IMM8_V8)) {
1318 GateRef obj = gateAccessor_.GetValueIn(gate, 0); // 0: index of obj
1319 GateRef func = gateAccessor_.GetValueIn(gate, 1); // 1: index of func
1320 return CheckNamespaceFunc(func) && gateAccessor_.IsValueSelector(obj);
1321 }
1322 }
1323 return false;
1324 }
1325
CheckNamespaceFunc(GateRef func) const1326 bool MethodTypeInfer::CheckNamespaceFunc(GateRef func) const
1327 {
1328 if (IsByteCodeGate(func)) {
1329 auto &bytecodeInfo = GetByteCodeInfo(func);
1330 if (bytecodeInfo.IsBc(EcmaOpcode::DEFINEFUNC_IMM8_ID16_IMM8) ||
1331 bytecodeInfo.IsBc(EcmaOpcode::DEFINEFUNC_IMM16_ID16_IMM8)) {
1332 uint16_t id = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(func, 0)); // 0: index of methodId
1333 uint32_t methodId =
1334 ctx_->GetJSPandaFile()->ResolveMethodIndex(methodLiteral_->GetMethodId(), id).GetOffset();
1335 auto &bcInfo = ctx_->GetBytecodeInfo();
1336 auto &methodLists = bcInfo.GetMethodList();
1337 auto &methodInfo = methodLists.at(methodId);
1338 return methodInfo.IsNamespace();
1339 }
1340 }
1341 return false;
1342 }
1343
PrintTypeAnnotation() const1344 void MethodTypeInfer::PrintTypeAnnotation() const
1345 {
1346 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1347 panda_file::File::EntityId fieldId = methodLiteral_->GetMethodId();
1348 TypeAnnotationExtractor annoExtractor(jsPandaFile, fieldId.GetOffset());
1349 annoExtractor.Print();
1350 }
1351
PrintByteCodesWithTypes() const1352 void MethodTypeInfer::PrintByteCodesWithTypes() const
1353 {
1354 std::vector<GateRef> gateList;
1355 circuit_->GetAllGates(gateList);
1356
1357 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1358 const MethodLiteral *methodLiteral = builder_->GetMethod();
1359 auto methodId = methodLiteral->GetMethodId();
1360 const std::string functionName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
1361
1362 const uint32_t adjustment = 6;
1363 LOG_COMPILER(INFO) << "====================================================================";
1364 LOG_COMPILER(INFO) << "print bytecode types:";
1365 LOG_COMPILER(INFO) << ".recordName " + recordName_;
1366 LOG_COMPILER(INFO) << ".function " + functionName + "() {";
1367 uint32_t lastBcIndex = builder_->GetLastBcIndex();
1368 DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
1369 for (uint32_t bcIndex = 0; bcIndex < lastBcIndex; bcIndex++) { // ignore last element
1370 const uint8_t *pc = builder_->GetPCByIndex(bcIndex);
1371 BytecodeInstruction inst(pc);
1372 int32_t lineNumber = 0;
1373 auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
1374 lineNumber = line + 1;
1375 return true;
1376 };
1377 int32_t columnNumber = 0;
1378 auto callbackColumnFunc = [&columnNumber](int32_t column) -> bool {
1379 columnNumber += column + 1;
1380 return true;
1381 };
1382 auto offset = builder_->GetPcOffset(bcIndex);
1383 debugExtractor->MatchLineWithOffset(callbackLineFunc, methodId, offset);
1384 debugExtractor->MatchColumnWithOffset(callbackColumnFunc, methodId, offset);
1385
1386 auto gates = builder_->GetGatesByBcIndex(bcIndex);
1387 if (gates.empty()) {
1388 LOG_COMPILER(INFO) << std::setw(adjustment) << std::to_string(bcIndex) << " " << inst << ", "
1389 << "at line: " + std::to_string(lineNumber) + " column: " + std::to_string(columnNumber)
1390 << ", pcOffset: " + std::to_string(offset);
1391 }
1392
1393 for (const auto gate : gates) {
1394 if (gate == Circuit::NullGate()) {
1395 continue;
1396 }
1397
1398 GateType type = gateAccessor_.GetGateType(gate);
1399 GlobalTSTypeRef gt = type.GetGTRef();
1400 LOG_COMPILER(INFO) << std::setw(adjustment) << std::to_string(bcIndex) << " " << inst << ", "
1401 << "[type: " + tsManager_->GetTypeStr(type) + ", "
1402 << "moduleId: " + std::to_string(gt.GetModuleId()) + ", "
1403 << "localId: " + std::to_string(gt.GetLocalId()) + "], "
1404 << "at line: " + std::to_string(lineNumber) + " column: " + std::to_string(columnNumber)
1405 << ", pcOffset: " + std::to_string(offset);
1406 }
1407 }
1408 LOG_COMPILER(INFO) << "}";
1409 }
1410
PrintCircuitWithTypes() const1411 void MethodTypeInfer::PrintCircuitWithTypes() const
1412 {
1413 LOG_COMPILER(INFO) << "\033[34m"
1414 << "===================="
1415 << " After ts type infer "
1416 << "[" << GetMethodName() << "]"
1417 << "===================="
1418 << "\033[0m";
1419 circuit_->PrintAllGatesWithBytecode();
1420 LOG_COMPILER(INFO) << "\033[34m" << "========================= End ==========================" << "\033[0m";
1421 LOG_COMPILER(INFO) << "";
1422 }
1423
Verify() const1424 void MethodTypeInfer::Verify() const
1425 {
1426 std::vector<GateRef> gateList;
1427 circuit_->GetAllGates(gateList);
1428 for (const auto &gate : gateList) {
1429 if (IsByteCodeGate(gate)) {
1430 TypeCheck(gate);
1431 PGOTypeCheck(gate);
1432 }
1433 }
1434 }
1435
1436 /*
1437 * Let v be a variable in one ts-file and t be a type. To check whether the type of v is t after
1438 * type inferenece, one should declare a function named "AssertType(value:any, type:string):void"
1439 * in ts-file and call it with arguments v and t, where t is the expected type string.
1440 * The following interface performs such a check at compile time.
1441 */
TypeCheck(GateRef gate) const1442 void MethodTypeInfer::TypeCheck(GateRef gate) const
1443 {
1444 auto &info = GetByteCodeInfo(gate);
1445 if (!info.IsBc(EcmaOpcode::CALLARGS2_IMM8_V8_V8)) {
1446 return;
1447 }
1448 auto func = gateAccessor_.GetValueIn(gate, 2); // 2: acc
1449 auto &funcInfo = GetByteCodeInfo(func);
1450 if (!funcInfo.IsBc(EcmaOpcode::TRYLDGLOBALBYNAME_IMM8_ID16) &&
1451 !funcInfo.IsBc(EcmaOpcode::TRYLDGLOBALBYNAME_IMM16_ID16)) {
1452 return;
1453 }
1454 auto funcName = gateAccessor_.GetValueIn(func, 1);
1455 uint16_t funcNameStrId = gateAccessor_.GetConstantValue(funcName);
1456 auto methodOffset = gateAccessor_.TryGetMethodOffset(gate);
1457 auto funcNameString = tsManager_->GetStdStringFromConstantPool(methodOffset, funcNameStrId);
1458 if (funcNameString == "AssertType") {
1459 GateRef expectedGate = gateAccessor_.GetValueIn(gate, 1);
1460 GateRef constId = gateAccessor_.GetValueIn(expectedGate, 0);
1461 uint16_t strId = gateAccessor_.GetConstantValue(constId);
1462 auto expectedTypeStr = tsManager_->GetStdStringFromConstantPool(methodOffset, strId);
1463 GateRef valueGate = gateAccessor_.GetValueIn(gate, 0);
1464 auto type = gateAccessor_.GetGateType(valueGate);
1465 if (expectedTypeStr != tsManager_->GetTypeStr(type)) {
1466 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1467 EntityId methodId = builder_->GetMethod()->GetMethodId();
1468 DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
1469 const std::string &sourceFileName = debugExtractor->GetSourceFile(methodId);
1470 const std::string functionName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
1471
1472 std::string log = CollectGateTypeLogInfo(valueGate, debugExtractor, "[TypeAssertion] ", false);
1473 log += "[TypeAssertion] but expected type: " + expectedTypeStr + "\n";
1474
1475 LOG_COMPILER(ERROR) << "[TypeAssertion] [" << sourceFileName << ":" << functionName << "] begin:";
1476 LOG_COMPILER(FATAL) << log << "[compiler] [TypeAssertion] end";
1477 }
1478 }
1479 }
1480
PGOTypeCheck(GateRef gate) const1481 void MethodTypeInfer::PGOTypeCheck(GateRef gate) const
1482 {
1483 auto &info = GetByteCodeInfo(gate);
1484 if (!info.IsBc(EcmaOpcode::CALLTHIS2_IMM8_V8_V8_V8)) { // ArkTools.pgoAssertType
1485 return ;
1486 }
1487 // 1. thisObj
1488 auto thisObj = gateAccessor_.GetValueIn(gate, 0);
1489 if (!IsByteCodeGate(thisObj)) {
1490 return ;
1491 }
1492 auto &thisObjInfo = GetByteCodeInfo(thisObj);
1493 if (!thisObjInfo.IsBc(EcmaOpcode::TRYLDGLOBALBYNAME_IMM8_ID16) &&
1494 !thisObjInfo.IsBc(EcmaOpcode::TRYLDGLOBALBYNAME_IMM16_ID16)) {
1495 return;
1496 }
1497 auto thisObjName = gateAccessor_.GetValueIn(thisObj, 1);
1498 uint16_t thisObjNameStrId = gateAccessor_.GetConstantValue(thisObjName);
1499 auto methodOffset = gateAccessor_.TryGetMethodOffset(gate);
1500 auto thisObjNameString = tsManager_->GetStdStringFromConstantPool(methodOffset,
1501 thisObjNameStrId);
1502 // 2. funcName
1503 auto func = gateAccessor_.GetValueIn(gate, 3);
1504 if (!IsByteCodeGate(func)) {
1505 return ;
1506 }
1507 auto &funcInfo = GetByteCodeInfo(func);
1508 if (!funcInfo.IsBc(EcmaOpcode::LDOBJBYNAME_IMM8_ID16)) {
1509 return;
1510 }
1511 auto funcName = gateAccessor_.GetValueIn(func, 1);
1512 uint16_t funcNameStrId = gateAccessor_.GetConstantValue(funcName);
1513 auto funcNameString = tsManager_->GetStdStringFromConstantPool(methodOffset, funcNameStrId);
1514 // 3. check whether it is ArkTools.pgoAssertType()
1515 if (thisObjNameString == "ArkTools" && funcNameString == "pgoAssertType") {
1516 // 4. expected type
1517 GateRef expectedGate = gateAccessor_.GetValueIn(gate, 2);
1518 GateRef constId = gateAccessor_.GetValueIn(expectedGate, 0);
1519 uint16_t strId = gateAccessor_.GetConstantValue(constId);
1520 auto expectedTypeStr = tsManager_->GetStdStringFromConstantPool(methodOffset, strId);
1521 // 5. pgo type
1522 GateRef valueGate = gateAccessor_.GetValueIn(gate, 1);
1523 auto pgoType = gateAccessor_.TryGetPGOType(valueGate); // pgo type
1524 auto pgoTypeStr = pgoType.GetPGOSampleType()->ToString();
1525 // 6. compare expected type and pgo type
1526 if (expectedTypeStr != pgoTypeStr) {
1527 const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
1528 EntityId methodId = builder_->GetMethod()->GetMethodId();
1529 DebugInfoExtractor *debugExtractor = JSPandaFileManager::GetInstance()->GetJSPtExtractor(jsPandaFile);
1530 const std::string &sourceFileName = debugExtractor->GetSourceFile(methodId);
1531 const std::string functionName = MethodLiteral::ParseFunctionName(jsPandaFile, methodId);
1532
1533 std::string log = CollectGateTypeLogInfo(valueGate, debugExtractor, "[PGOTypeAssertion] ", true);
1534 log += "[PGOTypeAssertion] but expected type: " + expectedTypeStr + "\n";
1535
1536 LOG_COMPILER(ERROR) << "[PGOTypeAssertion] [" << sourceFileName << ":" << functionName << "] begin:";
1537 LOG_COMPILER(FATAL) << log << "[compiler] [PGOTypeAssertion] end";
1538 }
1539 }
1540 }
1541
CollectGateTypeLogInfo(GateRef gate,DebugInfoExtractor * debugExtractor,const std::string & logPreFix,bool isPGO) const1542 std::string MethodTypeInfer::CollectGateTypeLogInfo(GateRef gate, DebugInfoExtractor *debugExtractor,
1543 const std::string &logPreFix, bool isPGO) const
1544 {
1545 std::string log(logPreFix);
1546 log += "gate id: "+ std::to_string(gateAccessor_.GetId(gate)) + ", ";
1547 OpCode op = gateAccessor_.GetOpCode(gate);
1548 log += "op: " + GateMetaData::Str(op) + ", ";
1549 if (op == OpCode::ARG) {
1550 log += "arg gate, ";
1551 } else if (op != OpCode::VALUE_SELECTOR) {
1552 auto &bytecodeInfo = GetByteCodeInfo(gate);
1553 // handle ByteCode gate: print gate id, bytecode and line number in source code.
1554 log += "bytecode: " + GetEcmaOpcodeStr(bytecodeInfo.GetOpcode()) + ", ";
1555
1556 int32_t lineNumber = 0;
1557 auto callbackLineFunc = [&lineNumber](int32_t line) -> bool {
1558 lineNumber = line + 1;
1559 return true;
1560 };
1561
1562 const auto bcIndex = jsgateToBytecode_.at(gate);
1563 auto offset = builder_->GetPcOffset(bcIndex);
1564 const MethodLiteral *methodLiteral = builder_->GetMethod();
1565 debugExtractor->MatchLineWithOffset(callbackLineFunc, methodLiteral->GetMethodId(), offset);
1566
1567 log += "at line: " + std::to_string(lineNumber) + ", ";
1568 } else {
1569 // handle phi gate: print gate id and input gates id list.
1570 log += "phi gate, ins: ";
1571 auto ins = gateAccessor_.ConstIns(gate);
1572 for (auto it = ins.begin(); it != ins.end(); it++) {
1573 log += std::to_string(gateAccessor_.GetId(*it)) + " ";
1574 }
1575 }
1576
1577 if (!isPGO) {
1578 GateType type = gateAccessor_.GetGateType(gate);
1579 log += "type: " + tsManager_->GetTypeStr(type) + ", ";
1580 if (!tsManager_->IsPrimitiveTypeKind(type)) {
1581 GlobalTSTypeRef gt = type.GetGTRef();
1582 log += "[moduleId: " + std::to_string(gt.GetModuleId()) + ", ";
1583 log += "localId: " + std::to_string(gt.GetLocalId()) + "], ";
1584 }
1585 } else {
1586 auto pgoType = gateAccessor_.TryGetPGOType(gate); // pgo type
1587 log += "pgoType: " + pgoType.GetPGOSampleType()->ToString() + ", ";
1588 }
1589
1590 log += "\n[compiler] ";
1591 return log;
1592 }
1593
VerifyTypePercent()1594 void MethodTypeInfer::VerifyTypePercent()
1595 {
1596 shouldInferNum_ = needInferGates_.size();
1597 for (auto gate : needInferGates_) {
1598 if (!gateAccessor_.GetGateType(gate).IsAnyType()) {
1599 normalInferNum_++;
1600 }
1601 }
1602 double rate = needInferGates_.empty() ? 0.0 : (double)normalInferNum_ / (double)shouldInferNum_;
1603 auto typeThreshold = tsManager_->GetTypeThreshold();
1604 if (rate <= typeThreshold) {
1605 methodInfo_->SetTypeInferAbort(true);
1606 }
1607 if (IsLogEnabled()) {
1608 LOG_COMPILER(INFO) << "====================================================================";
1609 LOG_COMPILER(INFO) << "[TypeCoverage] print method type coverage: \n"
1610 << "[compiler] [TypeCoverage] [ShouldInferedGate]: " << shouldInferNum_
1611 << " || [NormalInferedGate]: " << normalInferNum_ << "\n"
1612 << "[compiler] [TypeCoverage] [TypeCoverage Percentage]: "
1613 << std::fixed << std::setprecision(PERCENT_LENS) << rate * HUNDRED_TIME << "%";
1614 if (rate <= typeThreshold) {
1615 LOG_COMPILER(INFO) << "[TypeCoverage] TypeCoverage Percentage is lower than threshold: ["
1616 << typeThreshold << "]";
1617 }
1618 }
1619 }
1620 } // namespace panda::ecmascript
1621