Package Hack.VMEmulator

Source Code of Hack.VMEmulator.BuiltInFunctionsRunner$ProgramToBuiltInRequest

/********************************************************************************
* The contents of this file are subject to the GNU General Public License      *
* (GPL) Version 2 or later (the "License"); you may not use this file except   *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.gnu.org/copyleft/gpl.html                                         *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* without warranty of any kind, either expressed or implied. See the License   *
* for the specific language governing rights and limitations under the         *
* License.                                                                     *
*                                                                              *
* This file was originally developed as part of the software suite that        *
* supports the book "The Elements of Computing Systems" by Nisan and Schocken, *
* MIT Press 2005. If you modify the contents of this file, please document and *
* mark your changes clearly, for the benefit of others.                        *
********************************************************************************/

package Hack.VMEmulator;

import Hack.Controller.ProgramException;
import Hack.Utilities.Definitions;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* A class that runs built-in VM code as a coroutine so that
* built-in code may pause and call VM code that the user may debug and step
* through.
*/
public class BuiltInFunctionsRunner implements Runnable {

  // Message types between threads
  private static final int CALL_REQUEST = 0;
  private static final int RETURN_REQUEST = 1;
  private static final int END_PROGRAM_REQUEST = 2; // program => built-in
  private static final int THROW_PROGRAM_EXCEPTION_REQUEST = 3; // b.i.=>prog
  private static final int INFINITE_LOOP_REQUEST = 4; // b.i.=>prog

    // objects for communication between the threads
  private class BuiltInToProgramRequest {
    int request;
    String details;
    short[] params;
    short returnValue;
  }
    private class ProgramToBuiltInRequest {
    int request;
    Method functionObject;
    Object[] params;
    short returnValue;
  };
  private BuiltInToProgramRequest builtInToProgram;
  private ProgramToBuiltInRequest programToBuiltIn;

  // The thread that runs the built-in code
  private Thread thread;

  // The CPU that communicates with this class
  private CPU cpu;

  // The built-in dir
  private File builtInDir;
 
  /********************** Code common to both threads *****/

  /**
   * Relinquises control to the other thread until it relinquishes back.
   * Invariant: at any given time one of the threads is waiting here.
   */
  private synchronized void continueOtherThread() {
    notify();
    while (true) {
      try {
        wait();
      } catch (InterruptedException e) {
        continue;
      }
      return;
    }
  }

  /********************** Code run by the VM Emulator  thread *****/

    /**
     * Constructs a new BuiltInFunctionsRunner, which will read memory
   * and issue call-backs and returns to the given CPU and load built-in
   * vm code from the given directory.
     */
    public BuiltInFunctionsRunner(CPU cpu, File builtInDir) {
    this.cpu = cpu;
    this.builtInDir = builtInDir;
    builtInToProgram = new BuiltInToProgramRequest();
    programToBuiltIn = new ProgramToBuiltInRequest();
    thread = new Thread(this);
    synchronized (this) {
      thread.start();
      continueOtherThread(); // Let the built-in code runner init itself
                   // The notify part of this call does nothing
    }
    }

  /**
   * Called by the VM emulator. Tells the built-in code runner thread
   * to exit all currently running built-in functions. Returns after
   * this was completed.
   */
  public void killAllRunningBuiltInFunctions() {
    programToBuiltIn.request = END_PROGRAM_REQUEST;
    continueOtherThread();
  }

  /**
   * Called by the VM emulator. Tells  the built-in code runner thread
   * to resume an already-running built-in function which was waiting for
   * a return value from another function.
   */
  public void returnToBuiltInFunction(short returnValue) throws ProgramException {
    programToBuiltIn.request = RETURN_REQUEST;
    programToBuiltIn.returnValue = returnValue;
    sendBuiltInRequestAndWaitForAnswer();
  }

