Type infer and transfer between multiple files

1.Collect import info and export info at the level of type infer based on module manager and bytecodes
2.Add topological Sort for Records in compilerdriver
3.Improve the compatibility of pgo update in compilerdriver
4.Add infer strategy for ldexternalmodulevar

Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I6DHT0

Signed-off-by: lijincheng <lijincheng13@huawei.com>
This commit is contained in:
lijincheng 2023-02-07 16:33:15 +08:00
parent 5c4568b321
commit b4b26de43e
32 changed files with 1189 additions and 68 deletions

View File

@ -81,6 +81,7 @@ int Main(const int argc, const char **argv)
bool ret = true;
// ark_aot_compiler running need disable asm interpreter to disable the loading of AOT files.
runtimeOptions.SetEnableAsmInterpreter(false);
runtimeOptions.DisableReportModuleResolvingFailure();
EcmaVM *vm = JSNApi::CreateEcmaVM(runtimeOptions);
if (vm == nullptr) {
LOG_COMPILER(ERROR) << "Cannot Create vm";

View File

@ -15,6 +15,7 @@
#include "ecmascript/compiler/bytecode_info_collector.h"
#include "ecmascript/base/path_helper.h"
#include "ecmascript/compiler/type_recorder.h"
#include "ecmascript/interpreter/interpreter-inl.h"
#include "ecmascript/ts_types/ts_type_parser.h"
@ -64,7 +65,6 @@ void BytecodeInfoCollector::ProcessClasses()
std::map<const uint8_t *, std::pair<size_t, uint32_t>> processedInsns;
Span<const uint32_t> classIndexes = jsPandaFile_->GetClasses();
auto &mainMethodIndexes = bytecodeInfo_.GetMainMethodIndexes();
auto &recordNames = bytecodeInfo_.GetRecordNames();
auto &methodPcInfos = bytecodeInfo_.GetMethodPcInfos();
@ -75,8 +75,9 @@ void BytecodeInfoCollector::ProcessClasses()
}
panda_file::ClassDataAccessor cda(*pf, classId);
CString desc = utf::Mutf8AsCString(cda.GetDescriptor());
cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedInsns, &desc,
&mainMethodIndexes, &recordNames, &methodPcInfos] (panda_file::MethodDataAccessor &mda) {
const CString recordName = JSPandaFile::ParseEntryPoint(desc);
cda.EnumerateMethods([this, methods, &methodIdx, pf, &processedInsns,
&recordNames, &methodPcInfos, &recordName] (panda_file::MethodDataAccessor &mda) {
auto methodId = mda.GetMethodId();
CollectFunctionTypeId(vm_->GetJSThread(), methodId);
@ -86,9 +87,7 @@ void BytecodeInfoCollector::ProcessClasses()
auto methodOffset = methodId.GetOffset();
CString name = reinterpret_cast<const char *>(jsPandaFile_->GetStringData(mda.GetNameId()).data);
if (JSPandaFile::IsEntryOrPatch(name)) {
const CString recordName = JSPandaFile::ParseEntryPoint(desc);
jsPandaFile_->UpdateMainMethodIndex(methodOffset, recordName);
mainMethodIndexes.emplace_back(methodOffset);
recordNames.emplace_back(recordName);
}
@ -110,7 +109,7 @@ void BytecodeInfoCollector::ProcessClasses()
auto it = processedInsns.find(insns);
if (it == processedInsns.end()) {
std::vector<std::string> classNameVec;
CollectMethodPcsFromBC(codeSize, insns, methodLiteral, classNameVec);
CollectMethodPcsFromBC(codeSize, insns, methodLiteral, classNameVec, recordName);
processedInsns[insns] = std::make_pair(methodPcInfos.size() - 1, methodOffset);
// collect className and literal offset for type infer
if (EnableCollectLiteralInfo()) {
@ -122,6 +121,8 @@ void BytecodeInfoCollector::ProcessClasses()
jsPandaFile_->SetMethodLiteralToMap(methodLiteral);
});
}
// Collect import(infer-needed) and export relationship among all records.
CollectRecordReferenceREL();
LOG_COMPILER(INFO) << "Total number of methods in file: "
<< jsPandaFile_->GetJSPandaFileDesc()
<< " is: "
@ -243,7 +244,8 @@ void BytecodeInfoCollector::StoreClassTypeOffset(const uint32_t typeOffset, std:
}
void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr,
const MethodLiteral *method, std::vector<std::string> &classNameVec)
const MethodLiteral *method, std::vector<std::string> &classNameVec,
const CString &recordName)
{
auto bcIns = BytecodeInst(insArr);
auto bcInsLast = bcIns.JumpTo(insSz);
@ -255,6 +257,7 @@ void BytecodeInfoCollector::CollectMethodPcsFromBC(const uint32_t insSz, const u
while (bcIns.GetAddress() != bcInsLast.GetAddress()) {
CollectMethodInfoFromBC(bcIns, method, classNameVec, bcIndex);
CollectModuleInfoFromBC(bcIns, method, recordName);
CollectConstantPoolIndexInfoFromBC(bcIns, method);
curPc = bcIns.GetAddress();
auto nextInst = bcIns.GetNext();
@ -272,6 +275,7 @@ void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset,
uint32_t numOfLexVars = 0;
LexicalEnvStatus status = LexicalEnvStatus::VIRTUAL_LEXENV;
auto &methodList = bytecodeInfo_.GetMethodList();
std::set<uint32_t> indexSet{};
// Methods with the same instructions in abc files have the same static information. Since
// information from bytecodes is collected only once, methods other than the processed method
// will obtain static information from the processed method.
@ -280,6 +284,7 @@ void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset,
const MethodInfo &processedMethod = processedIter->second;
numOfLexVars = processedMethod.GetNumOfLexVars();
status = processedMethod.GetLexEnvStatus();
indexSet = processedMethod.GetImportIndexes();
}
auto iter = methodList.find(methodOffset);
@ -288,10 +293,13 @@ void BytecodeInfoCollector::SetMethodPcInfoIndex(uint32_t methodOffset,
methodInfo.SetMethodPcInfoIndex(processedMethodPcInfoIndex);
methodInfo.SetNumOfLexVars(numOfLexVars);
methodInfo.SetLexEnvStatus(status);
// if these methods have the same bytecode, their import indexs must be the same.
methodInfo.CopyImportIndex(indexSet);
return;
}
MethodInfo info(GetMethodInfoID(), processedMethodPcInfoIndex, LexEnv::DEFAULT_ROOT,
MethodInfo::DEFAULT_OUTMETHOD_OFFSET, numOfLexVars, status);
info.CopyImportIndex(indexSet);
methodList.emplace(methodOffset, info);
}
@ -459,6 +467,210 @@ void BytecodeInfoCollector::CollectMethodInfoFromBC(const BytecodeInstruction &b
}
}
void BytecodeInfoCollector::CollectModuleInfoFromBC(const BytecodeInstruction &bcIns,
const MethodLiteral *method,
const CString &recordName)
{
auto methodOffset = method->GetMethodId().GetOffset();
// For records without tsType, we don't need to collect its export info.
if (jsPandaFile_->HasTSTypes(recordName) && !(bcIns.HasFlag(BytecodeInstruction::Flags::STRING_ID) &&
BytecodeInstruction::HasId(BytecodeInstruction::GetFormat(bcIns.GetOpcode()), 0))) {
BytecodeInstruction::Opcode opcode = static_cast<BytecodeInstruction::Opcode>(bcIns.GetOpcode());
switch (opcode) {
case BytecodeInstruction::Opcode::STMODULEVAR_IMM8: {
auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
// The export syntax only exists in main function.
if (jsPandaFile_->GetMainMethodIndex(recordName) == methodOffset) {
CollectExportIndexs(recordName, imm);
}
break;
}
case BytecodeInstruction::Opcode::WIDE_STMODULEVAR_PREF_IMM16: {
auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
if (jsPandaFile_->GetMainMethodIndex(recordName) == methodOffset) {
CollectExportIndexs(recordName, imm);
}
break;
}
case BytecodeInstruction::Opcode::LDEXTERNALMODULEVAR_IMM8:{
auto imm = bcIns.GetImm<BytecodeInstruction::Format::IMM8>();
CollectImportIndexs(methodOffset, imm);
break;
}
case BytecodeInstruction::Opcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:{
auto imm = bcIns.GetImm<BytecodeInstruction::Format::PREF_IMM16>();
CollectImportIndexs(methodOffset, imm);
break;
}
default:
break;
}
}
}
void BytecodeInfoCollector::CollectImportIndexs(uint32_t methodOffset, uint32_t index)
{
auto &methodList = bytecodeInfo_.GetMethodList();
auto iter = methodList.find(methodOffset);
if (iter != methodList.end()) {
MethodInfo &methodInfo = iter->second;
// Collect import indexs of each method in its MethodInfo to do accurate Pgo compilation analysis.
methodInfo.AddImportIndex(index);
return;
}
MethodInfo info(GetMethodInfoID(), 0, LexEnv::DEFAULT_ROOT);
info.AddImportIndex(index);
methodList.emplace(methodOffset, info);
}
void BytecodeInfoCollector::CollectExportIndexs(const CString &recordName, uint32_t index)
{
ModuleManager *moduleManager = vm_->GetModuleManager();
JSThread *thread = vm_->GetJSThread();
CString exportLocalName = "*default*";
ObjectFactory *objFactory = vm_->GetFactory();
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
if (currentModule->GetLocalExportEntries().IsUndefined()) {
return;
}
// localExportEntries contain all local element info exported in this record.
JSHandle<TaggedArray> localExportArray(thread, currentModule->GetLocalExportEntries());
ASSERT(index < localExportArray->GetLength());
JSHandle<LocalExportEntry> currentExportEntry(thread, localExportArray->Get(index));
JSHandle<JSTaggedValue> exportName(thread, currentExportEntry->GetExportName());
JSHandle<JSTaggedValue> localName(thread, currentExportEntry->GetLocalName());
JSHandle<JSTaggedValue> exportLocalNameHandle =
JSHandle<JSTaggedValue>::Cast(objFactory->NewFromUtf8(exportLocalName));
JSHandle<JSTaggedValue> defaultName = thread->GlobalConstants()->GetHandledDefaultString();
/* if current exportName is "default", but localName not "*default*" like "export default class A{},
* localName is A, exportName is default in exportEntry". this will be recorded as "A:classType" in
* exportTable in typeSystem. At this situation, we will use localName to judge whether it has a actual
* Type record. Otherwise, we will use exportName.
*/
if (JSTaggedValue::SameValue(exportName, defaultName) &&
!JSTaggedValue::SameValue(localName, exportLocalNameHandle)) {
exportName = localName;
}
JSHandle<EcmaString> exportNameStr(thread, EcmaString::Cast(exportName->GetTaggedObject()));
// When a export element whose name is not recorded in exportTypeTable or recorded as Any, we will think it
// as an infer needed type and add it to the ExportRecordInfo of this record.
// What we do is to reduce redundant compilation.
if (!CheckExportName(recordName, exportNameStr)) {
bytecodeInfo_.AddExportIndexToRecord(recordName, index);
}
}
bool BytecodeInfoCollector::CheckExportName(const CString &recordName, const JSHandle<EcmaString> &exportStr)
{
auto tsManager = vm_->GetTSManager();
// To compare with the exportTable, we need to parse the literalbuffer in abc TypeAnnotation.
// If the exportTable already exist, we will use it directly. OtherWise, we will parse and store it.
// In type system parser at a later stage, we will also use these arrays to avoid duplicate parsing.
if (tsManager->HasResolvedExportTable(jsPandaFile_, recordName)) {
JSTaggedValue exportTypeTable = tsManager->GetResolvedExportTable(jsPandaFile_, recordName);
JSHandle<TaggedArray> table(vm_->GetJSThread(), exportTypeTable);
return IsEffectiveExportName(exportStr, table);
}
JSHandle<TaggedArray> newResolvedTable = tsManager->GenerateExportTableFromLiteral(jsPandaFile_, recordName);
return IsEffectiveExportName(exportStr, newResolvedTable);
}
bool BytecodeInfoCollector::IsEffectiveExportName(const JSHandle<EcmaString> &exportNameStr,
const JSHandle<TaggedArray> &exportTypeTable)
{
uint32_t length = exportTypeTable->GetLength();
for (uint32_t i = 0; i < length; i = i + 2) { // 2: skip a pair of key and value
EcmaString *valueString = EcmaString::Cast(exportTypeTable->Get(i).GetTaggedObject());
uint32_t typeId = static_cast<uint32_t>(exportTypeTable->Get(i + 1).GetInt());
if (EcmaStringAccessor::StringsAreEqual(*exportNameStr, valueString) && typeId != 0) {
return true;
}
}
return false;
}
void BytecodeInfoCollector::CollectRecordReferenceREL()
{
auto &recordNames = bytecodeInfo_.GetRecordNames();
for (auto &record : recordNames) {
if (jsPandaFile_->HasTSTypes(record) && jsPandaFile_->IsModule(vm_->GetJSThread(), record)) {
CollectRecordImportInfo(record);
CollectRecordExportInfo(record);
}
}
}
/* Each import index is corresponded to a ResolvedIndexBinding in the Environment of its module.
* Through ResolvedIndexBinding, we can get the export module and its export index. Only when the
* export index is in the non-type-record set which we have collected in CollectExportIndexs function,
* this export element can be infer-needed. We will collect the map as (key: import index , value: (exportRecord,
* exportIndex)) for using in pgo analysis and type infer.
*/
void BytecodeInfoCollector::CollectRecordImportInfo(const CString &recordName)
{
auto thread = vm_->GetJSThread();
ModuleManager *moduleManager = vm_->GetModuleManager();
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
// Collect Import Info
JSTaggedValue moduleEnvironment = currentModule->GetEnvironment();
if (moduleEnvironment.IsUndefined()) {
return;
}
ASSERT(moduleEnvironment.IsTaggedArray());
JSHandle<TaggedArray> moduleArray(thread, moduleEnvironment);
auto length = moduleArray->GetLength();
for (size_t index = 0; index < length; index++) {
JSTaggedValue resolvedBinding = moduleArray->Get(index);
// if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module.
if (resolvedBinding.IsHole()) {
continue;
}
ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
CString resolvedRecord = ModuleManager::GetRecordName(binding->GetModule());
auto bindingIndex = binding->GetIndex();
if (bytecodeInfo_.HasExportIndexToRecord(resolvedRecord, bindingIndex)) {
bytecodeInfo_.AddImportRecordInfoToRecord(recordName, resolvedRecord, index, bindingIndex);
}
}
}
// For type infer under retranmission (export * from "xxx"), we collect the star export records in this function.
void BytecodeInfoCollector::CollectRecordExportInfo(const CString &recordName)
{
auto thread = vm_->GetJSThread();
ModuleManager *moduleManager = vm_->GetModuleManager();
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName);
// Collect Star Export Info
JSTaggedValue starEntries = currentModule->GetStarExportEntries();
if (starEntries.IsUndefined()) {
return;
}
ASSERT(starEntries.IsTaggedArray());
JSHandle<TaggedArray> starEntriesArray(thread, starEntries);
auto starLength = starEntriesArray->GetLength();
JSMutableHandle<StarExportEntry> starExportEntry(thread, JSTaggedValue::Undefined());
for (size_t index = 0; index < starLength; index++) {
starExportEntry.Update(starEntriesArray->Get(index));
JSTaggedValue moduleRequest = starExportEntry->GetModuleRequest();
CString moduleRequestName = ConvertToString(EcmaString::Cast(moduleRequest.GetTaggedObject()));
auto [isNative, _] = ModuleManager::CheckNativeModule(moduleRequestName);
if (isNative) {
return;
}
CString baseFileName = jsPandaFile_->GetJSPandaFileDesc();
CString entryPoint = base::PathHelper::ConcatFileNameWithMerge(thread, jsPandaFile_,
baseFileName, recordName, moduleRequestName);
if (jsPandaFile_->HasTypeSummaryOffset(entryPoint)) {
bytecodeInfo_.AddStarExportToRecord(recordName, entryPoint);
}
}
}
void BytecodeInfoCollector::CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction &bcIns,
const MethodLiteral *method)
{

View File

@ -110,6 +110,81 @@ struct MethodPcInfo {
uint32_t methodsSize {0};
};
// importRecord : ldExternalModuleVar {importIndex} --> {exportRecordName} : stmoduleVar {exportIndex}
class ImportRecordInfo {
public:
ImportRecordInfo() = default;
~ImportRecordInfo() = default;
void AddImportIdAndRecord(uint32_t importId, uint32_t bindingId, const CString &importRecord)
{
records_.insert(importRecord);
if (idToRecord_.find(importId) == idToRecord_.end()) {
idToRecord_.emplace(importId, std::make_pair(importRecord, bindingId));
}
}
const std::set<CString> &GetImportRecords() const
{
return records_;
}
uint32_t GetImportRecordSize() const
{
return records_.size();
}
const std::unordered_map<uint32_t, std::pair<CString, uint32_t>> &GetImportIdToExportRecord() const
{
return idToRecord_;
}
private:
std::set<CString> records_ {};
std::unordered_map<uint32_t, std::pair<CString, uint32_t>> idToRecord_ {};
};
// exportIndex_ {exportIndex1...}: collect bytecode export index whose type has not been recorded.
// starExportRecord_ {recordName A...}: collect recordName when there is Forwarding syntax in this record,
// like "export * from record A, export * from record B"
class ExportRecordInfo {
public:
ExportRecordInfo() = default;
explicit ExportRecordInfo(uint32_t index) : exportIndex_({index}) {}
explicit ExportRecordInfo(const CString &starRecord) : starExportRecord_({starRecord}) {}
~ExportRecordInfo() = default;
bool HasExportIndex(uint32_t index) const
{
return exportIndex_.find(index) != exportIndex_.end();
}
bool HasStarExport() const
{
return !starExportRecord_.empty();
}
void AddExportIndex(uint32_t index)
{
exportIndex_.insert(index);
}
void AddStarExport(const CString &starExportRecord)
{
starExportRecord_.insert(starExportRecord);
}
const std::unordered_set<CString> &GetstarExportRecord() const
{
return starExportRecord_;
}
private:
std::unordered_set<uint32_t> exportIndex_ {};
std::unordered_set<CString> starExportRecord_ {};
};
class MethodInfo {
public:
MethodInfo(uint32_t methodInfoIndex, uint32_t methodPcInfoIndex, uint32_t outMethodIdx,
@ -194,6 +269,21 @@ public:
return innerMethods_;
}
inline void AddImportIndex(uint32_t index)
{
importIndex_.insert(index);
}
inline const std::set<uint32_t> &GetImportIndexes() const
{
return importIndex_;
}
inline void CopyImportIndex(const std::set<uint32_t> &indexSet)
{
importIndex_ = indexSet;
}
bool IsPGO() const
{
return CompileStateBit::PGOBit::Decode(compileState_.value_);
@ -262,6 +352,7 @@ private:
uint32_t outerMethodOffset_ { MethodInfo::DEFAULT_OUTMETHOD_OFFSET };
uint32_t numOfLexVars_ { 0 };
LexicalEnvStatus status_ { LexicalEnvStatus::VIRTUAL_LEXENV };
std::set<uint32_t> importIndex_ {};
CompileStateBit compileState_ { 0 };
};
@ -354,6 +445,8 @@ public:
}
}
// for deopt resolve, when we add new resolve method to compile queue, the recordName vector also need to update
// for seek, its recordName also need to be set correspondingly
void AddRecordName(const CString &recordName)
{
recordNames_.emplace_back(recordName);
@ -422,6 +515,69 @@ public:
functionTypeIdToMethodOffset_.emplace(functionTypeId, methodOffset);
}
}
bool HasExportIndexToRecord(const CString &recordName, uint32_t index) const
{
auto iter = recordNameToExportInfo_.find(recordName);
if (iter != recordNameToExportInfo_.end()) {
return iter->second.HasExportIndex(index);
}
return false;
}
bool HasStarExportToRecord(const CString &recordName) const
{
auto iter = recordNameToExportInfo_.find(recordName);
if (iter != recordNameToExportInfo_.end()) {
return iter->second.HasStarExport();
}
return false;
}
void AddExportIndexToRecord(const CString &recordName, uint32_t index)
{
auto iter = recordNameToExportInfo_.find(recordName);
if (iter != recordNameToExportInfo_.end()) {
iter->second.AddExportIndex(index);
} else {
ExportRecordInfo info(index);
recordNameToExportInfo_.emplace(recordName, std::move(info));
}
}
void AddStarExportToRecord(const CString &recordName, const CString &starRecord)
{
auto iter = recordNameToExportInfo_.find(recordName);
if (iter != recordNameToExportInfo_.end()) {
iter->second.AddStarExport(starRecord);
} else {
ExportRecordInfo info(starRecord);
recordNameToExportInfo_.emplace(recordName, std::move(info));
}
}
const std::unordered_set<CString> &GetstarExportToRecord(const CString &recordName) const
{
return recordNameToExportInfo_.at(recordName).GetstarExportRecord();
}
void AddImportRecordInfoToRecord(const CString &recordName, const CString &importRecord,
uint32_t importIndex, uint32_t bindingIndex)
{
auto iter = recordToImportRecordsInfo_.find(recordName);
if (iter == recordToImportRecordsInfo_.end()) {
ImportRecordInfo info;
info.AddImportIdAndRecord(importIndex, bindingIndex, importRecord);
recordToImportRecordsInfo_.emplace(recordName, std::move(info));
} else {
iter->second.AddImportIdAndRecord(importIndex, bindingIndex, importRecord);
}
}
const std::unordered_map<CString, ImportRecordInfo> &GetImportRecordsInfos() const
{
return recordToImportRecordsInfo_;
}
private:
std::vector<uint32_t> mainMethodIndexes_ {};
std::vector<CString> recordNames_ {};
@ -433,6 +589,8 @@ private:
size_t maxMethodSize_;
std::unordered_map<uint32_t, uint32_t> classTypeLOffsetToDefMethod_ {};
std::unordered_map<uint32_t, uint32_t> functionTypeIdToMethodOffset_ {};
std::unordered_map<CString, ExportRecordInfo> recordNameToExportInfo_ {};
std::unordered_map<CString, ImportRecordInfo> recordToImportRecordsInfo_ {};
};
class LexEnvManager {
@ -542,7 +700,7 @@ private:
const CString GetEntryFunName(const std::string_view &entryPoint) const;
void ProcessClasses();
void CollectMethodPcsFromBC(const uint32_t insSz, const uint8_t *insArr,
const MethodLiteral *method, std::vector<std::string> &classNameVec);
const MethodLiteral *method, std::vector<std::string> &classNameVec, const CString &recordName);
void SetMethodPcInfoIndex(uint32_t methodOffset, const std::pair<size_t, uint32_t> &processedMethodInfo);
void CollectInnerMethods(const MethodLiteral *method, uint32_t innerMethodOffset);
void CollectInnerMethods(uint32_t methodId, uint32_t innerMethodOffset);
@ -551,11 +709,22 @@ private:
void CollectInnerMethodsFromNewLiteral(const MethodLiteral *method, panda_file::File::EntityId literalId);
void CollectMethodInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method,
std::vector<std::string> &classNameVec, int32_t bcIndex);
void CollectModuleInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method,
const CString &recordName);
void CollectConstantPoolIndexInfoFromBC(const BytecodeInstruction &bcIns, const MethodLiteral *method);
void IterateLiteral(const MethodLiteral *method, std::vector<uint32_t> &classOffsetVector);
void StoreClassTypeOffset(const uint32_t typeOffset, std::vector<uint32_t> &classOffsetVector);
void CollectClassLiteralInfo(const MethodLiteral *method, const std::vector<std::string> &classNameVec);
void CollectFunctionTypeId(JSThread *thread, panda_file::File::EntityId fieldId);
void CollectImportIndexs(uint32_t methodOffset, uint32_t index);
void CollectExportIndexs(const CString &recordName, uint32_t index);
bool CheckExportName(const CString &recordName,
const JSHandle<EcmaString> &exportStr);
bool IsEffectiveExportName(const JSHandle<EcmaString> &exportNameStr,
const JSHandle<TaggedArray> &exportTypeTable);
void CollectRecordReferenceREL();
void CollectRecordImportInfo(const CString &recordName);
void CollectRecordExportInfo(const CString &recordName);
EcmaVM *vm_;
JSPandaFile *jsPandaFile_ {nullptr};

