:: Enseignements :: Master :: M2 :: 2016-2017 :: Machine Virtuelle (et bazar autour ...) ::
[LOGO]

Lab 3 - JVM Interpreter


We now want to implement an interpreter based on the JVM.
A source code to boot this lab is available: vm2016-lab3.zip
Here is a video of Charles Nutter, one of the creators of JRuby that explains how the Java bytecode works.
Java Bytecode for Dummies by Charles Nutter

Exercice 1 - JVM interpreter

The aim of this exercise is to write a simple interpreter/compiler that will generate a Java Class with one static method for each function of minijs.
One goal is to make the interpreter lazy and to only compile a method when needed (i.e. at the first execution) so the interpreter will work mostly like a JIT.
Once a function is translated to bytecode, the interpreter will transfer the execution to the JVM

  1. What is the purpose of the classes Rewriter, FunctionClassLoader, Dictionary and RT ?
    How the method Rewriter.createFunction works in details ?
    Why the enum StackResult exists ? How to use it ?
  2. Modify the Rewriter in order to execute the following code
      "hello"
            
    Verify that a POP is generated to remove "hello" from the stack.
    Note: you can use the instruction LDC to load a constant on stack.
  3. Why we can't reuse LDC to load an integer literal ?
    Use invokedynamic with the bootstrap method bsm_const defined in RT to load an integer literal in the same way that undefined is loaded.
    Test with the following code that everything works !
      3
            
  4. Implement in the Rewriter the function call and add a special case in bsm_funcall of RT to be able to call "print" by creating a private static method in RT that prints the value.
    And then verify that the following code works
      print("hello");
      print(3);
            

    Note: don't forget to drop the JSObject corresponding to the qualifier in Rt.bsm_funcall.
  5. What the following code should print ?
      print(print)
            

    In the Rewriter, change the lambda corresponding to LocalVarAccess to do a lookup on the global object.
    Note: you should have to implement bsm_lookup in RT too !
    Note 2: to get the JSObject "global", take the class of the caller (lookupClass), get the classloader, cast it to a FunctionClassLoader and then get the global object.
  6. Generalize the code of bsm_funcall to be able to code any JSObject which is a function creating by a method handle on JSObject.invoke.
    Note: the receiver of a function call is undefined, so the first argument of JSObject.invoke should be always undefined. You can use MethodHandles.insertArguments for that.
    Note 2: given that JSObject.invoke last parameter is an array (a varargs), you should use MethodHandle.asCollector to transform the method handle.
  7. What this code prints, is it what we want ?
      print(print(3))
            
  8. Implement the support of local variable in order to execute the following code:
      var a = 3;
      print(a);
            
  9. We want to be able to execute the following code:
      print(a);
      var a = 2;
            

    What method should be changed for that ?
    Change this method to be able to execute the code below.
  10. We now want to be able to create and execute a function.
      function foo(x) {
        return x + 1;
      }
    
      print(foo(2));
            
    Implement in the Rewriter the code that calls invokedynamic with the fun AST node (in order to do that, because invokedynamic is not able to encode live object but only constant, use the Dictionary object as usual) and then use the bootstrap method bsm_fun to create a JSObject corresponding to the function by calling the Rewriter and to show the JSObject as a constant.
    Note: remember that we want the interpreter to be lazy and create the JObject corresponding to a function only the first time the function is called.
    Note 2: if the function has a name, the function should be registered in the global object (see RT.bsm_register).
  11. We now want to implement the if.
      function f(x) {
        if (x < 3) {
          return 0;
        } else {
          return x;
        }
      }
    
      print(f(2));
      print(f(7));
            
    Implement the lambda in the visitor corresponding to the if in the Rewriter.
    Note: in byte, the instruction ifeq want a boolean on stack so we need a way to convert any Object to a boolean (see the class Boolean), that's what the bootstrap method bsm_truth does.
  12. Verify that recursive calls work by executing the sample samples/fibo.js.
      function fibo(n) {
        if (n < 2) {
          return 1
        } else {
          return fibo(n - 1) + fibo(n - 2)
        }
      }
    
      print(fibo(7))
            
  13. Add the support of the object creation and test with the following code:
      var o = {
        x: 1,
        y: 2
      }
            
    Note: here we know that the result is a JSObject, so creating the JSObject and populating it can be done by using classical call instruction like invokestatic and invokevirtual.
    Note 2: don't forget the JSObject prototype.
  14. Verify that the following code works:
      var a = 1;
      var o = {
        x: a,
        y: a + 1
      }
            
  15. Add the code to extract a field value from an object.
      var john = { name: "John" };
      print(john.name);
            
  16. And then add the code to change the value of a field of an object.
      var john = { name: "John" };
      john.name = "Jane";
      print(john.name);
            
  17. Verify that if the field doesn't exist, the field is created.
      var object = { };
      object.foo = "bar";
      print(object);