vm: fix crashes when array contains pointers/strings

This commit is contained in:
Irmen de Jong 2022-09-24 13:54:00 +02:00
parent 5c9c7f2c5e
commit c26e116f0e
18 changed files with 175 additions and 30 deletions

View File

@ -29,6 +29,14 @@ class PtSub(
override fun printProperties() {
print(name)
}
init {
// params and return value should not be str
if(parameters.any{ it.type !in NumericDatatypes })
throw AssemblyError("non-numeric parameter")
if(returntype!=null && returntype !in NumericDatatypes)
throw AssemblyError("non-numeric returntype $returntype")
}
}

View File

@ -106,7 +106,7 @@ sealed class SourceCode {
* [origin]: `library:/x/y/z.p8` for a given `pathString` of "x/y/z.p8"
*/
class Resource(pathString: String): SourceCode() {
private val normalized = "/" + Path.of(pathString).normalize().toMutableList().joinToString("/")
private val normalized = "/" + Path(pathString).normalize().toMutableList().joinToString("/")
override val isFromResources = true
override val isFromFilesystem = false
@ -125,7 +125,7 @@ sealed class SourceCode {
}
val stream = object {}.javaClass.getResourceAsStream(normalized)
text = stream!!.reader().use { it.readText() }
name = Path.of(pathString).toFile().nameWithoutExtension
name = Path(pathString).toFile().nameWithoutExtension
}
}

View File