View File

@ -31,6 +31,67 @@ CompilationDriver::~CompilationDriver()
vm_->GetTSManager()->SetCompilationDriver(nullptr);
}
void CompilationDriver::TopologicalSortForRecords()
{
const auto &importRecordsInfos = bytecodeInfo_.GetImportRecordsInfos();
std::queue<CString> recordList;
std::unordered_map<CString, uint32_t> recordInDegree;
std::unordered_map<CString, std::vector<CString>> exportRecords;
std::vector<CString> &tpOrder = bytecodeInfo_.GetRecordNames();
for (auto &record : tpOrder) {
auto iter = importRecordsInfos.find(record);
if (iter == importRecordsInfos.end()) {
recordInDegree.emplace(record, 0);
recordList.emplace(record);
} else {
recordInDegree.emplace(record, iter->second.GetImportRecordSize());
}
}
tpOrder.clear();
for (auto iter = importRecordsInfos.begin(); iter != importRecordsInfos.end(); iter++) {
const auto &importRecords = iter->second.GetImportRecords();
for (const auto &import : importRecords) {
if (exportRecords.find(import) != exportRecords.end()) {
exportRecords[import].emplace_back(iter->first);
} else {
exportRecords.emplace(import, std::vector<CString>{iter->first});
}
}
}
while (!recordList.empty()) {
auto curRecord = recordList.front();
tpOrder.emplace_back(curRecord);
recordList.pop();
auto iter = exportRecords.find(curRecord);
if (iter != exportRecords.end()) {
for (const auto &ele : iter->second) {
if (recordInDegree[ele] > 0 && --recordInDegree[ele] == 0) {
recordList.emplace(ele);
}
}
}
}
if (UNLIKELY(tpOrder.size() != recordInDegree.size())) {
LOG_COMPILER(INFO) << "There are circular references in records";
for (auto &it : recordInDegree) {
if (it.second != 0) {
tpOrder.emplace_back(it.first);
}
}
ASSERT(tpOrder.size() == recordInDegree.size());
}
auto &mainMethods = bytecodeInfo_.GetMainMethodIndexes();
auto sortId = 0;
for (auto &it : tpOrder) {
mainMethods.emplace_back(jsPandaFile_->GetMainMethodIndex(it));
sortedRecords_.emplace(it, sortId++);
}
ASSERT(tpOrder.size() == mainMethods.size());
}
void CompilationDriver::UpdatePGO()
{
std::unordered_set<EntityId> newMethodIds;
@ -41,7 +102,7 @@ void CompilationDriver::UpdatePGO()
return newMethodIds;
}
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
SearchForCompilation(oldIds, newMethodIds, mainMethodOffset, false);
SearchForCompilation(recordName, oldIds, newMethodIds, mainMethodOffset, false);
return newMethodIds;
};
pfLoader_.Update(dfs);
@ -49,9 +110,10 @@ void CompilationDriver::UpdatePGO()
void CompilationDriver::InitializeCompileQueue()
{
TopologicalSortForRecords();
auto &mainMethodIndexes = bytecodeInfo_.GetMainMethodIndexes();
for (auto mainMethodIndex : mainMethodIndexes) {
compileQueue_.push(mainMethodIndex);
compileQueue_.push_back(mainMethodIndex);
}
}

