Exception in thread "Thread-1" java.lang.StringIndexOutOfBoun
|
|||||||
Exception in thread "Thread-1" java.lang.StringIndexOutOfBoun
|
This post was updated on .
The ProblemWhen you try to open one of your compiled.vm files in the VMEmulator, either it sits there and says "Loading...", or (if you still have its terminal open), you get an error likeException in thread "Thread-1" java.lang.StringIndexOutOfBoundsException: String index out of range: -1This is followed by a stack trace. tl;dr:This probably means that you're calling or defining a function without a period in it.How to FixBoth your "function " lines and your "call " lines need to be prefaced by their class.So if you have defined a function "function Bat.hide {num_local}", then when you call it, you need to "call Bat.hide {num_args}". Just having "call hide {num_args}" or "function hide {num_local}" will cause the VMProgram to throw java.lang.StringIndexOutOfBoundsException: String index out of range: -1 How I Troubleshot thisI will say, I was very pleased with how I figured this out.The ErrorException in thread "Thread-1" java.lang.StringIndexOutOfBoundsException: String index out of range: -1 at java.lang.String.substring(String.java:1967) at Hack.VMEmulator.VMProgram.getAddress(Unknown Source) at Hack.VMEmulator.VMProgram.buildProgram(Unknown Source) at Hack.VMEmulator.VMProgram.loadProgram(Unknown Source) at Hack.VMEmulator.VMProgram$LoadProgramTask.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) From the above, I could tell that the error I was seeing was a failure to substring (on index -1) in the getAddress method of the VMProgram class in the Hack.VMEmulator package. The HuntI opened NetBeans, and used it to open JAR files innand2tetris/tools/bin/libuntil I found the Hack.VMEmulator package in Simulator.jar, and sure enough it had a VMProgram class. Because this is a compiled class file, unfortunately most of the source is not present, only compiled Java ByteCode (we're getting quite meta here) Piecing it Back TogetherThankfully, Wikipedia hosts a table of the Java ByteCode operations listingsI found-and-replaced the Mnemonics in the VMProgram.class file until I had a pretty good understanding of what was occurring (hint, Java's pushing variables to, and popping them from, its stack). public short getAddress(String string) throws Hack.Controller.ProgramException { // <editor-fold defaultstate="collapsed" desc="Compiled Code"> /* 0: aload_0 load a reference onto the stack from local variable 0 * 1: getfield Hack/VMEmulator/VMProgram.functions:Ljava/util/Hashtable; get a field value of an object objectref, where the field is identified by field reference in the constant pool index (indexbyte1 << 8 + indexbyte2) * 4: aload_1 load a reference onto the stack from local variable 1 * 5: invokevirtual java/util/Hashtable.get:(Ljava/lang/Object;)Ljava/lang/Object; invoke virtual method on object objectref and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 + indexbyte2) * 8: checkcast java/lang/Short checks whether an objectref is of a certain type, the class reference of which is in the constant pool at index (indexbyte1 << 8 + indexbyte2) * 11: astore_2 store a reference into local variable 2 * 12: aload_2 load a reference onto the stack from local variable 2 * 13: ifnull 21 if value is null, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 + branchbyte2) * 16: aload_2 load a reference onto the stack from local variable 2 * 17: invokevirtual java/lang/Short.shortValue:()S invoke virtual method on object objectref and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 + indexbyte2) * 20: ireturn * 21: aload_1 load a reference onto the stack from local variable 1 * 22: iconst_0 load the int value 0 onto the stack * 23: aload_1 load a reference onto the stack from local variable 1 * 24: ldc . push a constant #index from a constant pool (String, int, float, Class, java.lang.invoke.MethodType, or java.lang.invoke.MethodHandle) onto the stack . * 26: invokevirtual java/lang/String.indexOf:(Ljava/lang/String;)I invoke virtual method on object objectref and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 + indexbyte2) * 29: invokevirtual java/lang/String.substring:(II)Ljava/lang/String; invoke virtual method on object objectref and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 + indexbyte2)Ops 0 and 1 seem to indicate that we're pushing some Hashtable "functions" onto our stack. Ops 4 and 5 suggest that we're pushing a (String?) key onto the stack, and calling functions.get(key). Ops 8, 11, and 12 are something about checking whether the returned value is a Short Op 13 jumps to Op 21 "ifnull," and the ops in between appear to return (thus not causing our StringIndexOutOfBoundsException), so let's go to Op 21. Op 21 pushes the same key onto the stack (from local 1). Op 22 pushes int 0 onto the stack Op 23 pushes the same key from local 1 Op 24 pushes a literal "." onto the stack Op 26 calls String.indexOf; it's looking for a period in your function name and when it doesn't find it, returns -1 Op 29 calls String.substring, but since our two topmost stack values are (-1, 0), we get an StringIndexOutOfBoundsException. Boom. Nand2Tetris made me understand Java. |
Great job!
I don't know if you are aware, but the nand2tetris software suite is open source and the code is available from here: https://www.nand2tetris.org/software (scroll to the bottom of the page). Shameless self plug: I also created a project in github. You can read about the changes here. |
Haha that source would have made this process less tedious
For closure, the source corresponding to the above compiled Java ByteCode is public short getAddress(String functionName) throws ProgramException { Short address = (Short)functions.get(functionName); if (address != null) { return address.shortValue(); } else { String className = functionName.substring(0, functionName.indexOf("."));from {nand2tetris_source_zipfile}/SimulatorsPackageSource/Hack/VMEmulator/VMProgram.javaThis confirms that, indeed, if your function name does not have a "." in it, the VMProgram will raise an unhandled java.lang.StringIndexOutOfBoundsException |
Free forum by Nabble | Edit this page |