Closed JBYoshi closed 9 years ago
No, 'Object.registerNatives()' is implemented in a weird spot because it's the only native that needs to be implemented manually by the VM.
Can you show me the code that you're trying to run?
import cc.turtle.Turtle;
public final class Robot {
private final Turtle t = Turtle.getInstance();
public boolean goForward() {
return t.forward();
}
public boolean goBack() {
return t.back();
}
public void turnLeft() {
t.turnLeft();
}
public void turnRight() {
t.turnRight();
}
public boolean isFinished() {
return !t.detectDown();
}
public static void main(String[] args) {
Robot robot = new Robot();
while (!robot.isFinished()) {
robot.turnLeft();
if (robot.goForward()) continue;
robot.turnRight();
if (robot.goForward()) continue;
robot.turnRight();
if (robot.goForward()) continue;
robot.turnRight();
if (robot.goForward()) continue;
robot.turnRight();
robot.turnRight();
}
}
}
Hm it seems to be an issue with the 1.74 update. This could take some digging...
@Yevano I have no idea why it worked before, but it seems we have an issue with invokestatic
on empty stacks.
java.lang.Object.<clinit>()V
Length: 24
Locals: 0
[1] GETTABLE 1 0 256 ;Pushing stacktrace
;Loading and calling function
[2] GETTABLE 2 0 257
[3] GETTABLE 3 0 258
[4] GETTABLE 4 0 259
[5] GETTABLE 5 0 260
[6] CALL 1 5 1
[0] B8:
[7] GETTABLE 4 0 261 ;Setting line number
;Loading and calling function
[8] GETTABLE 5 0 262
[9] CALL 4 2 1
[10] MOVE 3 0
[11] GETTABLE 0 0 263
[12] CALL 0 1 3 ;java.lang.Object.registerNatives()V
[13] TEST 1 0 ;Check throw
[14] JMP 5
[15] GETTABLE 4 0 264 ;Throw
;Popping stacktrace
;Loading and calling function
[16] CALL 4 1 1
[17] LOADNIL 4 4
[18] MOVE 5 1
[19] RETURN 4 3
[20] MOVE 2 1
[21] MOVE 1 0
[22] MOVE 0 3
This is the assembly being generated. You can see on line 15 which GETTABLE
instruction is erroring. It's because we're doing that trick, where we move the object beneath all the method arguments to a register above those arguments, so that we don't have to move each argument up a register to inject the function beneath them. With an empty stack, this end up trying to move the RTI table above the stack, which leads to major problems when we try to access the RTI table before moving back to place, like on line 15.
Actually, does this problem exist in all the invokes? If the invocation target is included as part of the arguments list (which it is), and there's nothing beneath that target besides RTI, shouldn't we get the same problem?
I should also mention, I think the reason we never saw the issue before now is the fact that it will only appear when an exception is thrown. You'll notice that the invoke instructions don't use RTI after stowing it above the stack until it moves it back. Except when an exception is thrown, and it needs to use RTI to pop the stack trace. I imagine we just haven't had an instance where an exception was thrown in an invocation on an empty stack.
I suppose one solution could be to move the rsave
object (in this case, RTI) back in place before the throw code, but that seems hacky to me compared to just not stowing a value that's technically beneath the stack.
One final detail I just realized: The issue should also only occur when a static method invokes a method. Being within a static method is the only way to not have a local variable to stow sitting above the RTI.
@ElvishJerricco In that case, the simplest solution would be to simply skip a cell in the stack when we enter a static method, right? If possible, I don't want to go back to the original way of doing it where we move all the registers up, because that's slower.
What do you mean by that?
Also, I think we should generally avoid moving items that aren't on the stack, such as local variables and RTI. Because if we do anything unexpected, like throwing an exception and moving the frame to a catch block, we want those areas to be untouched.
The other alternative is we can move rsave
back to rx
before the TEST
instruction in asmCheckThrow
. But since that solution is specific to certain scenarios, we can't just implement it in asmCheckThrow
, which would end up making this solution pretty ugly.
Working on fixing this. I'm generalizing the invoke instructions. And I'm making it so the stack is moved up in the case that there is nothing beneath the arguments on the stack. This means no moving local variables, because of the danger of misplaced locals in finally blocks.
I'm encountering a very strange problem. It seems arguments aren't being passed into JIT'd functions at all. Considering Dan didn't change the LuaJ version, there is no apparent reason for this problem.
Testing under 1.7, everything works. But under 1.74, arguments aren't passed to JIT'd functions. Wut.
Hm upon further inspection, it seems the values are properly passed in, but for some reason they're failing null pointer checks anyway.
Ok I finally found the issue. The bit API is misbehaving. I tried switching to the new bit32, but it exhibits the same behavior.
Huh? Looks like development is no longer dormant.
I always watch the repo for breaking bugs like this, and for pull requests. So it's always open. We'll see if I start working on the runtime some more though.
It works now. Thanks!
java.lang.Object.<clinit>()V/bytecode:15: attempt to index ? (a nil value)
classloader.lua:745: Error in java.lang.Object <clinit>()V
After some testing, it seems that
Object.registerNatives()
needs to be implemented.