View File

@ -46,7 +46,8 @@ public:
fullResolvedMethodSet.clear();
std::unordered_set<EntityId> currentResolvedMethodSet {resolvedMethod};
uint32_t mainMethodOffset = jsPandaFile_->GetMainMethodIndex(recordName);
SearchForCompilation(currentResolvedMethodSet, fullResolvedMethodSet, mainMethodOffset, true);
SearchForCompilation(recordName, currentResolvedMethodSet,
fullResolvedMethodSet, mainMethodOffset, true);
return fullResolvedMethodSet;
};
@ -71,7 +72,7 @@ public:
while (!compileQueue_.empty()) {
std::queue<uint32_t> methodCompiledOrder;
methodCompiledOrder.push(compileQueue_.front());
compileQueue_.pop();
compileQueue_.pop_front();
while (!methodCompiledOrder.empty()) {
auto compilingMethod = methodCompiledOrder.front();
methodCompiledOrder.pop();
@ -116,6 +117,8 @@ public:
UpdateCompileQueue(recordName, resolvedMethodId);
}
private:
void TopologicalSortForRecords();
void UpdatePGO();
void InitializeCompileQueue();
@ -125,30 +128,126 @@ private:
return methodInfo.IsTypeInferAbort() && methodInfo.IsResolvedMethod();
}
void SearchForCompilation(const std::unordered_set<EntityId> &methodSet, std::unordered_set<EntityId> &newMethodSet,
void AddDependList(const CString &recordName, uint32_t methodOffset,
std::deque<CString> &dependList)
{
auto &methodList = bytecodeInfo_.GetMethodList();
const auto &importRecordInfos = bytecodeInfo_.GetImportRecordsInfos();
auto iter = importRecordInfos.find(recordName);
// if the resolved method don't have import records need-inferred, just return.
if (iter == importRecordInfos.end()) {
return;
}
auto &methodInfo = methodList.at(methodOffset);
// Get the import indexs collected in methodInfo.
auto &importIndexs = methodInfo.GetImportIndexes();
// idInRecord is a map like "importIndex: (exportRecord: exportIndex)".
const auto &idInRecord = iter->second.GetImportIdToExportRecord();
for (auto index : importIndexs) {
auto it = idInRecord.find(index);
if (it != idInRecord.end()) {
dependList.emplace_back(it->second.first);
}
}
}
bool VerifyAndMarkCurMethod(uint32_t methodOffset, std::unordered_set<EntityId> &newMethodSet)
{
// if current method is at the boundary state we should stop the define chain search.
if (methodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) {
return false;
}
// if pgo profile is not matched with current abc file, methodOffset will be different.
auto &methodList = bytecodeInfo_.GetMethodList();
auto iter = methodList.find(methodOffset);
if (iter == methodList.end()) {
LOG_COMPILER(ERROR) << "The correct aot profile has not been used";
return false;
}
auto &methodInfo = iter->second;
// if current method has already been marked as PGO, stop searching upper layer of the define chain.
if (methodInfo.IsPGO() && !methodInfo.IsTypeInferAbort()) {
return false;
}
// we need to collect these new-marked PGO methods to update PGO profile
panda_file::File::EntityId methodId(methodOffset);
newMethodSet.insert(std::move(methodId));
methodInfo.SetIsPGO(true);
return true;
}
void UpdateResolveDepends(std::vector<CString> &dependNames, bool needUpdate)
{
if (needUpdate && !dependNames.empty()) {
// depend methods should keep the topological sorting rule
std::sort(dependNames.begin(), dependNames.end(), [this](auto first, auto second) {
return sortedRecords_.at(first) < sortedRecords_.at(second);
});
auto resolvedMethod = compileQueue_.back();
compileQueue_.pop_back();
// it should be like "depended main method1, depended main method2, resolved method" in compileQueue.
for (const auto &ele : dependNames) {
bytecodeInfo_.AddRecordName(ele);
auto eleOffset = jsPandaFile_->GetMainMethodIndex(ele);
// these methods will be added to compile queue
bytecodeInfo_.EraseSkippedMethod(eleOffset);
compileQueue_.push_back(eleOffset);
}
compileQueue_.push_back(resolvedMethod);
}
}
void SearchForCompilation(const CString &recordName, const std::unordered_set<EntityId> &methodSet,
std::unordered_set<EntityId> &newMethodSet,
uint32_t mainMethodOffset, bool needUpdateCompile)
{
auto &methodList = bytecodeInfo_.GetMethodList();
std::function<void(EntityId, bool)> dfs = [this, &newMethodSet, &mainMethodOffset, &dfs, &methodList]
std::unordered_set<EntityId> mainMethodSet;
auto getMainMethodSet = [this, &mainMethodSet](const CString &importRecord,
[[maybe_unused]] const std::unordered_set<EntityId> &oldIds) -> std::unordered_set<EntityId>& {
mainMethodSet.clear();
auto mainMethodOffset = jsPandaFile_->GetMainMethodIndex(importRecord);
panda_file::File::EntityId mainMethodId(mainMethodOffset);
mainMethodSet.insert(mainMethodId);
return mainMethodSet;
};
std::vector<CString> importNames{};
std::function<void(EntityId)> importBfs =
[this, &methodList, &recordName, &importNames, &getMainMethodSet]
(EntityId methodId) -> void {
uint32_t methodOffset = methodId.GetOffset();
std::deque<CString> importList{};
AddDependList(recordName, methodOffset, importList);
while (!importList.empty()) {
auto importRecord = importList.front();
importList.pop_front();
// export syntax only exists in main method, so just judge and collect the main method.
auto mainMethodOffset = jsPandaFile_->GetMainMethodIndex(importRecord);
auto &mainMethodInfo = methodList.at(mainMethodOffset);
// mark the main method in other record as PGO.
if (mainMethodInfo.IsPGO()) {
continue;
}
importNames.emplace_back(importRecord);
mainMethodInfo.SetIsPGO(true);
pfLoader_.Update(importRecord, getMainMethodSet);
AddDependList(importRecord, mainMethodOffset, importList);
}
};
std::function<void(EntityId, bool)> dfs =
[this, &newMethodSet, &mainMethodOffset, &dfs, &methodList, &importBfs]
(EntityId methodId, bool needUpdateCompile) -> void {
uint32_t methodOffset = methodId.GetOffset();
if (methodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) {
return;
}
// if pgo profile is not matched with current abc file, methodOffset will be different
if (methodList.find(methodOffset) == methodList.end()) {
LOG_COMPILER(ERROR) << "The correct aot profile has not been used";
// verify whether we should stop the search for pgo methods or resolve methods.
if (!VerifyAndMarkCurMethod(methodOffset, newMethodSet)) {
return;
}
auto &methodInfo = methodList.at(methodOffset);
auto outMethodOffset = methodInfo.GetOutMethodOffset();
// if current method has already been marked as PGO, stop searching upper layer of the define chain
if (methodInfo.IsPGO() && !methodInfo.IsTypeInferAbort()) {
return;
}
// we need to collect these new-marked PGO methods to update PGO profile
newMethodSet.insert(methodId);
methodInfo.SetIsPGO(true);
// for pgo method, collect its import depends method
importBfs(methodId);
if (needUpdateCompile) {
// in deopt, we need to push the first un-marked method which is
// in upper layer of the deopt method's define chain (or maybe the deopt method itself)
@ -156,18 +255,18 @@ private:
if (methodOffset != mainMethodOffset) {
// few methods which have the same bytecodes as other method can't find its outter method
if (outMethodOffset == MethodInfo::DEFAULT_OUTMETHOD_OFFSET) {
compileQueue_.push(methodOffset);
compileQueue_.push_back(methodOffset);
return;
}
// currentMethod whose outtermethod has been marked as pgo need to push into queue
auto outMethodInfo = methodList.at(outMethodOffset);
if (outMethodInfo.IsPGO()) {
compileQueue_.push(methodOffset);
compileQueue_.push_back(methodOffset);
return;
}
} else {
// if current searched method is an un-marked main method, just push it to compile queue
compileQueue_.push(methodOffset);
compileQueue_.push_back(methodOffset);
}
}
if (methodOffset == mainMethodOffset) {
@ -181,6 +280,8 @@ private:
for (auto pgoMethod = methodSet.begin(); pgoMethod != methodSet.end(); pgoMethod++) {
dfs(*pgoMethod, needUpdateCompile);
}
// update compile queue only for resolve method when it depends on other record
UpdateResolveDepends(importNames, needUpdateCompile);
}
bool FilterMethod(const CString &recordName, const MethodLiteral *methodLiteral,
@ -190,7 +291,8 @@ private:
const JSPandaFile *jsPandaFile_ {nullptr};
PGOProfilerLoader &pfLoader_;
BCInfo &bytecodeInfo_;
std::queue<uint32_t> compileQueue_ {};
std::deque<uint32_t> compileQueue_ {};
std::map<CString, uint32_t> sortedRecords_ {};
};
} // namespace panda::ecmascript::kungfu
#endif // ECMASCRIPT_COMPILER_COMPILATION_DRIVER_H

View File

@ -167,11 +167,14 @@ void PassManager::ResolveModule(const JSPandaFile *jsPandaFile, const std::strin
const auto &recordInfo = jsPandaFile->GetJSRecordInfo();
ModuleManager *moduleManager = vm_->GetModuleManager();
JSThread *thread = vm_->GetJSThread();
[[maybe_unused]] EcmaHandleScope scope(thread);
for (auto info: recordInfo) {
auto recordName = info.first;
if (jsPandaFile->IsModule(thread, recordName)) {
ASSERT(!thread->HasPendingException());
moduleManager->HostResolveImportedModuleWithMerge(fileName.c_str(), recordName);
JSHandle<JSTaggedValue> moduleRecord = moduleManager->HostResolveImportedModuleWithMerge(fileName.c_str(),
recordName);
SourceTextModule::Instantiate(thread, moduleRecord);
}
}
}

View File

@ -327,6 +327,9 @@ bool TypeInfer::Infer(GateRef gate)
case EcmaOpcode::LDLOCALMODULEVAR_IMM8:
case EcmaOpcode::WIDE_LDLOCALMODULEVAR_PREF_IMM16:
return InferLdLocalModuleVar(gate);
case EcmaOpcode::LDEXTERNALMODULEVAR_IMM8:
case EcmaOpcode::WIDE_LDEXTERNALMODULEVAR_PREF_IMM16:
return InferLdExternalModuleVar(gate);
default:
break;
}
@ -930,7 +933,7 @@ bool TypeInfer::InferStModuleVar(GateRef gate)
auto defineGate = gateAccessor_.GetValueIn(gate, 1);
auto defineType = gateAccessor_.GetGateType(defineGate);
if (!defineType.IsAnyType()) {
tsManager_->AddTypeToLocalModuleVarGtMap(jsPandaFile, recordName_, index, defineType.GetGTRef());
tsManager_->AddTypeToModuleVarGtMap(jsPandaFile, recordName_, index, defineType.GetGTRef());
return true;
}
return false;
@ -947,6 +950,55 @@ bool TypeInfer::InferLdLocalModuleVar(GateRef gate)
return UpdateType(gate, type);
}
bool TypeInfer::InferLdExternalModuleVar(GateRef gate)
{
auto index = gateAccessor_.GetConstantValue(gateAccessor_.GetValueIn(gate, 0));
auto loadType = gateAccessor_.GetGateType(gate);
auto bcInfoCollector = tsManager_->GetBytecodeInfoCollector();
ASSERT(bcInfoCollector != nullptr);
const auto &bcInfo = bcInfoCollector->GetBytecodeInfo();
const auto &importRecordsInfos = bcInfo.GetImportRecordsInfos();
auto iter = importRecordsInfos.find(recordName_);
const JSPandaFile *jsPandaFile = builder_->GetJSPandaFile();
CString resolvedRecord = "";
uint32_t resolvedIndex = 0;
if (loadType.IsAnyType() && iter != importRecordsInfos.end()) {
const auto &importIdToExportRecord = iter->second.GetImportIdToExportRecord();
if (importIdToExportRecord.find(index) != importIdToExportRecord.end()) {
std::tie(resolvedRecord, resolvedIndex) = importIdToExportRecord.at(index);
if (tsManager_->HasExportGT(jsPandaFile, resolvedRecord, resolvedIndex)) {
return UpdateType(gate, tsManager_->GetGTFromModuleMap(jsPandaFile, resolvedRecord, resolvedIndex));
}
}
}
// if we can't find type in exportRecords, we will try to find type using resolved index binding directly.
// However, this compilation order is not guaranteed, so the export type may not have been infered.
if (UNLIKELY(loadType.IsAnyType())) {
auto thread = tsManager_->GetEcmaVM()->GetJSThread();
ModuleManager *moduleManager = tsManager_->GetEcmaVM()->GetModuleManager();
[[maybe_unused]] EcmaHandleScope scope(thread);
JSHandle<SourceTextModule> currentModule = moduleManager->HostGetImportedModule(recordName_);
JSTaggedValue moduleEnvironment = currentModule->GetEnvironment();
if (moduleEnvironment.IsUndefined()) {
return UpdateType(gate, GateType::AnyType());
}
ASSERT(moduleEnvironment.IsTaggedArray());
JSHandle<TaggedArray> moduleArray(thread, moduleEnvironment);
JSTaggedValue resolvedBinding = moduleArray->Get(index);
// if resolvedBinding.IsHole(), means that importname is * or it belongs to empty Aot module.
if (resolvedBinding.IsHole()) {
return UpdateType(gate, GateType::AnyType());
}
ResolvedIndexBinding *binding = ResolvedIndexBinding::Cast(resolvedBinding.GetTaggedObject());
resolvedRecord = ModuleManager::GetRecordName(binding->GetModule());
resolvedIndex = binding->GetIndex();
if (tsManager_->HasExportGT(jsPandaFile, resolvedRecord, resolvedIndex)) {
return UpdateType(gate, tsManager_->GetGTFromModuleMap(jsPandaFile, resolvedRecord, resolvedIndex));
}
}
return UpdateType(gate, GateType::AnyType());
}
bool TypeInfer::InferLoopBeginPhiGate(GateRef gate)
{
// loop-begin phi gate has 3 ins: loop_begin(stateWire), loopInGate(valueWire), loopBackGate(valueWire)
@ -1189,7 +1241,7 @@ void TypeInfer::VerifyTypePercent()
normalInferNum_++;
}
}
double rate = (double)normalInferNum_ / (double)shouldInferNum_;
double rate = needInferGates_.empty() ? 0.0 : (double)normalInferNum_ / (double)shouldInferNum_;
auto typeThreshold = tsManager_->GetTypeThreshold();
if (rate <= typeThreshold) {
methodInfo_->SetTypeInferAbort(true);

View File

@ -116,6 +116,7 @@ private:
bool InferStLexVarDyn(GateRef gate);
bool InferStModuleVar(GateRef gate);
bool InferLdLocalModuleVar(GateRef gate);
bool InferLdExternalModuleVar(GateRef gate);
bool IsNewLexEnv(EcmaOpcode opcode) const;
bool InferGetIterator(GateRef gate);
bool InferLoopBeginPhiGate(GateRef gate);

View File

@ -442,6 +442,18 @@ CString JSHClass::DumpJSType(JSType type)
return "ClassLiteral";
case JSType::VTABLE:
return "VTable";
case JSType::SOURCE_TEXT_MODULE_RECORD:
return "SourceTextModuleRecord";
case JSType::RESOLVEDBINDING_RECORD:
return "ResolvedBingingRecord";
case JSType::RESOLVEDINDEXBINDING_RECORD:
return "ResolvedIndexBingingRecord";
case JSType::IMPORTENTRY_RECORD:
return "ImportEntry";
case JSType::LOCAL_EXPORTENTRY_RECORD:
return "LocalExportEntry";
case JSType::STAR_EXPORTENTRY_RECORD:
return "StarExportEntry";
default: {
CString ret = "unknown type ";
return ret + static_cast<char>(type);

View File

@ -459,6 +459,11 @@ public:
HeapProfilerInterface *GetOrNewHeapProfile();
#endif
bool EnableReportModuleResolvingFailure() const
{
return options_.EnableReportModuleResolvingFailure();
}
void SetAssetPath(const CString &assetPath)
{
assetPath_ = assetPath;

View File

@ -384,6 +384,16 @@ public:
return (static_cast<uint32_t>(arkProperties_) & ArkProperties::ENABLE_SNAPSHOT_DESERIALIZE) != 0;
}
void DisableReportModuleResolvingFailure()
{
reportModuleResolvingFailure_ = false;
}
bool EnableReportModuleResolvingFailure() const
{
return reportModuleResolvingFailure_;
}
bool WasSetMaxNonmovableSpaceCapacity() const
{
return WasOptionSet(OPTION_MAX_NONMOVABLE_SPACE_CAPACITY);
@ -958,6 +968,7 @@ private:
uint64_t wasSet_ {0};
bool enablePrintExecuteTime_ {false};
bool enablePGOProfiler_ {false};
bool reportModuleResolvingFailure_ {true};
uint32_t pgoHotnessThreshold_ {2};
std::string pgoProfilerPath_ {""};
bool traceDeopt_ {false};

View File

@ -329,6 +329,26 @@ bool ModuleManager::IsImportedModuleLoaded(JSTaggedValue referencing)
return (entry != -1);
}
bool ModuleManager::SkipDefaultBundleFile(const CString &moduleFileName) const
{
// just to skip misunderstanding error log in LoadJSPandaFile when we ignore Module Resolving Failure.
return !vm_->EnableReportModuleResolvingFailure() &&
base::StringHelper::StringStartWith(moduleFileName, PathHelper::BUNDLE_INSTALL_PATH);
}
JSHandle<JSTaggedValue> ModuleManager::ResolveModuleInMergedABC(JSThread *thread, const JSPandaFile *jsPandaFile,
const CString &recordName)
{
// In static parse Phase, due to lack of some parameters, we will create a empty SourceTextModule which will
// be marked as INSTANTIATED to skip Dfs traversal of this import branch.
if (!vm_->EnableReportModuleResolvingFailure() && (jsPandaFile == nullptr ||
(jsPandaFile != nullptr && !jsPandaFile->HasRecord(recordName)))) {
return CreateEmptyModule();
} else {
return ResolveModuleWithMerge(thread, jsPandaFile, recordName);
}
}
JSHandle<JSTaggedValue> ModuleManager::HostResolveImportedModuleWithMerge(const CString &moduleFileName,
const CString &recordName)
{
@ -341,14 +361,19 @@ JSHandle<JSTaggedValue> ModuleManager::HostResolveImportedModuleWithMerge(const
if (entry != -1) {
return JSHandle<JSTaggedValue>(thread, dict->GetValue(entry));
}
std::shared_ptr<JSPandaFile> jsPandaFile =
std::shared_ptr<JSPandaFile> jsPandaFile = SkipDefaultBundleFile(moduleFileName) ? nullptr :
JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, moduleFileName, recordName, false);
if (jsPandaFile == nullptr) {
CString msg = "Load file with filename '" + moduleFileName + "' failed, recordName '" + recordName + "'";
THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str());
// In Aot Module Instantiate, we miss some runtime parameters from framework like bundleName or moduleName
// which may cause wrong recordName parsing and we also can't load files not in this app hap. But in static
// phase, these should not be an error, just skip it is ok.
if (vm_->EnableReportModuleResolvingFailure()) {
CString msg = "Load file with filename '" + moduleFileName + "' failed, recordName '" + recordName + "'";
THROW_NEW_ERROR_AND_RETURN_HANDLE(thread, ErrorType::REFERENCE_ERROR, JSTaggedValue, msg.c_str());
}
}
JSHandle<JSTaggedValue> moduleRecord = ResolveModuleWithMerge(thread, jsPandaFile.get(), recordName);
JSHandle<JSTaggedValue> moduleRecord = ResolveModuleInMergedABC(thread, jsPandaFile.get(), recordName);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
JSHandle<NameDictionary> handleDict(thread, resolvedModules_);
resolvedModules_ = NameDictionary::Put(thread, handleDict, JSHandle<JSTaggedValue>(recordNameHandle),
@ -357,6 +382,20 @@ JSHandle<JSTaggedValue> ModuleManager::HostResolveImportedModuleWithMerge(const
return moduleRecord;
}
JSHandle<JSTaggedValue> ModuleManager::CreateEmptyModule()
{
if (!cachedEmptyModule_.IsHole()) {
return JSHandle<JSTaggedValue>(vm_->GetJSThread(), cachedEmptyModule_);
}
ObjectFactory *factory = vm_->GetFactory();
JSHandle<SourceTextModule> tmpModuleRecord = factory->NewSourceTextModule();
tmpModuleRecord->SetStatus(ModuleStatus::INSTANTIATED);
tmpModuleRecord->SetTypes(ModuleTypes::ECMA_MODULE);
tmpModuleRecord->SetIsNewBcVersion(true);
cachedEmptyModule_ = tmpModuleRecord.GetTaggedValue();
return JSHandle<JSTaggedValue>::Cast(tmpModuleRecord);
}
JSHandle<JSTaggedValue> ModuleManager::HostResolveImportedModule(const CString &referencingModule)
{
JSThread *thread = vm_->GetJSThread();
@ -574,6 +613,7 @@ JSTaggedValue ModuleManager::GetModuleNamespaceInternal(JSTaggedValue localName,
void ModuleManager::Iterate(const RootVisitor &v)
{
v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&resolvedModules_)));
v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&cachedEmptyModule_)));
}
CString ModuleManager::GetRecordName(JSTaggedValue module)

