haxe icon indicating copy to clipboard operation
haxe copied to clipboard

[JVM] Operand stack underflow

Open Ilir-Liburn opened this issue 4 years ago • 9 comments

I have latest Haxe build on Windows and Linux, plus Java (openjdk 11 on Linux) and hxJava as well. Following code

@:multiType
abstract Zero<T>(Null<T>) {
    public function new();

    @:to public static inline function toFloat(v:Float):Float return 0;
}

class Main {
    public static function main()  {
        var z:Zero<Float> = null;
        trace(z);
    }
}

produces

Error: Unable to initialize main class haxe.root.Main
Caused by: java.lang.VerifyError: Operand stack underflow
Exception Details:
  Location:
    haxe/root/Main.main()V @1: dstore_0
  Reason:
    Attempt to pop empty stack.
  Current Frame:
    bci: @1
    flags: { }
    locals: { }
    stack: { null }
  Bytecode:
    0000000: 0147 b200 1326 b800 19bb 001b 5912 1d12
    0000010: 1f10 1312 20b7 0024 b600 2ab1

Ilir-Liburn avatar Mar 23 '20 09:03 Ilir-Liburn

What's the -D dump of Main here?

Simn avatar Mar 23 '20 09:03 Simn

		[Block:Void]
			[Var z(3286):Zero<Float>] [Const:Zero<Float>] null
			[Call:Void]
				[Field:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void]
					[TypeExpr haxe.Log:Class<haxe.Log>]
					[FStatic:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void]
						haxe.Log
						trace:(v : Dynamic, ?infos : Null<haxe.PosInfos>) -> Void
				[Local z(3286):Zero<Float>:Zero<Float>]
				[ObjectDecl:{ methodName : java.lang.String, lineNumber : Int, fileName : java.lang.String, className : java.lang.String }]
					fileName: [Const:java.lang.String] "source/Main.hx"
					lineNumber: [Const:Int] 19
					className: [Const:java.lang.String] "Main"
					methodName: [Const:java.lang.String] "main"

Ilir-Liburn avatar Mar 23 '20 09:03 Ilir-Liburn

I guess the compiler follows the underlying type of the abstract and concludes that it's Float via that @:to function. And then we end up assigning a null value to a basic type and everything explodes. This code probably isn't quite sound.

On the other hand, I'm not sure why this manifests as a stack underflow. At the very least this should fail differently.

Simn avatar Mar 23 '20 09:03 Simn

As I remember, @:multiType abstract resolves Null<T> as T assigning 0.0 value in normal case. It seems null is used here causing this to "explode". Here is normal Java output

double z = 0.0;

Ilir-Liburn avatar Mar 23 '20 09:03 Ilir-Liburn

I think that only happens to work because genjava uses null as "default value" in cases like this.

By the way, does it work if you change the return type of that @:to function to Null<Float>?

Simn avatar Mar 23 '20 09:03 Simn

No, same happens

Ilir-Liburn avatar Mar 23 '20 09:03 Ilir-Liburn

Ah, the reason this comes out as a stack underflow is because double values on the JVM take up two stack slots instead of one. With only aconst_null on the stack it can't pop two stack elements and thus underflows. So there's no mystery in that part.

Anyway, this code should not make it out of the typer like that. It either has to error or inject the @:to call.

Simn avatar Mar 23 '20 10:03 Simn

I think using Float there should be a compiler error. The underlying type promises that it's Null<T> and thus nullable, but the concrete type is Float and not nullable.

However, I think it should work with the return type being Null<Float>. There's probably a different problem here with the Null being followed away by someone.

Simn avatar Mar 23 '20 10:03 Simn

BTW, is it possible for @:to and @:from on @:multiType abstract to return specific value, instead of automatically assign 0 for @:to and return argument value at @:from? I checked how Map is coded, there shouldn't be problem because each return value is as it should be.

I guess not, because if @:multiType abstract is used as a member variable inside the class for example, default value (0 or null) will be used depending on platform, unless return value of @:to is used inside init().

What I'm trying to do is to resolve null value as Math.NaN for @:multiType abstract where Null<Float> resolves as Float.

Ilir-Liburn avatar Mar 23 '20 10:03 Ilir-Liburn