  /**
   * Called by the VM emulator. Searches for a built-in vm function by its
   * name and number of parameters (the length of the params array).
   * If found - tells the built-in code runner thread
   * to call the named built-in function with the given params.
   * Throws a ProgramException if no built-in implementation was found.
   */
  public void callBuiltInFunction(String functionName, short[] params) throws ProgramException {
        int dotLocation = functionName.indexOf(".");
        if (dotLocation == -1) {
            throw new ProgramException("Illegal function name: " + functionName);
    }
        String className = functionName.substring(0, dotLocation);
    String methodName = functionName.substring(dotLocation+1, functionName.length());
    if (methodName.equals("new")) {
      // new is a reserved Java word - therefore Jack functions named
      // new are implemented by Java functions named NEW.
      methodName = "NEW";
    }
   
    // Find the implementing class
    Class implementingClass;
    try {
      implementingClass = Class.forName(builtInDir+"."+className);
    } catch (ClassNotFoundException cnfe) {
      throw new ProgramException("Can't find "+className+".vm or a built-in implementation for class "+className);
    }

    // Check that the class is a subclass of BuiltInVMClass
    // (right now not that important if the class doesn't want to access
    // the computer - like math - but good practice anyway).
    Class currentClass = implementingClass;
    boolean found;
    do {
      currentClass = currentClass.getSuperclass();
      found = currentClass.getName().equals("Hack.VMEmulator.BuiltInVMClass");
    } while (!found && !currentClass.getName().equals("java.lang.Object"));
    if (!found) {
      throw new ProgramException("Built-in implementation for "+className+" is not a subclass of BuiltInVMClass");
    }

    // Find the implementing method & fill in the request
    Class[] paramsClasses = new Class[params.length];
    Object[] requestParams = new Object[params.length];
    for (int i=0; i<params.length; ++i) {
      requestParams[i] = new Short(params[i]);
      paramsClasses[i] = short.class;
    }

    Method functionObject;
    try {
      functionObject =
        implementingClass.getDeclaredMethod(methodName, paramsClasses);
    } catch (NoSuchMethodException nsme) {
      throw new ProgramException("Can't find "+className+".vm or a built-in implementation for function "+methodName+" in class "+className+" taking "+params.length+" argument"+(params.length==1?"":"s")+".");
    }
    Class returnType = functionObject.getReturnType();
    if (returnType != short.class && returnType != void.class &&
      returnType != char.class && returnType != boolean.class) {
      throw new ProgramException("Can't find "+className+".vm and the built-in implementation for "+functionName+" taking "+params.length+" arguments doesn't return short/char/void/boolean.");
    }
    programToBuiltIn.request = CALL_REQUEST;
    programToBuiltIn.params = requestParams;
    programToBuiltIn.functionObject = functionObject;
     
    sendBuiltInRequestAndWaitForAnswer();
  }

  /**
   * Sends a request to the built-in thread (the request is a data-member)
   * and waits for an answer from the built-in thread and passes it to the
   * VM Emulator.
   * If a built-in function finished, calls
   * cpu.returnFromBuiltInFunction with the return value.
   * If the function requested another function call,
   * calls cpu.callFunctionFromBuiltIn with the function name and parameters.
   * If an exception was thrown by a built-in function, throws a
   * ProgramException.
   */
  private void sendBuiltInRequestAndWaitForAnswer() throws ProgramException {
    continueOtherThread();
    switch(builtInToProgram.request) {
    case CALL_REQUEST:
      cpu.callFunctionFromBuiltIn(builtInToProgram.details,
                    builtInToProgram.params);
      break;
    case RETURN_REQUEST:
      cpu.returnFromBuiltInFunction(builtInToProgram.returnValue);
      break;
    case INFINITE_LOOP_REQUEST:
      cpu.infiniteLoopFromBuiltIn(builtInToProgram.details);
      break;
    case THROW_PROGRAM_EXCEPTION_REQUEST:
      throw new ProgramException(builtInToProgram.details);
      //break - unreachable
    }
  }

  /********************** Code run by the Built In Code Runner thread *****/

  /**
   * Runs the built-in code runner thread
   */
  public void run() {
    // Nothing on this thread should work while
    // the main program isn't waiting
    synchronized (this) {
      // Make sure that callbacks / memory access for built-in code
      // run on this thread is done by this instance of
      // BuiltInFunctionsRunner.
      BuiltInVMClass.associateForThread(this);
      while (true) {
        try {
          // Tell the VM Emulator that we finished init
          // by issueing a request for calling a "null function" -
          // The emulator expects us to tell it we finished init
          // and will ignore the actual function call request and
          // continue as normal.
          builtInFunctionRequestsCall(null, null);
        } catch (TerminateVMProgramThrowable e) {
          continue;
        }
        return;
      }
    }
  }