@ -9,7 +9,7 @@ import prog8.code.core.IErrorReporter
import prog8.codegen.intermediate.IRCodeGen
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import java.nio.file.Path
import kotlin.io.path.Path
class VmCodeGen(private val program: PtProgram,
private val symbolTable: SymbolTable,
@ -33,7 +33,7 @@ class VmCodeGen(private val program: PtProgram,
companion object {
fun compileIR(listingFilename: String): IAssemblyProgram {
val irProgram = IRFileReader(Path.of(""), listingFilename).readFile()
val irProgram = IRFileReader(Path(""), listingFilename).readFile()
return VmAssemblyProgram(irProgram.name, irProgram)
}
}

View File

@ -58,7 +58,12 @@ internal class VmVariableAllocator(val st: SymbolTable, val encoding: IStringEnc
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toHex() }
variable.onetimeInitializationArrayValue!!.joinToString(",") {
if(it.number!=null)
it.number!!.toHex()
else
"&${it.addressOf!!.joinToString(".")}"
}
} else {
(1..variable.length!!).joinToString(",") { "0" }
}

View File

@ -1 +1 @@
8.5
8.6-dev

View File

@ -13,6 +13,7 @@ import java.io.File
import java.nio.file.*
import java.time.LocalDateTime
import kotlin.system.exitProcess
import kotlin.io.path.Path
fun main(args: Array<String>) {
@ -142,7 +143,7 @@ private fun compileMain(args: Array<String>): Boolean {
for (importedFile in allImportedFiles) {
print(" ")
println(importedFile)
val watchDir = importedFile.parent ?: Path.of("")
val watchDir = importedFile.parent ?: Path("")
watchDir.register(watchservice, StandardWatchEventKinds.ENTRY_MODIFY)
}
println("[${LocalDateTime.now().withNano(0)}] Waiting for file changes.")

View File

@ -320,9 +320,12 @@ class IntermediateAstMaker(val program: Program) {
private fun transformSub(srcSub: Subroutine): PtSub {
val (vardecls, statements) = srcSub.statements.partition { it is VarDecl }
var returntype = srcSub.returntypes.singleOrNull()
if(returntype==DataType.STR)
returntype=DataType.UWORD // if a sub returns 'str', replace with uword. Intermediate AST and I.R. don't contain 'str' datatype anymore.
val sub = PtSub(srcSub.name,
srcSub.parameters.map { PtSubroutineParameter(it.name, it.type, it.position) },
srcSub.returntypes.singleOrNull(),
returntype,
srcSub.inline,
srcSub.position)
sub.parameters.forEach { it.parent=sub }

View File

@ -22,6 +22,7 @@ internal fun compileFile(
outputDir: Path = prog8tests.helpers.outputDir,
errors: IErrorReporter? = null,
writeAssembly: Boolean = true,
keepIR: Boolean = true,
optFloatExpr: Boolean = true
) : CompilationResult? {
val filepath = fileDir.resolve(fileName)
@ -36,7 +37,7 @@ internal fun compileFile(
quietAssembler = true,
asmListfile = false,
experimentalCodegen = false,
keepIR = false,
keepIR = keepIR,
platform.name,
evalStackBaseAddress = null,
symbolDefs = emptyMap(),
@ -57,12 +58,14 @@ internal fun compileText(
sourceText: String,
errors: IErrorReporter? = null,
writeAssembly: Boolean = true,
keepIR: Boolean = true,
optFloatExpr: Boolean = true
) : CompilationResult? {
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
filePath.toFile().writeText(sourceText)
return compileFile(platform, optimize, filePath.parent, filePath.name, errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr)
return compileFile(platform, optimize, filePath.parent, filePath.name,
errors=errors, writeAssembly=writeAssembly, optFloatExpr = optFloatExpr, keepIR=keepIR)
}

View File

@ -0,0 +1,104 @@
package prog8tests.vm
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldNotBe
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8.vm.VmRunner
import prog8tests.helpers.compileText
import kotlin.io.path.readText
class TestCompilerVirtual: FunSpec({
test("compile virtual: any all sort reverse builtin funcs") {
val src = """
main {
sub start() {
uword[] words = [1111,2222,0,4444,3333]
ubyte result = all(words)
result++
result = any(words)
result++
sort(words)
reverse(words)
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText(), false)
}
test("compile virtual: array with pointers") {
val src = """
main {
sub start() {
ubyte variable
uword[] words = [1111,2222,"three",&variable]
variable = 2222 in words
}
}"""
val othertarget = Cx16Target()
compileText(othertarget, true, src, writeAssembly = true, keepIR=true) shouldNotBe null
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText(), false)
}
test("compile virtual: str args and return type") {
val src = """
main {
sub start() {
sub testsub(str s1) -> str {
return "result"
}
uword result = testsub("arg")
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText(), false)
}
test("compile virtual: nested labels") {
val src = """
main {
sub start() {
uword i
uword k
mylabel_outside:
for i in 0 to 10 {
mylabel_inside:
if i==100 {
goto mylabel_outside
goto mylabel_inside
}
while k <= 10 {
k++
}
do {
k--
} until k==0
for k in 0 to 5 {
i++
}
repeat 10 {
k++
}
}
}
}"""
val target = VMTarget()
val result = compileText(target, true, src, writeAssembly = true, keepIR=true)!!
val virtfile = result.compilationOptions.outputDir.resolve(result.program.name + ".p8virt")
VmRunner().runProgram(virtfile.readText(), false)
}
})

View File

@ -8,7 +8,7 @@ import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.code.core.*
import prog8.parser.Prog8ANTLRParser
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.isRegularFile
@ -20,7 +20,7 @@ private data class NumericLiteralNode(val number: Double, val datatype: DataType
private fun ParserRuleContext.toPosition() : Position {
val pathString = start.inputStream.sourceName
val filename = if(SourceCode.isRegularFilesystemPath(pathString)) {
val path = Path.of(pathString)
val path = Path(pathString)
if(path.isRegularFile()) {
SourceCode.relative(path).toString()
} else {

View File

@ -3,8 +3,10 @@ TODO
For next release
^^^^^^^^^^^^^^^^
- fix compiler crash in examples/vm/bsieve.p8
- fix nullpointer in allocator in examples/vm/textelite.p8
- fix vm crash in TestCompilerVirtual: while loop
- fix vm crash in TestCompilerVirtual: array with pointers
- fix vm crash (while loop) in examples/vm/bsieve.p8 (Assembler)
- fix vm crash (parseValue error) in examples/vm/textelite.p8 (Assembler)
...

View File

@ -26,13 +26,14 @@ main {
@(flags_ptr + k) = false
k += prime
}
; txt.print_uw(prime)
; txt.spc()
txt.print_uw(prime)
txt.spc()
count++
}
}
}
txt.nl()
txt.print_uw(count)
txt.print(" primes\n")
}

View File

@ -13,8 +13,8 @@ import org.takes.rs.RsJson
import org.takes.tk.TkSlf4j
import prog8.compiler.CompilerArguments
import prog8.compiler.compileProgram
import java.nio.file.Path
import javax.json.Json
import kotlin.io.path.Path
class Jsonding: RsJson.Source {
@ -31,7 +31,7 @@ class RequestParser : Take {
val names = form.names()
val a = form.param("a").single()
val args = CompilerArguments(
Path.of(a),
Path(a),
optimize = true,
optimizeFloatExpressions = false,
dontReinitGlobals = false,

View File

@ -4,6 +4,7 @@ import prog8.code.*
import prog8.code.core.*
import prog8.code.target.*
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.bufferedReader
import kotlin.io.path.div
@ -54,6 +55,7 @@ class IRFileReader(outputDir: Path, programName: String) {
var loadAddress = target.machine.PROGRAM_LOAD_ADDRESS
var dontReinitGlobals = false
var evalStackBaseAddress: UInt? = null
var outputDir = Path("")
if(line!="<OPTIONS>")
throw IRParseException("invalid OPTIONS")
while(true) {
@ -82,6 +84,7 @@ class IRFileReader(outputDir: Path, programName: String) {
val (start, end) = value.split(',')
zpReserved.add(UIntRange(start.toUInt(), end.toUInt()))
}
"outputDir" -> outputDir = Path(value)
else -> throw IRParseException("illegal OPTION $name")
}
}
@ -96,7 +99,8 @@ class IRFileReader(outputDir: Path, programName: String) {
target,
loadAddress,
dontReinitGlobals = dontReinitGlobals,
evalStackBaseAddress = evalStackBaseAddress
evalStackBaseAddress = evalStackBaseAddress,
outputDir = outputDir
)
}
@ -130,7 +134,12 @@ class IRFileReader(outputDir: Path, programName: String) {
}
}
in ArrayDatatypes -> {
initArray = value.split(',').map { StArrayElement(it.toDouble(), null) }
initArray = value.split(',').map {
if(it.startsWith('&'))
StArrayElement(null, it.drop(1).split('.'))
else
StArrayElement(it.toDouble(), null)
}
}
DataType.STR -> throw IRParseException("STR should have been converted to byte array")
else -> throw IRParseException("weird dt")
@ -173,7 +182,7 @@ class IRFileReader(outputDir: Path, programName: String) {
"uword" -> DataType.ARRAY_UW
"float" -> DataType.ARRAY_F
"bool" -> DataType.ARRAY_B
else -> throw IRParseException("invalid dt")
else -> throw IRParseException("invalid dt $type")
}
} else {
return when(type) {
@ -183,7 +192,8 @@ class IRFileReader(outputDir: Path, programName: String) {
"uword" -> DataType.UWORD
"float" -> DataType.FLOAT
"bool" -> DataType.BOOL
else -> throw IRParseException("invalid dt")
// note: 'str' should not occur anymore in IR. Should be 'uword'
else -> throw IRParseException("invalid dt $type")
}
}
}

View File

@ -93,6 +93,7 @@ class IRFileWriter(private val irProgram: IRProgram) {
out.write("loadAddress=${irProgram.options.loadAddress}\n")
out.write("dontReinitGlobals=${irProgram.options.dontReinitGlobals}\n")
out.write("evalStackBaseAddress=${irProgram.options.evalStackBaseAddress}\n")
out.write("outputDir=${irProgram.options.outputDir.toAbsolutePath()}\n")
// other options not yet useful here?
out.write("</OPTIONS>\n")
}
@ -117,7 +118,12 @@ class IRFileWriter(private val irProgram: IRProgram) {
}
in ArrayDatatypes -> {
if(variable.onetimeInitializationArrayValue!==null) {
variable.onetimeInitializationArrayValue!!.joinToString(",") { it.number!!.toInt().toString() }
variable.onetimeInitializationArrayValue!!.joinToString(",") {
if(it.number!=null)
it.number!!.toInt().toString()
else
"&${it.addressOf!!.joinToString(".")}"
}
} else {
(1..variable.length!!).joinToString(",") { "0" }
}

View File

@ -97,6 +97,12 @@ class IRSubroutine(val name: String,
throw IllegalArgumentException("subroutine name is not scoped: $name")
if(name.startsWith("main.main."))
throw IllegalArgumentException("subroutine name invalid main prefix: $name")
// params and return value should not be str
if(parameters.any{ it.dt !in NumericDatatypes })
throw IllegalArgumentException("non-numeric parameter")
if(returnType!=null && returnType !in NumericDatatypes)
throw IllegalArgumentException("non-numeric returntype $returnType")
}
operator fun plusAssign(chunk: IRCodeChunkBase) { chunks+= chunk }

View File

@ -12,17 +12,13 @@ import prog8.code.target.Cx16Target
import prog8.intermediate.IRFileReader
import prog8.intermediate.IRFileWriter
import prog8.intermediate.IRProgram
import java.nio.file.Path
import kotlin.io.path.deleteExisting
import kotlin.io.path.name
import kotlin.io.path.readLines
import kotlin.io.path.writeText
import kotlin.io.path.*
class TestIRFileInOut: FunSpec({
test("test IR writer") {
val st = SymbolTable()
val target = Cx16Target()
val tempdir = Path.of(System.getProperty("java.io.tmpdir"))
val tempdir = Path(System.getProperty("java.io.tmpdir"))
val options = CompilationOptions(
OutputType.RAW,
CbmPrgLauncherType.NONE,

View File

@ -2112,6 +2112,6 @@ class VmRunner: IVirtualMachineRunner {
assembler.initializeMemory(memsrc, memory)
val program = assembler.assembleProgram(programsrc)
val vm = VirtualMachine(memory, program, assembler.cx16virtualregBaseAdress)
vm.run(throttle = true)
vm.run(throttle = throttle)
}
}