View File

@ -91,6 +91,10 @@ private:
NO_COPY_SEMANTIC(ModuleManager);
NO_MOVE_SEMANTIC(ModuleManager);
bool SkipDefaultBundleFile(const CString &moduleFileName) const;
JSHandle<JSTaggedValue> ResolveModuleInMergedABC(JSThread *thread, const JSPandaFile *jsPandaFile,
const CString &recordName);
JSHandle<JSTaggedValue> CreateEmptyModule();
JSTaggedValue GetModuleValueOutterInternal(int32_t index, JSTaggedValue currentModule);
void StoreModuleValueInternal(JSHandle<SourceTextModule> &currentModule,
int32_t index, JSTaggedValue value);
@ -109,7 +113,7 @@ private:
JSHandle<JSTaggedValue> ResolveModule(JSThread *thread, const JSPandaFile *jsPandaFile);
JSHandle<JSTaggedValue> ResolveModuleWithMerge(JSThread *thread, const JSPandaFile *jsPandaFile,
const CString &recodeName);
const CString &recordName);
static Local<JSValueRef> GetRequireNativeModuleFunc(EcmaVM *vm, ModuleTypes moduleType);
static CString GetStrippedModuleName(const CString &moduleRequestName);
@ -118,6 +122,7 @@ private:
EcmaVM *vm_ {nullptr};
JSTaggedValue resolvedModules_ {JSTaggedValue::Hole()};
JSTaggedValue cachedEmptyModule_ {JSTaggedValue::Hole()};
bool isExecuteBuffer_ {false};
friend class EcmaVM;

