diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index 716d75244c5fed4ebfac63401cfc2f77aa6daec1..85857608ebabbccacdb40cc2ed35e125bbb312e8 100644 --- a/compiler/src/ets_checker.ts +++ b/compiler/src/ets_checker.ts @@ -110,6 +110,7 @@ export interface LanguageServiceCache { service?: ts.LanguageService; pkgJsonFileHash?: string; targetESVersion?: ts.ScriptTarget; + types?: string[]; maxFlowDepth?: number; preTsImportSendable?: boolean; preSkipOhModulesLint?: boolean; @@ -437,30 +438,34 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi let service: ts.LanguageService | undefined = cache?.service; const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash; const currentTargetESVersion: ts.ScriptTarget = compilerOptions.target; + const currentTypes: string[] | undefined = compilerOptions.types; const currentMaxFlowDepth: number | undefined = compilerOptions.maxFlowDepth; const lastHash: string | undefined = cache?.pkgJsonFileHash; const lastTargetESVersion: ts.ScriptTarget | undefined = cache?.targetESVersion; + const lastTypes: string[] | undefined = cache?.types; const lastMaxFlowDepth: number | undefined = cache?.maxFlowDepth; const hashDiffers: boolean | undefined = currentHash && lastHash && currentHash !== lastHash; const shouldRebuildForDepDiffers: boolean | undefined = reuseLanguageServiceForDepChange ? (hashDiffers && !rollupShareObject?.depInfo?.enableIncre) : hashDiffers; const targetESVersionDiffers: boolean | undefined = lastTargetESVersion && currentTargetESVersion && lastTargetESVersion !== currentTargetESVersion; + const typesDiff: boolean | undefined = !areEqualArrays(lastTypes, currentTypes); const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; const tsImportSendableDiff: boolean = (cache?.preTsImportSendable === undefined && !tsImportSendable) ? false : cache?.preTsImportSendable !== tsImportSendable; - const skipOhModulesLintDiff: boolean = (cache?.preSkipOhModulesLint === undefined && !skipOhModulesLint) ? + const skipOhModulesLintDiff: boolean = (cache?.preSkipOhModulesLint === undefined && !skipOhModulesLint) ? false : cache?.preSkipOhModulesLint !== skipOhModulesLint; - const mixCompileDiff: boolean = (cache?.preMixCompile === undefined && !mixCompile) ? + const mixCompileDiff: boolean = (cache?.preMixCompile === undefined && !mixCompile) ? false : cache?.preMixCompile !== mixCompile; const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || - tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; + tsImportSendableDiff || typesDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; + const onlyDeleteBuildInfoCache: boolean | undefined = tsImportSendableDiff || typesDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { needReCheckForChangedDepUsers = true; } if (!service || shouldRebuild) { - rebuildProgram(targetESVersionDiffers, tsImportSendableDiff, maxFlowDepthDiffers, skipOhModulesLintDiff, mixCompileDiff); + rebuildProgram(targetESVersionDiffers, onlyDeleteBuildInfoCache); service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } else { // Found language service from cache, update root files @@ -472,6 +477,7 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi service: service, pkgJsonFileHash: currentHash, targetESVersion: currentTargetESVersion, + types: currentTypes, maxFlowDepth: currentMaxFlowDepth, preTsImportSendable: tsImportSendable, preSkipOhModulesLint: skipOhModulesLint, @@ -481,14 +487,33 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi return service; } -function rebuildProgram(targetESVersionDiffers: boolean | undefined, tsImportSendableDiff: boolean, - maxFlowDepthDiffers: boolean | undefined, skipOhModulesLintDiff: boolean, mixCompileDiff: boolean): void { +export function areEqualArrays(lastArray: string[] | undefined, currentArray: string[] | undefined): boolean { + if (!lastArray || !currentArray) { + return lastArray === currentArray; + } + + const currentSet = new Set(currentArray); + const lastSet = new Set(lastArray); + + if (lastSet.size !== currentSet.size) { + return false; + } + + for (const item of lastSet) { + if (!currentSet.has(item)) { + return false; + } + } + return true; +} + +function rebuildProgram(targetESVersionDiffers: boolean | undefined, onlyDeleteBuildInfoCache: boolean | undefined): void { if (targetESVersionDiffers) { // If the targetESVersion is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); targetESVersionChanged = true; - } else if (tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff) { - // When tsImportSendable or maxFlowDepth is changed, we need to delete the build info cahce files + } else if (onlyDeleteBuildInfoCache) { + // When tsImportSendable or types or maxFlowDepth or skipOhModuleslint or mixCompile is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); } } diff --git a/compiler/src/interop/src/ets_checker.ts b/compiler/src/interop/src/ets_checker.ts index e1d5381b33b6c10d13b7e985ec997e2fda472c1a..fe11635c6cd432dba84750710c0be9a158187ed2 100644 --- a/compiler/src/interop/src/ets_checker.ts +++ b/compiler/src/interop/src/ets_checker.ts @@ -123,6 +123,7 @@ export interface LanguageServiceCache { service?: ts.LanguageService; pkgJsonFileHash?: string; targetESVersion?: ts.ScriptTarget; + types?: string[]; maxFlowDepth?: number; preTsImportSendable?: boolean; preSkipOhModulesLint?: boolean; @@ -457,14 +458,17 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi let service: ts.LanguageService | undefined = cache?.service; const currentHash: string | undefined = rollupShareObject?.projectConfig?.pkgJsonFileHash; const currentTargetESVersion: ts.ScriptTarget = compilerOptions.target; + const currentTypes: string[] | undefined = compilerOptions.types; const currentMaxFlowDepth: number | undefined = compilerOptions.maxFlowDepth; const lastHash: string | undefined = cache?.pkgJsonFileHash; const lastTargetESVersion: ts.ScriptTarget | undefined = cache?.targetESVersion; + const lastTypes: string[] | undefined = cache?.types; const lastMaxFlowDepth: number | undefined = cache?.maxFlowDepth; const hashDiffers: boolean | undefined = currentHash && lastHash && currentHash !== lastHash; const shouldRebuildForDepDiffers: boolean | undefined = reuseLanguageServiceForDepChange ? (hashDiffers && !rollupShareObject?.depInfo?.enableIncre) : hashDiffers; const targetESVersionDiffers: boolean | undefined = lastTargetESVersion && currentTargetESVersion && lastTargetESVersion !== currentTargetESVersion; + const typesDiff: boolean | undefined = !areEqualArrays(lastTypes, currentTypes); const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; const tsImportSendableDiff: boolean = (cache?.preTsImportSendable === undefined && !tsImportSendable) ? false : @@ -474,13 +478,14 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi const mixCompileDiff: boolean = (cache?.preMixCompile === undefined && !mixCompile) ? false : cache?.preMixCompile !== mixCompile; const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || - tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; + tsImportSendableDiff || typesDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; + const onlyDeleteBuildInfoCache: boolean | undefined = tsImportSendableDiff || typesDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff; if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { needReCheckForChangedDepUsers = true; } if (!service || shouldRebuild) { - rebuildProgram(targetESVersionDiffers, tsImportSendableDiff, maxFlowDepthDiffers, skipOhModulesLintDiff, mixCompileDiff); + rebuildProgram(targetESVersionDiffers, onlyDeleteBuildInfoCache); service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } else { // Found language service from cache, update root files @@ -492,6 +497,7 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi service: service, pkgJsonFileHash: currentHash, targetESVersion: currentTargetESVersion, + types: currentTypes, maxFlowDepth: currentMaxFlowDepth, preTsImportSendable: tsImportSendable, preSkipOhModulesLint: skipOhModulesLint, @@ -501,14 +507,33 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi return service; } -function rebuildProgram(targetESVersionDiffers: boolean | undefined, tsImportSendableDiff: boolean, - maxFlowDepthDiffers: boolean | undefined, skipOhModulesLintDiff: boolean, mixCompileDiff: boolean): void { +export function areEqualArrays(lastArray: string[] | undefined, currentArray: string[] | undefined): boolean { + if (!lastArray || !currentArray) { + return lastArray === currentArray; + } + + const currentSet = new Set(currentArray); + const lastSet = new Set(lastArray); + + if (lastSet.size !== currentSet.size) { + return false; + } + + for (const item of lastSet) { + if (!currentSet.has(item)) { + return false; + } + } + return true; +} + +function rebuildProgram(targetESVersionDiffers: boolean | undefined, onlyDeleteBuildInfoCache: boolean | undefined): void { if (targetESVersionDiffers) { // If the targetESVersion is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); targetESVersionChanged = true; - } else if (tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || mixCompileDiff) { - // When tsImportSendable or maxFlowDepth is changed, we need to delete the build info cahce files + } else if (onlyDeleteBuildInfoCache) { + // When tsImportSendable or types or maxFlowDepth or skipOhModuleslint or mixCompile is changed, we need to delete the build info cahce files deleteBuildInfoCache(compilerOptions.tsBuildInfoFile); } } diff --git a/compiler/test/ark_compiler_ut/ets_checker.test.ts b/compiler/test/ark_compiler_ut/ets_checker.test.ts index 17d258b70485ffcc9f1e1c2eb791f18ad9280467..4a1785e74031b8b7ffe8abfcbcf4992065db2f91 100644 --- a/compiler/test/ark_compiler_ut/ets_checker.test.ts +++ b/compiler/test/ark_compiler_ut/ets_checker.test.ts @@ -36,6 +36,7 @@ import { resetEtsCheck, serviceChecker, resolveModuleNames as resolveModuleNamesMain, + areEqualArrays, getMaxFlowDepth, MAX_FLOW_DEPTH_DEFAULT_VALUE, fileCache, @@ -204,6 +205,91 @@ mocha.describe('test ets_checker file api', function () { }); }); +mocha.describe('test areEqualArrays function', () => { + // test cases for basic functions + mocha.it('1-1: should return true for identical arrays', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['a', 'b', 'c'])).to.equal(true); + }); + + mocha.it('1-2: should return true for arrays with same elements in different order', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['c', 'a', 'b'])).to.equal(true); + }); + + mocha.it('1-3: should return false for arrays with different elements', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['a', 'b', 'd'])).to.equal(false); + }); + + mocha.it('1-4: should return false for arrays with different lengths', () => { + expect(areEqualArrays(['a', 'b', 'c'], ['a', 'b'])).to.equal(false); + expect(areEqualArrays(['a', 'b'], ['a', 'b', 'c'])).to.equal(false); + }); + + // test cases for boundary conditions + mocha.it('1-5: should return true for empty arrays', () => { + expect(areEqualArrays([], [])).to.equal(true); + }); + + mocha.it('1-6: should return false when one array is empty and other is not', () => { + expect(areEqualArrays([], ['a'])).to.equal(false); + expect(areEqualArrays(['a'], [])).to.equal(false); + }); + + // test cases for null and undefined scenarios + mocha.it('1-7: should return true when both arrays are undefined', () => { + expect(areEqualArrays(undefined, undefined)).to.equal(true); + }); + + mocha.it('1-8: should return true when both arrays are null', () => { + expect(areEqualArrays(null as any, null as any)).to.equal(true); + }); + + mocha.it('1-9: should return false when one array is undefined and other is not', () => { + expect(areEqualArrays(undefined, ['a'])).to.equal(false); + expect(areEqualArrays(['a'], undefined)).to.equal(false); + }); + + mocha.it('1-10: should return false when one array is null and other is not', () => { + expect(areEqualArrays(null as any, ['a'])).to.equal(false); + expect(areEqualArrays(['a'], null as any)).to.equal(false); + }); + + // test cases for duplicate elements + mocha.it('1-11: should handle duplicate elements correctly', () => { + expect(areEqualArrays(['a', 'a', 'b'], ['a', 'b', 'a'])).to.equal(true); + expect(areEqualArrays(['a', 'a', 'b'], ['a', 'b', 'b'])).to.equal(true); + }); + + // test case sensitivity scenarios + mocha.it('1-12: should be case sensitive', () => { + expect(areEqualArrays(['A', 'B'], ['a', 'b'])).to.equal(false); + expect(areEqualArrays(['A', 'B'], ['A', 'B'])).to.equal(true); + }); + + // test cases for special value scenarios + mocha.it('1-13: should handle special values', () => { + expect(areEqualArrays(['', 'null'], ['', 'null'])).to.equal(true); + expect(areEqualArrays(['0', '1'], ['0', '1'])).to.equal(true); + }); + + // test cases for performance + mocha.it('1-14: should handle large arrays efficiently', () => { + const largeArray1 = Array(10000).fill('item'); + const largeArray2 = Array(10000).fill('item'); + + expect(areEqualArrays(largeArray1, largeArray2)).to.equal(true); + }); + + // test cases for the edge condition + mocha.it('1-15: should return false for arrays with same length but different content', () => { + expect(areEqualArrays(['a', 'b'], ['a', 'c'])).to.equal(false); + }); + + mocha.it('1-16: should return true for single element arrays', () => { + expect(areEqualArrays(['a'], ['a'])).to.equal(true); + expect(areEqualArrays(['a'], ['b'])).to.equal(false); + }); + }); + mocha.describe('getMaxFlowDepth', () => { mocha.it('1-1: test should return the default value when maxFlowDepth is undefined', () => { const result = getMaxFlowDepth(); @@ -219,7 +305,7 @@ mocha.describe('getMaxFlowDepth', () => { } const result = getMaxFlowDepth(); expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE); - }); + }); mocha.it('1-3: test should return the value of maxFlowDepth when it is 2000 within the valid range', () => { const validMaxFlowDepth = 2000;