mirror of
https://github.com/irmen/prog8.git
synced 2025-02-22 16:29:05 +00:00
vm: fix crashes when array contains pointers/strings
This commit is contained in:
parent
5c9c7f2c5e
commit
c26e116f0e
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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" }
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
8.5
|
||||
8.6-dev
|
||||
|
@ -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.")
|
||||
|
@ -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 }
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
104
compiler/test/vm/TestCompilerVirtual.kt
Normal file
104
compiler/test/vm/TestCompilerVirtual.kt
Normal 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)
|
||||
}
|
||||
|
||||
})
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
...
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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" }
|
||||
}
|
||||
|
@ -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 }
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user