    /**
     * Called by a built-in function through the BuiltInVMClass class.
   * Requests that the VM Emulator run a function (either built-in or not).
   * Once the function completes, the function's return value is returned.
   * If due to a user's request or due to an error the VM program is
   * required to terminate, a TerminateVMProgramThrowable object is thrown.
   * The calling built-in function may catch this object, perform any
   * necessary cleanups, and rethrow it.
     */
  public short builtInFunctionRequestsCall(String functionName, short[] params) throws TerminateVMProgramThrowable {
    builtInToProgram.request = CALL_REQUEST;
    builtInToProgram.details = functionName;
    builtInToProgram.params = params;
    // Wait for a command and loop while we're getting call commands
    for(continueOtherThread(); programToBuiltIn.request == CALL_REQUEST;
      continueOtherThread()) {
      try { // Try to run the built-in implementation
        // programToBuiltIn might be overwritten until the return
        // from the call. Save what's needed.
        Class returnType = programToBuiltIn.functionObject.getReturnType();
        functionName =
          programToBuiltIn.functionObject.getName();
        // Execute
        Object returnValue =
          programToBuiltIn.functionObject.invoke(null,
                               programToBuiltIn.params);
        builtInToProgram.request = RETURN_REQUEST;
        if (returnType == short.class) {
          builtInToProgram.returnValue = ((Short)returnValue).shortValue();
        } else if (returnType == char.class) {
          builtInToProgram.returnValue = (short)((Character)returnValue).charValue();
        } else if (returnType == boolean.class) {
          if (((Boolean)returnValue).booleanValue()) {
            builtInToProgram.returnValue = (short)-1;
          } else {
            builtInToProgram.returnValue = 0;
          }
        } else { // returnType == void.class
          builtInToProgram.returnValue = 0;
        }
      } catch (IllegalAccessException iae) {
        // Error running - abort VM program
        builtInToProgram.request = THROW_PROGRAM_EXCEPTION_REQUEST;
        builtInToProgram.details = "Error trying to run the built-in implementation of "+functionName;
      } catch (InvocationTargetException ita) {
        // Rethrow a TerminateVMProgramThrowable object that was thrown
        try {
          throw (TerminateVMProgramThrowable)ita.getTargetException();
        } catch (ClassCastException cce) {
          // Error in the built-in function - abort VM program
          builtInToProgram.request = THROW_PROGRAM_EXCEPTION_REQUEST;
          builtInToProgram.details = "The built-in implementation of "+functionName+" caused an exception: "+ita.getTargetException().toString();
        }
      }
    }
    if (programToBuiltIn.request == RETURN_REQUEST) {
      return programToBuiltIn.returnValue;
    } else { // END_PROGRAM_REQUEST
      throw new TerminateVMProgramThrowable();
    }
  }

  /**
   * Makes sure an address that a built-in function requested
   * to write/read from is legal. If not - notifies the vm emulator
   * Thread that an exception occured, waits for a signal from it and
   * throws a TerminateVMProgramThrowable.
   */
  private void checkMemoryAddress(short address) throws TerminateVMProgramThrowable {
        if (!((address >= Definitions.HEAP_START_ADDRESS && address <= Definitions.HEAP_END_ADDRESS) ||
              (address >= Definitions.SCREEN_START_ADDRESS && address <= Definitions.SCREEN_END_ADDRESS) ||
              address == 0)) {
      builtInToProgram.request = THROW_PROGRAM_EXCEPTION_REQUEST;
      builtInToProgram.details = "A built-in function tried to access memory outside the Heap or Screen range";
      continueOtherThread();
      // now programToBuiltIn.request == END_PROGRAM_REQUEST
      throw new TerminateVMProgramThrowable();
    }
  }

  /**
   * Called by a built-in functio through the BuiltInVMClass class.
   * Enters an infinite loop, de-facto halting the program.
   * Important so that tests and other scripts finish counting
   * (since a built-in infinite loop doesn't count as steps).
   * Also needed because there is no good way to use the stop button to
   * stop an infinite loop in a built-in Jack class.
   * A message containing information may be provided (can be null).
   */
  public void builtInFunctionRequestsInfiniteLoop(String message)
      throws TerminateVMProgramThrowable {
    builtInToProgram.request = INFINITE_LOOP_REQUEST;
    builtInToProgram.details = message;
    continueOtherThread();
    // now programToBuiltIn.request == END_PROGRAM_REQUEST
    throw new TerminateVMProgramThrowable();
  }

  /**
     * Called by a built-in function through the BuiltInVMClass class.
   * Writes the given value top the given address in the VM memory.
   */
  public void builtInFunctionRequestsMemoryWrite(short address, short value) throws TerminateVMProgramThrowable {
    checkMemoryAddress(address);
    cpu.getRAM().setValueAt(address, value, false);
  }

  /**
     * Called by a built-in function through the BuiltInVMClass class.
   * Returns the contents of the given address in the VM memory.
   */
  public short builtInFunctionRequestsMemoryRead(short address) throws TerminateVMProgramThrowable {
    checkMemoryAddress(address);
    return cpu.getRAM().getValueAt(address);
  }
}
TOP

Related Classes of Hack.VMEmulator.BuiltInFunctionsRunner$ProgramToBuiltInRequest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.