diff --git a/compiler/src/ets_checker.ts b/compiler/src/ets_checker.ts index f209685721e3a0e114a86c097e70ca44e9d0a06e..8ad8a7c17d14649b5b06c52e3b36f68847d3a99f 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; @@ -447,28 +448,31 @@ 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 = lastTypes && currentTypes && !areEqualArrays(lastTypes, currentTypes); const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; const tsImportSendableDiff: boolean = checkValueDiff(cache?.preTsImportSendable, tsImportSendable); const skipOhModulesLintDiff: boolean = checkValueDiff(cache?.preSkipOhModulesLint, skipOhModulesLint); const enableStrictCheckOHModuleDiff: boolean = checkValueDiff(cache?.preEnableStrictCheckOHModule, enableStrictCheckOHModule); const mixCompileDiff: boolean = checkValueDiff(cache?.preMixCompile, mixCompile); - const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || - tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || enableStrictCheckOHModuleDiff || mixCompileDiff; + const onlyDeleteBuildInfoCache: boolean | undefined = tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || + enableStrictCheckOHModuleDiff || mixCompileDiff || typesDiff; + const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || onlyDeleteBuildInfoCache; if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { needReCheckForChangedDepUsers = true; } if (!service || shouldRebuild) { - rebuildProgram(targetESVersionChanged, tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || - enableStrictCheckOHModuleDiff || mixCompileDiff); + rebuildProgram(targetESVersionDiffers, onlyDeleteBuildInfoCache); service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } else { // Found language service from cache, update root files @@ -480,6 +484,7 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi service: service, pkgJsonFileHash: currentHash, targetESVersion: currentTargetESVersion, + types: currentTypes, maxFlowDepth: currentMaxFlowDepth, preTsImportSendable: tsImportSendable, preSkipOhModulesLint: skipOhModulesLint, @@ -499,13 +504,33 @@ function checkValueDiff(cacheValue: boolean | undefined, currentValue: boolean): return !(cacheValue === undefined && !currentValue) && cacheValue !== currentValue; } -function rebuildProgram(targetESVersionDiffers: boolean, languageSwithchDiffers: boolean): void { +export function areEqualArrays(lastArray: string[] | undefined, currentArray: string[] | undefined): boolean { + if (!lastArray || !currentArray) { + return lastArray === currentArray; + } + + const currentSet: Set = new Set(currentArray!); + const lastSet: Set = 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 (languageSwithchDiffers) { - // 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 1e85db9051064d0fd5fb931ad9a1f924951e7591..543618eb9c72746bb580e0b5871b923246e71b50 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; @@ -467,28 +468,31 @@ 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 = lastTypes && currentTypes && !areEqualArrays(lastTypes, currentTypes); const maxFlowDepthDiffers: boolean | undefined = lastMaxFlowDepth && currentMaxFlowDepth && lastMaxFlowDepth !== currentMaxFlowDepth; const tsImportSendableDiff: boolean = checkValueDiff(cache?.preTsImportSendable, tsImportSendable); const skipOhModulesLintDiff: boolean = checkValueDiff(cache?.preSkipOhModulesLint, skipOhModulesLint); const enableStrictCheckOHModuleDiff: boolean = checkValueDiff(cache?.preEnableStrictCheckOHModule, enableStrictCheckOHModule); const mixCompileDiff: boolean = checkValueDiff(cache?.preMixCompile, mixCompile); - const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || - tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || enableStrictCheckOHModuleDiff || mixCompileDiff; + const onlyDeleteBuildInfoCache: boolean | undefined = tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || + enableStrictCheckOHModuleDiff || mixCompileDiff || typesDiff; + const shouldRebuild: boolean | undefined = shouldRebuildForDepDiffers || targetESVersionDiffers || onlyDeleteBuildInfoCache; if (reuseLanguageServiceForDepChange && hashDiffers && rollupShareObject?.depInfo?.enableIncre) { needReCheckForChangedDepUsers = true; } if (!service || shouldRebuild) { - rebuildProgram(targetESVersionDiffers, tsImportSendableDiff || maxFlowDepthDiffers || skipOhModulesLintDiff || - enableStrictCheckOHModuleDiff || mixCompileDiff); + rebuildProgram(targetESVersionDiffers, onlyDeleteBuildInfoCache); service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry()); } else { // Found language service from cache, update root files @@ -500,6 +504,7 @@ function getOrCreateLanguageService(servicesHost: ts.LanguageServiceHost, rootFi service: service, pkgJsonFileHash: currentHash, targetESVersion: currentTargetESVersion, + types: currentTypes, maxFlowDepth: currentMaxFlowDepth, preTsImportSendable: tsImportSendable, preSkipOhModulesLint: skipOhModulesLint, @@ -519,13 +524,33 @@ function checkValueDiff(cacheValue: boolean | undefined, currentValue: boolean): return !(cacheValue === undefined && !currentValue) && cacheValue !== currentValue; } -function rebuildProgram(targetESVersionDiffers: boolean, languageSwithchDiffers: boolean): void { +export function areEqualArrays(lastArray: string[] | undefined, currentArray: string[] | undefined): boolean { + if (!lastArray || !currentArray) { + return lastArray === currentArray; + } + + const currentSet: Set = new Set(currentArray!); + const lastSet: Set = 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 (languageSwithchDiffers) { - // 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 b0dceb320531ba84cd2c508a7c9ecd40d2a968c2..dd6d070a6b73673260aa672aa6953833a4c7d799 100644 --- a/compiler/test/ark_compiler_ut/ets_checker.test.ts +++ b/compiler/test/ark_compiler_ut/ets_checker.test.ts @@ -39,7 +39,8 @@ import { getMaxFlowDepth, MAX_FLOW_DEPTH_DEFAULT_VALUE, fileCache, - getFileContentWithHash + getFileContentWithHash, + areEqualArrays } from '../../lib/ets_checker'; import { TS_BUILD_INFO_SUFFIX } from '../../lib/pre_define'; import { @@ -216,12 +217,97 @@ 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(); expect(result).to.equal(MAX_FLOW_DEPTH_DEFAULT_VALUE); }); - + mocha.it('1-2: test should return the default value and log a warning when maxFlowDepth is less than the minimum valid value', () => { const invalidMaxFlowDepth = 1999; projectConfig.projectArkOption = { @@ -231,7 +317,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; @@ -243,7 +329,7 @@ mocha.describe('getMaxFlowDepth', () => { const result = getMaxFlowDepth(); expect(result).to.equal(validMaxFlowDepth); }); - + mocha.it('1-4: test should return the value of maxFlowDepth when it is 3000 within the valid range', () => { const validMaxFlowDepth = 3000; projectConfig.projectArkOption = { @@ -265,7 +351,7 @@ mocha.describe('getMaxFlowDepth', () => { const result = getMaxFlowDepth(); expect(result).to.equal(validMaxFlowDepth); }); - + mocha.it('1-6: test should return the default value and log a warning when maxFlowDepth is greater than the maximum valid value', () => { const invalidMaxFlowDepth = 65536; projectConfig.projectArkOption = {