lboasso / oberonc

An Oberon-07 compiler for the JVM
MIT License
147 stars 17 forks source link

Unexpected Runtime Exception When Casting INTEGER to BYTE #29

Closed geekstakulus closed 12 months ago

geekstakulus commented 12 months ago

Introduction

In Oberon-07, it's possible to convert one scalar data type to another through the SYSTEM.VAL function. This is a system dependent function, hence it belongs to the SYSTEM module. However, in the particular system, we are expected to get the corresponding value casted to the identity of the target data type.

Current Behavior

When casting a variable of type INTEGER to a variable of type BYTE, we get no compile-time error, but when executing we get an error saying that the variable of type INTEGER does not exist.

Expected Behavior

Compiling the following code and executing it:

MODULE WriteByteTestFailing;
  IMPORT Out, SYSTEM;
  VAR
    x : INTEGER;
    b : BYTE;
BEGIN
  x := 0ECFFH;
  b := SYSTEM.VAL(BYTE, x);

  Out.String("BYTE = ");
  Out.Int(b, 0); Out.Ln;
END WriteByteTestFailing.

should render the following output:

BYTE = 255

However, we get a runtime exception instead.

Repro Steps

  1. Compile the code using the Oberonc compiler.

  2. Observe the exception being raised:

Exception in thread "main" java.lang.NoSuchFieldError: x
    at WriteByteTestFailing.<clinit>(WriteByteTestFailing.Mod:8)

Other Information

This issue seems to be specific to the class file generated by the Oberonc compiler, because if we reverse engineer the code, we get the following java class:

public final class WriteByteTestFailing {
    public static String[] args = new String[0];
    public static int x = 60671;
    public static byte b = (byte)(x & 0xFF);

    public static void main(String[] stringArray) {
        args = stringArray;
    }

    static {
        Out.String((char[])"BYTE = \u0000".toCharArray());
        Out.Int((int)(b & 0xFF), (int)0);
        Out.Ln();
    }
}

Compiling it with javacand executing it, renders the expected output.

lboasso commented 12 months ago

Thanks for the bug report! Issue fixed here. In Oberon07 conversions between INTEGER and BYTE don't need SYSTEM.VAL, but nonetheless this was a bug :)

geekstakulus commented 12 months ago

Thanks for the bug report! Issue fixed here. In Oberon07 conversions between INTEGER and BYTE don't need SYSTEM.VAL, but nonetheless this was a bug :)

Thanks! Wirth has the information spread all over the reports, so one gets a little lost. Furthermore, there are some bugs in his Oberong Operating System updated for Oberon-07, because he just edited the original one and it is common or not. That guy is just a copy and paste fanatic. Just look at this code in one of the modules:

  PROCEDURE Find(id: CARDINAL): ObjPtr;
    VAR obj: ObjPtr;
  BEGIN Scope := topScope;
    LOOP obj := FindInScope(id, Scope^.right);
      IF obj # NIL THEN EXIT END ;
      IF Scope^.kind = Module THEN
        obj := FindInScope(id, universe^.right); EXIT
      END ;
      Scope := Scope^.left
    END ;
    RETURN obj
  END Find;

  PROCEDURE FindImport(id: CARDINAL): ObjPtr;
    VAR obj: ObjPtr;
  BEGIN Scope := topScope^.left;
    LOOP obj := FindInScope(id, Scope^.right);
      IF obj # NIL THEN EXIT END ;
      IF Scope^.kind = Module THEN
        obj := FindInScope(id, universe^.right); EXIT
      END ;
      Scope := Scope^.left
    END ;
    RETURN obj
  END FindImport;

The exact same code, but different names. One is for finding imports and the other for other identifiers. Translating the code to Oberon-07, with its read-only export variables, one can track a lot of design issues. He defines the data types in one module and has procedures to create them in another module. Worst, he creates global variables in the data definition module that will be initialized in another module. He has a lot of copy and paste procedures. Particularly nested ones with the exact same signature. This is how I caught the bug on repeated identifier in different scopes. This compiler is a real test for the Oberonc compiler. I am sure other bugs will pop up. :-D

Wirth is definitely crowned a lousy and lazy programmer. He is a genius at coming up language design and with guidelines, but pretty bad at following and implementing them. You notice the difference in quality when his students have done it. If you inspect the multi-pass compiler, it is far more organized. In the single-pass, the files have his name alone, so no one else to blame. :-D

Anyway, thanks for fixing the bug and for elucidating me on the Byte to Integer conversion.

Fidel H Viegas

geekstakulus commented 12 months ago

I just noticed that the procedures differ on the first statement. This style of programming where you have thousands of statements in a single line is not very readable.

Anyway, he could have just passed the parameter to the procedure and avoid duplication.

lboasso commented 11 months ago

In Wirth's defense that code is quite old (different minimal expectations, no code reviews) and it has not been published by him (either in a book or as official repository). If you look at the code from the first edition of Project Oberon or his current RISC compiler things are much better :) I agree that Wirth does not always follow his own recommendations.

This is how I caught the bug on repeated identifier in different scopes. This compiler is a real test for the Oberonc compiler. I am sure other bugs will pop up. :-D

Hopefully easy bugs to fix, thanks again for reporting them :)

geekstakulus commented 11 months ago

In Wirth's defense that code is quite old (different minimal expectations, no code reviews) and it has not been published by him (either in a book or as official repository). If you look at the code from the first edition of Project Oberon or his current RISC compiler things are much better :) I agree that Wirth does not always follow his own recommendations.

I think he could've done something better, but hey, he was lazy. So, let's just forgive him for that.

This is how I caught the bug on repeated identifier in different scopes. This compiler is a real test for the Oberonc compiler. I am sure other bugs will pop up. :-D

It is a pleasure! I am having a hard time translating the code, because the modules were really badly designed. I have translated 90% of the code, and I am nearly completing it. The next stage will be refactor the code into proper module design, and take advantage of Oberon's type extensions.

Thanks for fixing the bugs quickly!

I have modified the Files.Mod, but I am reverting it and having a module in the compiler that implements the features needed to replicate the Modula-2 functionalities.