View File

@ -208,7 +208,10 @@ JSHandle<JSTaggedValue> SourceTextModule::ResolveExport(JSThread *thread, const
}
// 6. If SameValue(exportName, "default") is true, then
JSHandle<JSTaggedValue> defaultString = globalConstants->GetHandledDefaultString();
if (JSTaggedValue::SameValue(exportName, defaultString)) {
// In Aot static parse phase, some importModule maybe empty aot module, all elements will be undefined, it will
// return hole for resolve index binding at the end to skip error.
if (JSTaggedValue::SameValue(exportName, defaultString) &&
thread->GetEcmaVM()->EnableReportModuleResolvingFailure()) {
// a. Assert: A default export was not explicitly defined by this module.
// b. Return null.
// c. NOTE: A default export cannot be provided by an export *.
@ -219,6 +222,10 @@ JSHandle<JSTaggedValue> SourceTextModule::ResolveExport(JSThread *thread, const
// 8. For each ExportEntry Record e in module.[[StarExportEntries]], do
JSTaggedValue starExportEntriesTv = module->GetStarExportEntries();
if (starExportEntriesTv.IsUndefined()) {
// return Hole in Aot static parse phase to skip error.
if (!thread->GetEcmaVM()->EnableReportModuleResolvingFailure()) {
starResolution.Update(JSTaggedValue::Hole());
}
return starResolution;
}
JSMutableHandle<StarExportEntry> ee(thread, globalConstants->GetUndefined());
@ -1197,6 +1204,13 @@ JSHandle<JSTaggedValue> SourceTextModule::GetStarResolution(JSThread *thread,
JSHandle<JSTaggedValue> resolution =
SourceTextModule::ResolveExport(thread, importedModule, exportName, resolveVector);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread);
// if step into GetStarResolution in aot phase, the module must be a normal SourceTextModule not an empty
// aot module. Sometimes for normal module, if indirectExportEntries, localExportEntries, starExportEntries
// all don't have right exportName which means the export element is not from this module,
// it should return null but now will be hole.
if (!thread->GetEcmaVM()->EnableReportModuleResolvingFailure() && resolution->IsHole()) {
return globalConstants->GetHandledNull();
}
// c. If resolution is "ambiguous", return "ambiguous".
if (resolution->IsString()) { // if resolution is string, resolution must be "ambiguous"
return globalConstants->GetHandledAmbiguousString();

View File

@ -20,6 +20,7 @@
#include "ecmascript/jspandafile/js_pandafile_manager.h"
#include "ecmascript/jspandafile/program_object.h"
#include "ecmascript/subtyping_operator.h"
#include "ecmascript/ts_types/ts_type_parser.h"
#include "ecmascript/ts_types/ts_type_table_generator.h"
#include "ecmascript/vtable.h"
@ -372,6 +373,9 @@ void TSManager::Iterate(const RootVisitor &v)
for (auto iter : gtIhcMap_) {
iter.second.Iterate(v);
}
for (auto &exportTable : resolvedExportTable_) {
v(Root::ROOT_VM, ObjectSlot(reinterpret_cast<uintptr_t>(&resolvedExportTable_.at(exportTable.first))));
}
}
JSHandle<TSTypeTable> TSManager::GetTSTypeTable(int entry) const
@ -1216,6 +1220,15 @@ kungfu::GateType TSManager::TryNarrowUnionType(kungfu::GateType gateType)
return gateType;
}
JSHandle<TaggedArray> TSManager::GenerateExportTableFromLiteral(const JSPandaFile *jsPandaFile,
const CString &recordName)
{
TSTypeParser parser(this);
JSHandle<TaggedArray> typeOfExportedSymbols = parser.GetExportDataFromRecord(jsPandaFile, recordName);
AddResolvedExportTable(jsPandaFile, recordName, typeOfExportedSymbols.GetTaggedValue());
return typeOfExportedSymbols;
}
bool TSManager::IsBuiltinMath(kungfu::GateType funcType) const
{
DISALLOW_GARBAGE_COLLECTION;

View File

@ -119,13 +119,23 @@ enum class BuiltinTypeId : uint8_t { // keep the same with enum BuiltinType in
TYPED_ARRAY_LAST = BIG_UINT64_ARRAY,
};
struct LocalModuleInfo {
struct ModuleInfo {
const JSPandaFile *jsPandaFile {nullptr}; // there may be serval merged abc files.
const CString recordName {""}; // distinguish different files which are all merged to a abc file.
uint32_t index {0}; // bytecode "ldlocalmodulevar index", importVar index is unique in the same recordName.
bool operator < (const LocalModuleInfo &localModuleInfo) const
const CString recordName {""}; // distinguish different files which are all merged to one abc file.
uint32_t index {0}; // bytecode "stmoudlevar index", exportVar index is unique in the same record.
bool operator < (const ModuleInfo &moduleInfo) const
{
return index < localModuleInfo.index;
if (index < moduleInfo.index) {
return true;
}
if (index == moduleInfo.index && recordName < moduleInfo.recordName) {
return true;
}
if (index == moduleInfo.index && recordName == moduleInfo.recordName &&
jsPandaFile < moduleInfo.jsPandaFile) {
return true;
}
return false;
}
};
@ -459,29 +469,52 @@ public:
return gtLiteralOffsetMap_.at(gt);
}
inline void AddTypeToLocalModuleVarGtMap(const JSPandaFile *jsPandaFile, const CString &recordName,
uint32_t index, GlobalTSTypeRef gt)
inline void AddTypeToModuleVarGtMap(const JSPandaFile *jsPandaFile, const CString &recordName,
uint32_t index, GlobalTSTypeRef gt)
{
LocalModuleInfo key = {jsPandaFile, recordName, index};
if (localModuleVarGtMap_.find(key) == localModuleVarGtMap_.end()) {
localModuleVarGtMap_.emplace(key, gt);
ModuleInfo key = {jsPandaFile, recordName, index};
if (moduleVarGtMap_.find(key) == moduleVarGtMap_.end()) {
moduleVarGtMap_.emplace(key, gt);
} else {
localModuleVarGtMap_[key] = gt;
moduleVarGtMap_[key] = gt;
}
}
inline bool HasExportGT(const JSPandaFile *jsPandaFile, const CString &recordName,
uint32_t index)
uint32_t index) const
{
LocalModuleInfo key = {jsPandaFile, recordName, index};
return localModuleVarGtMap_.find(key) != localModuleVarGtMap_.end();
ModuleInfo key = {jsPandaFile, recordName, index};
return moduleVarGtMap_.find(key) != moduleVarGtMap_.end();
}
inline GlobalTSTypeRef GetGTFromModuleMap(const JSPandaFile *jsPandaFile, const CString &recordName,
uint32_t index)
uint32_t index) const
{
LocalModuleInfo key = {jsPandaFile, recordName, index};
return localModuleVarGtMap_.at(key);
ModuleInfo key = {jsPandaFile, recordName, index};
return moduleVarGtMap_.at(key);
}
inline void AddResolvedExportTable(const JSPandaFile *jsPandaFile, const CString &recordName,
JSTaggedValue exportTable)
{
auto key = std::make_pair(jsPandaFile, recordName);
if (resolvedExportTable_.find(key) == resolvedExportTable_.end()) {
resolvedExportTable_.emplace(key, exportTable);
} else {
resolvedExportTable_[key] = exportTable;
}
}
inline bool HasResolvedExportTable(const JSPandaFile *jsPandaFile, const CString &recordName) const
{
auto key = std::make_pair(jsPandaFile, recordName);
return resolvedExportTable_.find(key) != resolvedExportTable_.end();
}
inline JSTaggedValue GetResolvedExportTable(const JSPandaFile *jsPandaFile, const CString &recordName) const
{
auto key = std::make_pair(jsPandaFile, recordName);
return resolvedExportTable_.at(key);
}
bool IsTSIterator(GlobalTSTypeRef gt) const
@ -660,6 +693,8 @@ public:
kungfu::GateType TryNarrowUnionType(kungfu::GateType gateType);
JSHandle<TaggedArray> GenerateExportTableFromLiteral(const JSPandaFile *jsPandaFile, const CString &recordName);
private:
NO_COPY_SEMANTIC(TSManager);
NO_MOVE_SEMANTIC(TSManager);
@ -740,7 +775,7 @@ private:
std::vector<uint32_t> builtinOffsets_ {};
JSPandaFile *builtinPandaFile_ {nullptr};
CString builtinsRecordName_ {""};
std::map<LocalModuleInfo, GlobalTSTypeRef> localModuleVarGtMap_{};
std::map<ModuleInfo, GlobalTSTypeRef> moduleVarGtMap_{};
kungfu::CompilationDriver *cmpDriver_ {nullptr};
std::set<GlobalTSTypeRef> collectedTypeOffsets_ {}; // use for storing types that need to generate hclasses
@ -748,6 +783,8 @@ private:
std::map<std::pair<const JSPandaFile *, uint32_t>, std::string> literalOffsetClassNameMap_ {};
kungfu::BytecodeInfoCollector *bcInfoCollector_ {nullptr};
// use for collect the literal of export type table.
CMap<std::pair<const JSPandaFile *, CString>, JSTaggedValue> resolvedExportTable_ {};
};
} // namespace panda::ecmascript

