usethesource / flybytes

Flybytes is an intermediate language between JVM bytecode and software languages (DSLs, PLs), for compilation and decompilation.
BSD 2-Clause "Simplified" License
16 stars 6 forks source link

Issues with doubles #25

Closed bys1 closed 1 year ago

bys1 commented 1 year ago

I have a big ass class that compiled fine with integers and starts spitting tons of errors after changing all integer types to doubles. The errors are very helpful (not), so I'm currently fighting a war with Flybytes to find out what's wrong.

I just produced the following small class that gives me an error:

void compileTest() {
    loc l = |cwd:///src/main/rascal/Test.rsc|(314, 16, <18, 5>, <18, 21>);
    str  mcl = "io.usethesource.capsule.Map$Immutable";
    Class cl = class(
        object("Test"),
        modifiers = {\public(), \final()},
        methods = [
            main(
                "args",
                [
                    decl(double(), "d", init = dconst(2.0)),
                    decl(integer(), "i", init = coerce(double(), integer(), load("d"))),
                    \return()
                ]
            )
        ],
        src=l
    );
    compileClass(cl, |cwd:///Test.class|);
}

I simply want to convert a double to an integer. The coerce doesn't seem to work:

|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1637,272,<25,0>,<27,123>): Java("NegativeArraySizeException","-2")
    at org.objectweb.asm.Frame.merge(|unknown:///Frame.java|(0,0,<1222,0>,<1222,0>))
    at org.objectweb.asm.MethodWriter.computeAllFrames(|unknown:///MethodWriter.java|(0,0,<1610,0>,<1610,0>))
    at org.objectweb.asm.MethodWriter.visitMaxs(|unknown:///MethodWriter.java|(0,0,<1546,0>,<1546,0>))
    at org.objectweb.asm.tree.MethodNode.accept(|unknown:///MethodNode.java|(0,0,<769,0>,<769,0>))
    at org.objectweb.asm.tree.MethodNode.accept(|unknown:///MethodNode.java|(0,0,<649,0>,<649,0>))
    at org.objectweb.asm.tree.ClassNode.accept(|unknown:///ClassNode.java|(0,0,<452,0>,<452,0>))
    at lang.flybytes.internal.ClassCompiler$Compile.compileClass(|unknown:///ClassCompiler.java|(0,0,<360,0>,<360,0>))
    at lang.flybytes.internal.ClassCompiler.compileClass(|unknown:///ClassCompiler.java|(0,0,<104,0>,<104,0>))
    at compileClass(|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1902,5,<27,116>,<27,121>))
    at $shell$(|prompt:///|(0,14,<1,0>,<1,14>)ok
rascal>
bys1 commented 1 year ago

Integer to double also doesn't seem to work:

void compileTest() {
    loc l = |cwd:///src/main/rascal/Test.rsc|(314, 16, <18, 5>, <18, 21>);
    str  mcl = "io.usethesource.capsule.Map$Immutable";
    Class cl = class(
        object("Test"),
        modifiers = {\public(), \final()},
        methods = [
            main(
                "args",
                [
                    decl(integer(), "i", init = iconst(2)),
                    decl(double(), "d", init = coerce(integer(), double(), load("i"))),
                    \return()
                ]
            )
        ],
        src=l
    );
    compileClass(cl, |cwd:///Test.class|);
}

When I simply use load("d"), I get:

|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1637,272,<25,0>,<27,123>): Java("ArrayIndexOutOfBoundsException","")
    at compileClass(|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1902,5,<27,116>,<27,121>))
    at $shell$(|prompt:///|(0,14,<1,0>,<1,14>)ok

When adding the coerce:

rascal>compileTest();
)
|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1637,272,<25,0>,<27,123>): Java("NegativeArraySizeException","-1")
    at org.objectweb.asm.Frame.merge(|unknown:///Frame.java|(0,0,<1222,0>,<1222,0>))
    at org.objectweb.asm.MethodWriter.computeAllFrames(|unknown:///MethodWriter.java|(0,0,<1610,0>,<1610,0>))
    at org.objectweb.asm.MethodWriter.visitMaxs(|unknown:///MethodWriter.java|(0,0,<1546,0>,<1546,0>))
    at org.objectweb.asm.tree.MethodNode.accept(|unknown:///MethodNode.java|(0,0,<769,0>,<769,0>))
    at org.objectweb.asm.tree.MethodNode.accept(|unknown:///MethodNode.java|(0,0,<649,0>,<649,0>))
    at org.objectweb.asm.tree.ClassNode.accept(|unknown:///ClassNode.java|(0,0,<452,0>,<452,0>))
    at lang.flybytes.internal.ClassCompiler$Compile.compileClass(|unknown:///ClassCompiler.java|(0,0,<360,0>,<360,0>))
    at lang.flybytes.internal.ClassCompiler.compileClass(|unknown:///ClassCompiler.java|(0,0,<104,0>,<104,0>))
    at compileClass(|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1902,5,<27,116>,<27,121>))
    at $shell$(|prompt:///|(0,14,<1,0>,<1,14>)ok

When doing this coerce in a different context I got a VerifyError, but I'm not yet sure what the cause of that is or how to reproduce it. Still posting it here:

|file:///Users/bys1/Documents/RUG/FBEG/flybytes-evalgen/src/main/rascal/fbeg/EvalGen.rsc|(340,91,<20,0>,<22,57>): Java("VerifyError","Operand stack underflow\nException Details:\n  Location:\n    ProgEval.eval_Expr(Lio/usethesource/vallang/IConstructor;Lio/usethesource/capsule/Map$Immutable;)D @339: i2d\n  Reason:\n    Attempt to pop empty stack.\n  Current Frame:\n    bci: @339\n    flags: { }\n    locals: { \'ProgEval\', \'io/usethesource/vallang/IConstructor\', \'io/usethesource/capsule/Map$Immutable\' }\n    stack: { }\n  Bytecode:\n    0000000: 2bb9 004c 0100 b600 52ab 0000 0000 02ed\n    0000010: 0000 0015 ac10 80ef 0000 00b3 b74c f490\n    0000020: 0000 02c1 0000 0cac 0000 00c9 0000 0ced\n    0000030: 0000 013c 0000 0d88 0000 01f6 0001 78a1\n    0000040: 0000 01c5 0001 8491 0000 010b 0001 8f53\n    0000050: 0000 020c 0001 a218 0000 027f 0001 a21b\n    0000060: 0000 00f5 0001 a5d5 0000 0168 0001 a7c4\n    0000070: 0000 0194 0001 a921 0000 0152 0001 a99a\n    0000080: 0000 0222 0001 bc5f 0000 017e 0001 be40\n    0000090: 0000 0238 0001 c727 0000 0295 002d df26\n    00000a0: 0000 00df 002e 7a5e 0
    at java.lang.Class.getDeclaredConstructors0(|unknown:///Class.java|(0,0,<0,0>,<0,0>))
    at java.lang.Class.privateGetDeclaredConstructors(|unknown:///Class.java|(0,0,<3373,0>,<3373,0>))
    at java.lang.Class.getConstructor0(|unknown:///Class.java|(0,0,<3578,0>,<3578,0>))
    at java.lang.Class.getConstructor(|unknown:///Class.java|(0,0,<2271,0>,<2271,0>))
    at fbeg.EvalGen.genEvalFunc(|unknown:///EvalGen.java|(0,0,<48,0>,<48,0>))
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(|unknown:///NativeMethodAccessorImpl.java|(0,0,<0,0>,<0,0>))
    at genEvalFunc(|file:///Users/bys1/Documents/RUG/FBEG/flybytes-evalgen/src/main/rascal/fbeg/EvalGen.rsc|(4369,8,<70,46>,<70,54>))
    at getEval(|file:///Users/bys1/Documents/RUG/FBEG/flybytes-evalgen/src/main/rascal/fbeg/demo/func/benchmarks/Gen.rsc|(305,83,<15,11>,<15,94>))
    at $shell$(|prompt:///|(5,10,<1,5>,<1,15>)ok
bys1 commented 1 year ago

[\return(cond(coerce(double(), integer(), recEval(...)), recEval(...), recEval(...)))]; (recEval returns a double)

->

|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1637,272,<25,0>,<27,123>): Java("NegativeArraySizeException","-2")
    at org.objectweb.asm.Frame.merge(|unknown:///Frame.java|(0,0,<1222,0>,<1222,0>))
    at org.objectweb.asm.MethodWriter.computeAllFrames(|unknown:///MethodWriter.java|(0,0,<1610,0>,<1610,0>))
    at org.objectweb.asm.MethodWriter.visitMaxs(|unknown:///MethodWriter.java|(0,0,<1546,0>,<1546,0>))
    at org.objectweb.asm.tree.MethodNode.accept(|unknown:///MethodNode.java|(0,0,<769,0>,<769,0>))
    at org.objectweb.asm.tree.MethodNode.accept(|unknown:///MethodNode.java|(0,0,<649,0>,<649,0>))
    at org.objectweb.asm.tree.ClassNode.accept(|unknown:///ClassNode.java|(0,0,<452,0>,<452,0>))
    at lang.flybytes.internal.ClassCompiler$Compile.compileClass(|unknown:///ClassCompiler.java|(0,0,<360,0>,<360,0>))
    at lang.flybytes.internal.ClassCompiler.compileClass(|unknown:///ClassCompiler.java|(0,0,<104,0>,<104,0>))
    at compileClass(|jar+file:///Users/bys1/.m2/repository/org/rascalmpl/flybytes/0.1.10/flybytes-0.1.10.jar!/src/lang/flybytes/Compiler.rsc|(1880,5,<27,94>,<27,99>))
    at genEval(|file:///Users/bys1/Documents/RUG/FBEG/flybytes-evalgen/src/main/rascal/fbeg/EvalGen.rsc|(4128,194,<68,4>,<69,56>))
    at getEval(|file:///Users/bys1/Documents/RUG/FBEG/flybytes-evalgen/src/main/rascal/fbeg/demo/func/benchmarks/Gen.rsc|(305,83,<15,11>,<15,94>))
    at $shell$(|prompt:///|(5,10,<1,5>,<1,15>)ok
jurgenvinju commented 1 year ago

Thanks for reporting this @bys1 !

An "operand stack underflow" is weird, but could be related to the fact that a double takes up more space than an integer does on the stack. Let's investigate if that is the issue here. I'm guessing now that the code that follows the conversion is assuming the old type rather than the new type.

jurgenvinju commented 1 year ago

Further reduced this to an example that only does a coerce, same exceptions:

void compileTest() {
    Class cl = class(
        object("Test"),
        modifiers = {\public(), \final()},
        methods = [
            main(
                "args",

                [
                    \do(coerce(integer(), double(), iconst(2))),
                    \return()
                ]
            )
        ]
    );
    compileClass(cl, |cwd:///Test.class|);
}
jurgenvinju commented 1 year ago

It appears there are zero tests for the coerce expression... ominous.

jurgenvinju commented 1 year ago

Thanks @bys1; we just made flybytes a little better together. The small test case you produced made all the difference.

jurgenvinju commented 1 year ago

The entire implementation of coerce was broken. In 90% of the type combinations it would not push the argument expression onto the stack. Hence the stack was short by 1 or 2 slots, and the next operation (a store in your example) would pop off elements beyond the end of the stack.