Skip to content

Commit 833e463

Browse files
committed
String indexing bound check now includes the terminating 0 character. Also fix negative indexes on strings.
fixes #190
1 parent 6611e4e commit 833e463

File tree

6 files changed

+98
-28
lines changed

6 files changed

+98
-28
lines changed

compiler/src/prog8/compiler/astprocessing/AstChecker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1962,7 +1962,7 @@ internal class AstChecker(private val program: Program,
19621962
} else if (target.datatype.isString) {
19631963
if (target.value is StringLiteral) {
19641964
// check string lengths for non-memory mapped strings
1965-
val stringLen = (target.value as StringLiteral).value.length
1965+
val stringLen = (target.value as StringLiteral).value.length + 1 // include the 0-byte terminator
19661966
if (index != null && index !in 0..<stringLen)
19671967
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
19681968
}

compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,16 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
432432
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
433433
arrayIndexedExpression.indexer.indexExpr = newIndex
434434
newIndex.linkParents(arrayIndexedExpression.indexer)
435+
} else if(target?.datatype?.isString==true) {
436+
val stringsize = (target.value as StringLiteral).value.length
437+
if(stringsize+index < 0) {
438+
errors.err("index out of bounds", arrayIndexedExpression.position)
439+
return noModifications
440+
}
441+
// replace the negative index by the normal index
442+
val newIndex = NumericLiteral.optimalNumeric(stringsize+index, arrayIndexedExpression.indexer.position)
443+
arrayIndexedExpression.indexer.indexExpr = newIndex
444+
newIndex.linkParents(arrayIndexedExpression.indexer)
435445
}
436446
} else if(arrayIndexedExpression.pointerderef!=null) {
437447
TODO("cleanup pointer indexing ${arrayIndexedExpression.position}")

compiler/test/ast/TestVariousCompilerAst.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,48 @@ main {
260260
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
261261
errors.errors.single() shouldContain "cannot use byte value"
262262
}
263+
264+
test("string indexing bounds checks") {
265+
val src="""
266+
main {
267+
sub start() {
268+
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
269+
str name = "hello"
270+
271+
name[5] = '!' ; don't do this in real code...
272+
name[5] = 0
273+
name[6] = 99 ; out of bounds
274+
name[-1] = 99 ; ok
275+
name[-5] = 99 ; ok
276+
277+
cx16.r0L = name[5]
278+
cx16.r1L = name[6] ; out of bounds
279+
cx16.r1L = name[-1] ; ok
280+
cx16.r1L = name[-5] ; ok
281+
282+
array[5] = '!'
283+
array[5] = 0
284+
array[6] = 99 ; out of bounds
285+
array[-1] = 99 ; ok
286+
array[-5] = 99 ; ok
287+
array[-6] = 99 ; ok
288+
289+
cx16.r0L = array[5]
290+
cx16.r1L = array[6] ; out of bounds
291+
cx16.r1L = array[-1] ; ok
292+
cx16.r1L = array[-5] ; ok
293+
cx16.r1L = array[-6] ; ok
294+
}
295+
}"""
296+
297+
val errors = ErrorReporterForTests()
298+
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
299+
errors.errors.size shouldBe 4
300+
errors.errors[0] shouldContain ":9:13: index out of bounds"
301+
errors.errors[1] shouldContain ":14:24: index out of bounds"
302+
errors.errors[2] shouldContain ":20:14: index out of bounds"
303+
errors.errors[3] shouldContain ":26:25: index out of bounds"
304+
}
263305
}
264306

265307
context("return value") {

compiler/test/codegeneration/TestArrayThings.kt

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -251,32 +251,26 @@ main {
251251
val src="""
252252
main {
253253
sub start() {
254-
ubyte[10] array
255-
array[-11] = 0
256-
}
257-
}"""
258-
val errors = ErrorReporterForTests()
259-
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
260-
errors.errors.size shouldBe 1
261-
errors.errors[0] shouldContain "out of bounds"
262-
}
254+
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
255+
str name = "hello"
263256
264-
test("bounds checking on strings invalid cases") {
265-
val src="""
266-
main {
267-
sub start() {
268-
str name = "1234567890"
269-
name[10] = 0
270-
name[-1] = 0
271-
name[-11] = 0
257+
name[-5] = 99 ; ok
258+
name[-6] = 99 ; out of bounds
259+
cx16.r1L = name[-5] ; ok
260+
cx16.r1L = name[-6] ; out of bounds
261+
array[-6] = 99 ; ok
262+
array[-7] = 99 ; out of bounds
263+
cx16.r1L = array[-6] ; ok
264+
cx16.r1L = array[-7] ; out of bounds
272265
}
273266
}"""
274267
val errors = ErrorReporterForTests()
275268
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
276-
errors.errors.size shouldBe 3
277-
errors.errors[0] shouldContain "out of bounds"
278-
errors.errors[1] shouldContain "out of bounds"
279-
errors.errors[2] shouldContain "out of bounds"
269+
errors.errors.size shouldBe 4
270+
errors.errors[0] shouldContain ":8:9: index out of bounds"
271+
errors.errors[1] shouldContain ":10:20: index out of bounds"
272+
errors.errors[2] shouldContain ":12:9: index out of bounds"
273+
errors.errors[3] shouldContain ":14:20: index out of bounds"
280274
}
281275

282276
test("array and string initializer with multiplication") {

docs/source/todo.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
TODO
22
====
33

4-
- fix/check github issues.
5-
64

75
Future Things and Ideas
86
^^^^^^^^^^^^^^^^^^^^^^^

examples/test.p8

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,33 @@
1+
%import textio
2+
13
main {
2-
sub start() {
3-
cx16.r0L = cx16.r1 as ubyte
4-
cx16.r1sL = cx16.r2s as byte
5-
}
64

5+
sub start() {
6+
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
7+
str name = "hello"
8+
9+
name[5] = '!' ; don't do this in real code...
10+
name[5] = 0
11+
name[6] = 99 ; out of bounds
12+
name[-1] = 99 ; ok
13+
name[-5] = 99 ; ok
14+
15+
cx16.r0L = name[5]
16+
cx16.r1L = name[6] ; out of bounds
17+
cx16.r1L = name[-1] ; ok
18+
cx16.r1L = name[-5] ; ok
19+
20+
array[5] = '!'
21+
array[5] = 0
22+
array[6] = 99 ; out of bounds
23+
array[-1] = 99 ; ok
24+
array[-5] = 99 ; ok
25+
array[-6] = 99 ; ok
26+
27+
cx16.r0L = array[5]
28+
cx16.r1L = array[6] ; out of bounds
29+
cx16.r1L = array[-1] ; ok
30+
cx16.r1L = array[-5] ; ok
31+
cx16.r1L = array[-6] ; ok
32+
}
733
}

0 commit comments

Comments
 (0)