View File

@ -24,6 +24,17 @@
#include "libpandafile/class_data_accessor-inl.h"
namespace panda::ecmascript {
TSTypeParser::TSTypeParser(TSManager *tsManager)
: tsManager_(tsManager), vm_(tsManager->GetEcmaVM()),
thread_(vm_->GetJSThread()), factory_(vm_->GetFactory()),
tableGenerator_(tsManager_)
{
auto bcInfoCollector = tsManager_->GetBytecodeInfoCollector();
if (bcInfoCollector != nullptr) {
bcInfo_ = bcInfoCollector->GetBytecodeInfoPtr();
}
}
GlobalTSTypeRef TSTypeParser::CreateGT(const JSPandaFile *jsPandaFile, const CString &recordName, uint32_t typeId)
{
if (typeId <= BUILDIN_TYPE_OFFSET) {
@ -133,7 +144,8 @@ GlobalTSTypeRef TSTypeParser::ResolveImportType(const JSPandaFile *jsPandaFile,
JSHandle<JSTaggedValue> exportTable = GenerateExportTableFromRecord(jsPandaFile, entryPoint, table);
JSHandle<TaggedArray> arrayWithGT(exportTable);
JSHandle<EcmaString> targetVarName = GenerateImportVar(importVarNamePath);
GlobalTSTypeRef importedGT = GetExportGTByName(targetVarName, arrayWithGT);
std::unordered_set<CString> markSet;
GlobalTSTypeRef importedGT = GetExportGTByName(targetVarName, arrayWithGT, jsPandaFile, entryPoint, markSet);
return GetAndStoreImportGT(jsPandaFile, typeId, recordName, importedGT);
}
@ -489,7 +501,13 @@ JSHandle<JSTaggedValue> TSTypeParser::GenerateExportTableFromRecord(const JSPand
JSHandle<JSTaggedValue> exportValeTable = TSTypeTable::GetExportValueTable(thread_, table);
if (exportValeTable->IsUndefined()) {
// Read export-data from annotation of the .abc File
JSHandle<TaggedArray> exportTable = GetExportDataFromRecord(jsPandaFile, recordName);
JSHandle<TaggedArray> exportTable;
if (tsManager_->HasResolvedExportTable(jsPandaFile, recordName)) {
JSTaggedValue resolvedTable = tsManager_->GetResolvedExportTable(jsPandaFile, recordName);
exportTable = JSHandle<TaggedArray>::Cast(JSHandle<JSTaggedValue>(thread_, resolvedTable));
} else {
exportTable = GetExportDataFromRecord(jsPandaFile, recordName);
}
uint32_t length = exportTable->GetLength();
// Replace typeIds with GT
@ -528,7 +546,9 @@ JSHandle<EcmaString> TSTypeParser::GenerateImportVar(JSHandle<EcmaString> import
return factory_->NewFromUtf8(target); // #A#./A -> A
}
GlobalTSTypeRef TSTypeParser::GetExportGTByName(JSHandle<EcmaString> target, JSHandle<TaggedArray> &exportTable) const
GlobalTSTypeRef TSTypeParser::GetExportGTByName(JSHandle<EcmaString> target, JSHandle<TaggedArray> &exportTable,
const JSPandaFile *jsPandaFile, const CString &recordName,
std::unordered_set<CString> &markSet)
{
uint32_t length = exportTable->GetLength();
// the exportTable is arranged as follows ["A", "101", "B", "102"]
@ -540,6 +560,35 @@ GlobalTSTypeRef TSTypeParser::GetExportGTByName(JSHandle<EcmaString> target, JSH
return GlobalTSTypeRef(exportTable->Get(i + 1).GetInt());
}
}
// if we can't find the exportName in exportTable of this record, we will try to search in its starExportRecords.
return IterateStarExport(target, jsPandaFile, recordName, markSet);
}
GlobalTSTypeRef TSTypeParser::IterateStarExport(JSHandle<EcmaString> target, const JSPandaFile *jsPandaFile,
const CString &recordName, std::unordered_set<CString> &markSet)
{
if (bcInfo_->HasStarExportToRecord(recordName)) {
const auto &starRecords = bcInfo_->GetstarExportToRecord(recordName);
markSet.insert(recordName);
JSMutableHandle<TaggedArray> starTable(thread_, JSTaggedValue::Undefined());
for (const auto &star : starRecords) {
// use markSet to avoid circular import
if (markSet.find(star) != markSet.end()) {
continue;
}
uint32_t starModuleId = tableGenerator_.TryGetModuleId(star);
if (UNLIKELY(!GlobalTSTypeRef::IsVaildModuleId(starModuleId))) {
continue;
}
JSHandle<TSTypeTable> table = tableGenerator_.GetOrGenerateTSTypeTable(jsPandaFile, star, starModuleId);
starTable.Update(GenerateExportTableFromRecord(jsPandaFile, star, table));
// the target name will be the same under retransmission.
auto gt = GetExportGTByName(target, starTable, jsPandaFile, star, markSet);
if (!gt.IsDefault()) {
return gt;
}
}
}
return GlobalTSTypeRef::Default();
}
} // namespace panda::ecmascript

View File

@ -31,14 +31,13 @@ namespace panda::ecmascript {
*/
class TSTypeParser {
public:
explicit TSTypeParser(TSManager *tsManager)
: tsManager_(tsManager), vm_(tsManager->GetEcmaVM()),
thread_(vm_->GetJSThread()), factory_(vm_->GetFactory()),
tableGenerator_(tsManager_) {}
explicit TSTypeParser(TSManager *tsManager);
~TSTypeParser() = default;
GlobalTSTypeRef PUBLIC_API CreateGT(const JSPandaFile *jsPandaFile, const CString &recordName, uint32_t typeId);
JSHandle<TaggedArray> GetExportDataFromRecord(const JSPandaFile *jsPandaFile, const CString &recordName);
static constexpr size_t USER_DEFINED_TYPE_OFFSET = 100;
private:
@ -117,8 +116,6 @@ private:
uint32_t startIndex, uint32_t lastIndex,
uint32_t &index);
JSHandle<TaggedArray> GetExportDataFromRecord(const JSPandaFile *jsPandaFile, const CString &recordName);
JSHandle<JSTaggedValue> GenerateExportTableFromRecord(const JSPandaFile *jsPandaFile, const CString &recordName,
const JSHandle<TSTypeTable> &table);
@ -126,13 +123,19 @@ private:
JSHandle<EcmaString> GenerateImportVar(JSHandle<EcmaString> import) const;
GlobalTSTypeRef GetExportGTByName(JSHandle<EcmaString> target, JSHandle<TaggedArray> &exportTable) const;
GlobalTSTypeRef GetExportGTByName(JSHandle<EcmaString> target, JSHandle<TaggedArray> &exportTable,
const JSPandaFile *jsPandaFile, const CString &recordName,
std::unordered_set<CString> &markSet);
GlobalTSTypeRef IterateStarExport(JSHandle<EcmaString> target, const JSPandaFile *jsPandaFile,
const CString &recordName, std::unordered_set<CString> &markSet);
TSManager *tsManager_ {nullptr};
EcmaVM *vm_ {nullptr};
JSThread *thread_ {nullptr};
ObjectFactory *factory_ {nullptr};
TSTypeTableGenerator tableGenerator_;
kungfu::BCInfo *bcInfo_ {nullptr};
};
} // panda::ecmascript
#endif // ECMASCRIPT_TS_TYPES_TS_TYPE_PARSER_H

View File

@ -16,6 +16,9 @@ group("ark_typeinfer_module_test") {
test_list = [
"module_class",
"module_function",
"module_function_infer",
"module_multi_import",
"module_retransmission",
]
deps = []

View File

@ -0,0 +1,20 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_typeinfer_test_action("module_function_infer") {
deps = []
is_multi_file_tests = true
is_enable_es2abc = false
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
export function foo1 (v:number, s:number):number {
return v + s;
}
export function foo2 (s:number):number {
return s;
}
AssertType(foo1, "(number, number) => number");
AssertType(foo1(123, 456), "number");
AssertType(foo2, "(number) => number");
AssertType(foo2(123), "number");
export let foo3 = foo1(123, 456) + foo2(123);

View File

@ -0,0 +1,22 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
import {foo1, foo2, foo3} from "./export"
AssertType(foo1, "(number, number) => number");
AssertType(foo2, "(number) => number");
AssertType(foo3, "number");

View File

@ -0,0 +1,20 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_typeinfer_test_action("module_multi_import") {
deps = []
is_multi_file_tests = true
is_enable_es2abc = false
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
import { Test2 } from "./export2";
import { t2, Test3 } from "./export3";
let t0 = new Test3(456)
export let t1 = Test2(t0, t2) + t2;
AssertType(t1, "double");

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
import { Test3 , t2} from "./export3";
export function Test2(a1 : Test3, a2 : number) : number{
return a1.foo() + a2;
}
let t0 = new Test3(456)
let t1 = Test2(t0, 123);
let t3 = t1 + t2;
AssertType(t0, "Test3");
AssertType(Test2, "(Test3, number) => number");
AssertType(t1, "number");
AssertType(t3, "double");

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
export class Test3 {
value : number;
constructor(t1 : number) {
this.value = t1;
}
foo() : number {
return this.value;
}
}
let t1 = new Test3(123);
export let t2 = t1.foo() + 2.2;
AssertType(t1, "Test3");
AssertType(t1.value, "number");
AssertType(t1.foo(), "number");
AssertType(t2, "double");

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
import { t1 } from "./export1";
AssertType(t1, "double");

View File

@ -0,0 +1,20 @@
# Copyright (c) 2023 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_typeinfer_test_action("module_retransmission") {
deps = []
is_multi_file_tests = true
is_enable_es2abc = false
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
export class Test1 {
value1:string;
constructor(value1:string) {
this.value1 = value1;
}
foo(): string {
return this.value1;
}
}
export function foo2(a1 : Test1, a2 : string) :string {
return a1.foo() + a2;
}
let t1 = new Test1("abc");
AssertType(t1, "Test1");
AssertType(t1.value1, "string");
AssertType(t1.foo(), "string");
AssertType(foo2, "(Test1, string) => string");
AssertType(foo2(t1, "def"), "string");

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from "./export"
export * from "./export3"

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
export class Test2 {
value : number;
constructor(t1 : number) {
this.value = t1;
}
foo() : number {
return this.value;
}
}
let t1 = new Test2(123);
AssertType(t1, "Test2");
AssertType(t1.value, "number");
AssertType(t1.foo(), "number");

View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
declare function AssertType(value:any, type:string):void;
import {Test1, Test2, foo2} from "./export2"
let t1 = new Test1("abc");
let t2 = new Test2(123);
let t3 = foo2(t1, "def")
AssertType(t1, "Test1");
AssertType(t2, "Test2");
AssertType(t3, "string");