Skip to content

fix(amazonq): switch to ulong to avoid overflow when input is larger than 2gb #5558

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type" : "bugfix",
"description" : "Fix integer overflow when local context index input is larger than 2GB"
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@

row(message("aws.settings.codewhisperer.project_context_index_thread")) {
intTextField(
range = IntRange(0, 50)
range = CodeWhispererSettings.CONTEXT_INDEX_THREADS

Check warning on line 136 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt#L136

Added line #L136 was not covered by tests
).bindIntText(codeWhispererSettings::getProjectContextIndexThreadCount, codeWhispererSettings::setProjectContextIndexThreadCount)
.apply {
connect.subscribe(
Expand All @@ -150,7 +150,7 @@

row(message("aws.settings.codewhisperer.project_context_index_max_size")) {
intTextField(
range = IntRange(1, 4096)
range = CodeWhispererSettings.CONTEXT_INDEX_SIZE

Check warning on line 153 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/settings/CodeWhispererConfigurable.kt#L153

Added line #L153 was not covered by tests
).bindIntText(codeWhispererSettings::getProjectContextIndexMaxSize, codeWhispererSettings::setProjectContextIndexMaxSize)
.apply {
connect.subscribe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,41 @@ class CodeWhispererSettingsTest : CodeWhispererTestBase() {
assertThat(actual.autoBuildSetting).hasSize(1)
assertThat(actual.autoBuildSetting["project1"]).isTrue()
}

@Test
fun `context thread count is returned in range`() {
val sut = CodeWhispererSettings.getInstance()

mapOf(
1 to 1,
0 to 0,
-1 to 0,
123 to 50,
50 to 50,
51 to 50,
).forEach { s, expected ->
sut.setProjectContextIndexThreadCount(s)
assertThat(sut.getProjectContextIndexThreadCount()).isEqualTo(expected)
}
}

@Test
fun `context index size is returned in range`() {
val sut = CodeWhispererSettings.getInstance()

mapOf(
1 to 1,
0 to 1,
-1 to 1,
123 to 123,
2047 to 2047,
4096 to 4096,
4097 to 4096,
).forEach { s, expected ->
sut.setProjectContextIndexMaxSize(s)
assertThat(sut.getProjectContextIndexMaxSize()).isEqualTo(expected)
}
}
}

class CodeWhispererSettingUnitTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@

data class FileCollectionResult(
val files: List<String>,
val fileSize: Int,
val fileSize: Int, // in MB

Check warning on line 73 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L73

Added line #L73 was not covered by tests
)

// TODO: move to LspMessage.kt
Expand Down Expand Up @@ -241,59 +241,7 @@
}
}

private fun willExceedPayloadLimit(currentTotalFileSize: Long, currentFileSize: Long): Boolean {
val maxSize = CodeWhispererSettings.getInstance().getProjectContextIndexMaxSize()
return currentTotalFileSize.let { totalSize -> totalSize > (maxSize * 1024 * 1024 - currentFileSize) }
}

private fun isBuildOrBin(fileName: String): Boolean {
val regex = Regex("""bin|build|node_modules|venv|\.venv|env|\.idea|\.conda""", RegexOption.IGNORE_CASE)
return regex.find(fileName) != null
}

fun collectFiles(): FileCollectionResult {
val collectedFiles = mutableListOf<String>()
var currentTotalFileSize = 0L
val allFiles = mutableListOf<VirtualFile>()

val projectBaseDirectories = project.getBaseDirectories()
val changeListManager = ChangeListManager.getInstance(project)

projectBaseDirectories.forEach {
VfsUtilCore.visitChildrenRecursively(
it,
object : VirtualFileVisitor<Unit>(NO_FOLLOW_SYMLINKS) {
// TODO: refactor this along with /dev & codescan file traversing logic
override fun visitFile(file: VirtualFile): Boolean {
if ((file.isDirectory && isBuildOrBin(file.name)) ||
!isWorkspaceSourceContent(file, projectBaseDirectories, changeListManager, additionalGlobalIgnoreRulesForStrictSources) ||
(file.isFile && file.length > 10 * 1024 * 1024)
) {
return false
}
if (file.isFile) {
allFiles.add(file)
return false
}
return true
}
}
)
}

for (file in allFiles) {
if (willExceedPayloadLimit(currentTotalFileSize, file.length)) {
break
}
collectedFiles.add(file.path)
currentTotalFileSize += file.length
}

return FileCollectionResult(
files = collectedFiles.toList(),
fileSize = (currentTotalFileSize / 1024 / 1024).toInt()
)
}
fun collectFiles(): FileCollectionResult = collectFiles(project.getBaseDirectories(), ChangeListManager.getInstance(project))

Check warning on line 244 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L244

Added line #L244 was not covered by tests

private fun queryResultToRelevantDocuments(queryResult: List<Chunk>): List<RelevantDocument> {
val documents: MutableList<RelevantDocument> = mutableListOf()
Expand Down Expand Up @@ -353,5 +301,57 @@

companion object {
private val logger = getLogger<ProjectContextProvider>()
private val regex = Regex("""bin|build|node_modules|venv|\.venv|env|\.idea|\.conda""", RegexOption.IGNORE_CASE)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker, but do we want to move this list to a common utils file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everyone seems to have a different concept of what to ignore at the moment

private val mega = (1024 * 1024).toULong()
private val tenMb = 10 * mega.toInt()

Check warning on line 306 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L304-L306

Added lines #L304 - L306 were not covered by tests

private fun willExceedPayloadLimit(maxSize: ULong, currentTotalFileSize: ULong, currentFileSize: Long) =
currentTotalFileSize.let { totalSize -> totalSize > (maxSize - currentFileSize.toUInt()) }

private fun isBuildOrBin(fileName: String): Boolean =
regex.find(fileName) != null

fun collectFiles(projectBaseDirectories: Set<VirtualFile>, changeListManager: ChangeListManager): FileCollectionResult {
val maxSize = CodeWhispererSettings.getInstance()
.getProjectContextIndexMaxSize().toULong() * mega
val collectedFiles = mutableListOf<String>()
var currentTotalFileSize = 0UL
val allFiles = mutableListOf<VirtualFile>()

Check warning on line 319 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L315-L319

Added lines #L315 - L319 were not covered by tests

projectBaseDirectories.forEach {
VfsUtilCore.visitChildrenRecursively(
it,
object : VirtualFileVisitor<Unit>(NO_FOLLOW_SYMLINKS) {

Check warning on line 324 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L321-L324

Added lines #L321 - L324 were not covered by tests
// TODO: refactor this along with /dev & codescan file traversing logic
override fun visitFile(file: VirtualFile): Boolean {
if ((file.isDirectory && isBuildOrBin(file.name)) ||
!isWorkspaceSourceContent(file, projectBaseDirectories, changeListManager, additionalGlobalIgnoreRulesForStrictSources) ||
(file.isFile && file.length > tenMb)
) {
return false

Check warning on line 331 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L331

Added line #L331 was not covered by tests
}
if (file.isFile) {
allFiles.add(file)
return false

Check warning on line 335 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L334-L335

Added lines #L334 - L335 were not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be true?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

files have no children to visit so it could go either way

}
return true

Check warning on line 337 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L337

Added line #L337 was not covered by tests
}
}
)
}

Check warning on line 341 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L341

Added line #L341 was not covered by tests

for (file in allFiles) {
if (willExceedPayloadLimit(maxSize, currentTotalFileSize, file.length)) {
break

Check warning on line 345 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L345

Added line #L345 was not covered by tests
}
collectedFiles.add(file.path)
currentTotalFileSize += file.length.toUInt()

Check warning on line 348 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L347-L348

Added lines #L347 - L348 were not covered by tests
}

return FileCollectionResult(
files = collectedFiles.toList(),
fileSize = (currentTotalFileSize / 1024u / 1024u).toInt()

Check warning on line 353 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/project/ProjectContextProvider.kt#L351-L353

Added lines #L351 - L353 were not covered by tests
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
fun getProjectContextIndexThreadCount(): Int = state.intValue.getOrDefault(
CodeWhispererIntConfigurationType.ProjectContextIndexThreadCount,
0
)
).coerceIn(CONTEXT_INDEX_THREADS)

Check warning on line 95 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt#L95

Added line #L95 was not covered by tests

fun setProjectContextIndexThreadCount(value: Int) {
state.intValue[CodeWhispererIntConfigurationType.ProjectContextIndexThreadCount] = value
Expand All @@ -101,7 +101,7 @@
fun getProjectContextIndexMaxSize(): Int = state.intValue.getOrDefault(
CodeWhispererIntConfigurationType.ProjectContextIndexMaxSize,
250
)
).coerceIn(CONTEXT_INDEX_SIZE)

Check warning on line 104 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt#L104

Added line #L104 was not covered by tests

fun setProjectContextIndexMaxSize(value: Int) {
state.intValue[CodeWhispererIntConfigurationType.ProjectContextIndexMaxSize] = value
Expand Down Expand Up @@ -134,10 +134,6 @@
state.value[CodeWhispererConfigurationType.IsTabAcceptPriorityNotificationShownOnce] = value
}

companion object {
fun getInstance(): CodeWhispererSettings = service()
}

override fun getState(): CodeWhispererConfiguration = CodeWhispererConfiguration().apply {
value.putAll(state.value)
intValue.putAll(state.intValue)
Expand All @@ -155,6 +151,13 @@
this.state.stringValue.putAll(state.stringValue)
this.state.autoBuildSetting.putAll(state.autoBuildSetting)
}

companion object {
fun getInstance(): CodeWhispererSettings = service()

Check warning on line 156 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt#L156

Added line #L156 was not covered by tests

val CONTEXT_INDEX_SIZE = IntRange(1, 4096)
val CONTEXT_INDEX_THREADS = IntRange(0, 50)

Check warning on line 159 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt

View check run for this annotation

Codecov / codecov/patch

plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/settings/CodeWhispererSettings.kt#L158-L159

Added lines #L158 - L159 were not covered by tests
}
}

class CodeWhispererConfiguration : BaseState() {
Expand Down
Loading