Package Hack.VMEmulator

Source Code of Hack.VMEmulator.VMProgram$LoadProgramTask

/********************************************************************************
* 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.ComputerParts.ComputerPartGUI;
import Hack.ComputerParts.InteractiveComputerPart;
import Hack.Controller.ProgramException;
import Hack.Events.ProgramEvent;
import Hack.Events.ProgramEventListener;
import Hack.Utilities.Definitions;
import Hack.Utilities.HackFileFilter;
import Hack.VirtualMachine.HVMInstructionSet;
import java.io.*;
import java.util.Hashtable;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.Vector;

/**
* A list of VM instructions, with a program counter.
*/
public class VMProgram extends InteractiveComputerPart
implements ProgramEventListener {

  // pseudo address for returning to built-in functions
  public static final short BUILTIN_FUNCTION_ADDRESS = -1;

  // Possible values for the current status - has the user allowed
  // access to built-in vm functions?
  private static final int BUILTIN_ACCESS_UNDECIDED = 0;
  private static final int BUILTIN_ACCESS_AUTHORIZED = 1;
  private static final int BUILTIN_ACCESS_DENIED = 2;

    // listeners to program changes
    private Vector listeners;

    // The list of VM instructions
    private VMEmulatorInstruction[] instructions;
  private int instructionsLength;
  private int visibleInstructionsLength;

    // The program counter - points to the next instruction that should be executed.
    private short nextPC;
    private short currentPC;
    private short prevPC;

    // The gui of the program.
    private VMProgramGUI gui;

    // The address of the initial instruction
    private short startAddress;

    // Mapping from file names to an array of two elements, containing the start and
    // end addresses of the corresponding static segment.
    private Hashtable staticRange;

  // Addresses of functions by name
  private Hashtable functions;
  private short infiniteLoopForBuiltInsAddress;
 
    // The current index of the static variables
    private int currentStaticIndex;

    // The largest static variable index found in the current file.
    private int largestStaticIndex;

  // Has the user allowed access to built-in vm functions?
  private int builtInAccessStatus;

  // Is the program currently being read in the middle of a /* */ comment?
  private boolean isSlashStar;

    /**
     * Constructs a new empty program with the given GUI.
     */
    public VMProgram(VMProgramGUI gui) {
        super(gui != null);
        this.gui = gui;
        listeners = new Vector();
        staticRange = new Hashtable();
    functions = new Hashtable();

        if (hasGUI) {
            gui.addProgramListener(this);
            gui.addErrorListener(this);
        }

        reset();
    }

    /**
     * Creates a vm program. If the given file is a dir, creates a program composed of the vm
     * files in the dir.
     * The vm files are scanned twice: in the first scan a symbol table (that maps
     * function & label names into addresses) is built. In the second scan, the instructions
     * array is built.
     * Throws ProgramException if an error occurs while loading the program.
     */
    public void loadProgram(String fileName) throws ProgramException {
        File file = new File(fileName);
        if (!file.exists())
            throw new ProgramException("cannot find " + fileName);

        File[] files;

        if (file.isDirectory()) {
            files = file.listFiles(new HackFileFilter(".vm"));
            if (files == null || files.length == 0)
                throw new ProgramException("No vm files found in " + fileName);
        }
        else
            files = new File[]{file};

        if (displayChanges)
            gui.showMessage("Loading...");

        // First scan
    staticRange.clear();
    functions.clear();
    builtInAccessStatus = BUILTIN_ACCESS_UNDECIDED;
        Hashtable symbols = new Hashtable();
    nextPC = 0;
        for (int i = 0; i < files.length; i++) {
            String name = files[i].getName();
            String className = name.substring(0, name.indexOf("."));
      // put some dummy into static range - just to tell the function
      // getAddress in the second pass which classes exist
      staticRange.put(className, new Boolean(true));
            try {
                updateSymbolTable(files[i], symbols, functions);
            } catch (ProgramException pe) {
                if (displayChanges)
                    gui.hideMessage();
                throw new ProgramException(name + ": " + pe.getMessage());
            }
        }
    boolean addCallBuiltInSysInit = false;
    if ((file.isDirectory() || symbols.get("Main.main") != null) &&
      symbols.get("Sys.init") == null) {
      // If the program is in multiple files or there's a Main.main
      // function it is assumed that it should be run by calling Sys.init.
      // If no Sys.init is found, add an invisible line with a call
      // to Sys.init to start on - the builtin version will be called.
      addCallBuiltInSysInit = true;
      getAddress("Sys.init"); // confirm calling the built-in Sys.init
      ++nextPC; // A "call Sys.init 0" line will be added
    }

        instructions = new VMEmulatorInstruction[nextPC+4];

        // Second scan
        nextPC = 0;
        currentStaticIndex = Definitions.VAR_START_ADDRESS;
        for (int i = 0; i < files.length; i++) {
            String name = files[i].getName();
            String className = name.substring(0, name.indexOf("."));

            largestStaticIndex = -1;
            int[] range = new int[2];
            range[0] = currentStaticIndex;

            try {
        // functions is not passed as an argument since it is accessed
        // through getAddress()
                buildProgram(files[i], symbols);
            } catch (ProgramException pe) {
                if (displayChanges)
                    gui.hideMessage();
                throw new ProgramException(name + ": " + pe.getMessage());
            }

            currentStaticIndex += largestStaticIndex + 1;
            range[1] = currentStaticIndex - 1;
            staticRange.put(className, range);
        }
    instructionsLength = visibleInstructionsLength = nextPC;
    if (builtInAccessStatus == BUILTIN_ACCESS_AUTHORIZED) {
      // Add some "invisible" code in the end to make everything work
      instructionsLength += 4;
      if (addCallBuiltInSysInit) {
        instructionsLength += 1;
      }
      short indexInInvisibleCode = 0;
      // Add a jump to the end (noone should get here since
      // both calls to built-in functions indicate that
      // that this is a function-based program and not a script
      // a-la proj7, but just to be on the safe side...).
      instructions[nextPC] =
        new VMEmulatorInstruction(HVMInstructionSet.GOTO_CODE,
                      (short)instructionsLength,
                      indexInInvisibleCode);
      instructions[nextPC].setStringArg("afterInvisibleCode");
      nextPC++;
      // Add a small infinite loop for built-in
      // methods to call (for example when Sys.halt is
      // called it must call a non-built-in infinite loop
      // because otherwise the current script would not
      // finish running - a problem for the OS tests.
      instructions[nextPC] =
        new VMEmulatorInstruction(HVMInstructionSet.LABEL_CODE,
                      (short)-1);
      instructions[nextPC].setStringArg("infiniteLoopForBuiltIns");
      nextPC++;
      infiniteLoopForBuiltInsAddress = nextPC;
      instructions[nextPC] =
        new VMEmulatorInstruction(HVMInstructionSet.GOTO_CODE,
                      nextPC, ++indexInInvisibleCode);
      instructions[nextPC].setStringArg("infiniteLoopForBuiltIns");
      nextPC++;
      if (addCallBuiltInSysInit) { // Add a call to the built-in Sys.init
        instructions[nextPC] =
          new VMEmulatorInstruction(HVMInstructionSet.CALL_CODE,
                        getAddress("Sys.init"), (short)0,
                        ++indexInInvisibleCode);
        instructions[nextPC].setStringArg("Sys.init");
        startAddress = nextPC;
        nextPC++;
      }
      // Add the label that the first invisible code line jumps to
      instructions[nextPC] =
        new VMEmulatorInstruction(HVMInstructionSet.LABEL_CODE,
                      (short)-1);
      instructions[nextPC].setStringArg("afterInvisibleCode");
      nextPC++;
    }

    if (!addCallBuiltInSysInit) {
      Short sysInitAddress = (Short)symbols.get("Sys.init");
      if (sysInitAddress == null) // Single file, no Sys.init - start at 0
        startAddress = 0;
      else // Implemented Sys.init - start there
        startAddress = sysInitAddress.shortValue();
    }

        if (displayChanges)
            gui.hideMessage();

    nextPC = startAddress;
        setGUIContents();

        notifyProgramListeners(ProgramEvent.LOAD, fileName);
    }

    // Scans the given file and creates symbols for its functions & label names.
    private void updateSymbolTable(File file, Hashtable symbols, Hashtable functions) throws ProgramException {
        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new FileReader(file.getAbsolutePath()));
        } catch (FileNotFoundException fnfe) {
            throw new ProgramException("file " + file.getName() + " does not exist");
        }

        String line;
        String currentFunction = null;
        String label;
        int lineNumber = 0;

    isSlashStar = false;
        try {
            while ((line = unCommentLine(reader.readLine())) != null) {
                lineNumber++;
                if (!line.trim().equals("")) {
                    if (line.startsWith("function ")) {
                        StringTokenizer tokenizer = new StringTokenizer(line);
                        tokenizer.nextToken();
                        currentFunction = tokenizer.nextToken();
                        if (symbols.containsKey(currentFunction))
                            throw new ProgramException("subroutine " + currentFunction +
                                                       " already exists");
                        functions.put(currentFunction, new Short(nextPC));
                        symbols.put(currentFunction, new Short(nextPC));
                    }
                    else if (line.startsWith("label ")) {
                        StringTokenizer tokenizer = new StringTokenizer(line);
                        tokenizer.nextToken();
                        label = currentFunction + "$" + tokenizer.nextToken();
                        symbols.put(label, new Short((short)(nextPC + 1)));
                    }

                    nextPC++;
                }
            }
            reader.close();
        } catch (IOException ioe) {
            throw new ProgramException("Error while reading from file");
        } catch (NoSuchElementException nsee) {
            throw new ProgramException("In line " + lineNumber + ": unexpected end of command");
        }
    if (isSlashStar) {
      throw new ProgramException("Unterminated /* comment at end of file");
    }
    }

    // Scans the given file and creates symbols for its functions & label names.
    private void buildProgram(File file, Hashtable symbols)
        throws ProgramException {

        BufferedReader reader = null;

        try {
            reader = new BufferedReader(new FileReader(file.getAbsolutePath()));
        } catch (FileNotFoundException fnfe) {
            throw new ProgramException("file does not exist");
        }

        int lineNumber = 0;
        String line;
        String label;
        String instructionName;
        String currentFunction = null;
        short indexInFunction = 0;
        byte opCode;
        short arg0, arg1;
        short pc = nextPC;
        HVMInstructionSet instructionSet = HVMInstructionSet.getInstance();

    isSlashStar = false;
        try {
            while ((line = unCommentLine(reader.readLine())) != null) {
                lineNumber++;

                if (!line.trim().equals("")) {
                    StringTokenizer tokenizer = new StringTokenizer(line);
                    instructionName = tokenizer.nextToken();

                    opCode = instructionSet.instructionStringToCode(instructionName);
                    if (opCode == HVMInstructionSet.UNKNOWN_INSTRUCTION)
                        throw new ProgramException("in line " + lineNumber +
                                                   ": unknown instruction - " + instructionName);

                    switch (opCode) {
                        case HVMInstructionSet.PUSH_CODE:
                            String segment = tokenizer.nextToken();
                            try {
                                arg0 = translateSegment(segment, instructionSet, file.getName());
                            } catch (ProgramException pe) {
                                throw new ProgramException("in line " + lineNumber + pe.getMessage());
                            }
                            arg1 = Short.parseShort(tokenizer.nextToken());
                            if (arg1 < 0)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Illegal argument - " + line);

                            if (arg0 == HVMInstructionSet.STATIC_SEGMENT_CODE && arg1 > largestStaticIndex)
                                largestStaticIndex = arg1;

                            instructions[pc] = new VMEmulatorInstruction(opCode, arg0, arg1,
                                                                         indexInFunction);
                            break;

                        case HVMInstructionSet.POP_CODE:
                            int n = tokenizer.countTokens();
                            segment = tokenizer.nextToken();
                            try {
                                arg0 = translateSegment(segment, instructionSet, file.getName());
                            } catch (ProgramException pe) {
                                throw new ProgramException("in line " + lineNumber + pe.getMessage());
                            }
                            arg1 = Short.parseShort(tokenizer.nextToken());

                            if (arg1 < 0)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Illegal argument - " + line);

                            if (arg0 == HVMInstructionSet.STATIC_SEGMENT_CODE && arg1 > largestStaticIndex)
                                largestStaticIndex = arg1;

                            instructions[pc] = new VMEmulatorInstruction(opCode, arg0, arg1,
                                                                         indexInFunction);
                            break;

                        case HVMInstructionSet.FUNCTION_CODE:
                            currentFunction = tokenizer.nextToken();
                            indexInFunction = 0;
                            arg0 = Short.parseShort(tokenizer.nextToken());

                            if (arg0 < 0)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Illegal argument - " + line);

                            instructions[pc] = new VMEmulatorInstruction(opCode, arg0, indexInFunction);
                            instructions[pc].setStringArg(currentFunction);
                            break;

                        case HVMInstructionSet.CALL_CODE:
                            String functionName = tokenizer.nextToken();
              try {
                arg0 = getAddress(functionName);
              } catch (ProgramException pe) {
                throw new ProgramException("in line " +
                               lineNumber + ": " +
                               pe.getMessage());
              }
                            arg1 = Short.parseShort(tokenizer.nextToken());

                            if (arg1 < 0 || ((arg0 < 0 || arg0 > Definitions.ROM_SIZE) && arg0 != BUILTIN_FUNCTION_ADDRESS))
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Illegal argument - " + line);

                            instructions[pc] = new VMEmulatorInstruction(opCode, arg0, arg1,
                                                                         indexInFunction);
                            instructions[pc].setStringArg(functionName);
                            break;

                        case HVMInstructionSet.LABEL_CODE:
                            label = currentFunction + "$" + tokenizer.nextToken();
                            instructions[pc] = new VMEmulatorInstruction(opCode, (short)(-1));
                            instructions[pc].setStringArg(label);
                            indexInFunction--; // since Label is not a "physical" instruction
                            break;

                        case HVMInstructionSet.GOTO_CODE:
                            label = currentFunction + "$" + tokenizer.nextToken();
                            Short labelAddress = (Short)symbols.get(label);
                            if (labelAddress == null)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Unknown label - " + label);
                            arg0 = labelAddress.shortValue();

                            if (arg0 < 0 || arg0 > Definitions.ROM_SIZE)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Illegal argument - " + line);

                            instructions[pc] = new VMEmulatorInstruction(opCode, arg0, indexInFunction);
                            instructions[pc].setStringArg(label);
                            break;

                        case HVMInstructionSet.IF_GOTO_CODE:
                            label = currentFunction + "$" + tokenizer.nextToken();
                            labelAddress = (Short)symbols.get(label);
                            if (labelAddress == null)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Unknown label - " + label);

                            arg0 = labelAddress.shortValue();

                            if (arg0 < 0 || arg0 > Definitions.ROM_SIZE)
                                throw new ProgramException("in line " + lineNumber +
                                                           ": Illegal argument - "  + line);

                            instructions[pc] = new VMEmulatorInstruction(opCode, arg0, indexInFunction);
                            instructions[pc].setStringArg(label);
                            break;

                        // All other instructions have either 1 or 0 arguments and require no
                        // special treatment
                        default:
                            if (tokenizer.countTokens() == 0) {
                                instructions[pc] = new VMEmulatorInstruction(opCode, indexInFunction);
                            }
                            else {
                                arg0 = Short.parseShort(tokenizer.nextToken());

                                if (arg0 < 0)
                                    throw new ProgramException("in line " + lineNumber +
                                                               ": Illegal argument - " + line);

                                instructions[pc] = new VMEmulatorInstruction(opCode, arg0,
                                                                             indexInFunction);
                            }
                            break;
                    }

                    // check end of command
                    if (tokenizer.hasMoreTokens())
                        throw new ProgramException("in line " + lineNumber +
                                                   ": Too many arguments - " + line);

                    pc++;
                    indexInFunction++;
                }

                nextPC = pc;
            }
            reader.close();
        } catch (IOException ioe) {
            throw new ProgramException("Error while reading from file");
        } catch (NumberFormatException nfe) {
            throw new ProgramException("Illegal 16-bit value");
        } catch (NoSuchElementException nsee) {
            throw new ProgramException("In line " + lineNumber + ": unexpected end of command");
        }
    if (isSlashStar) {
      throw new ProgramException("Unterminated /* comment at end of file");
    }
    }

    // Returns the "un-commented" version of the given line.
  // Comments can be either with // or /*.
  // The field isSlashStar holds the current /* comment state.
    private String unCommentLine(String line) {
        String result = line;

        if (line != null) {
      if (isSlashStar) {
        int posStarSlash = line.indexOf("*/");
        if (posStarSlash >= 0) {
          isSlashStar = false;
          result = unCommentLine(line.substring(posStarSlash+2));
        } else {
          result = "";
        }
      } else {
        int posSlashSlash = line.indexOf("//");
        int posSlashStar = line.indexOf("/*");
        if (posSlashSlash >= 0 &&
          (posSlashStar < 0 || posSlashStar > posSlashSlash)) {
          result = line.substring(0, posSlashSlash);
        } else if (posSlashStar >= 0) {
          isSlashStar = true;
          result = line.substring(0, posSlashStar) +
               unCommentLine(line.substring(posSlashStar+2));
        }
      }
        }

        return result;
    }

    /**
     * Returns the static variable address range of the given class name, in the
     * form of a 2-elements array {startAddress, endAddress}.
     * If unknown class name, returns null.
     */
    public int[] getStaticRange(String className) {
        return (int[])staticRange.get(className);
    }

    /**
     * Returns the size of the program.
     */
    public int getSize() {
        return instructionsLength;
    }

  public short getAddress(String functionName) throws ProgramException {
    Short address = (Short)functions.get(functionName);
    if (address != null) {
      return address.shortValue();
    } else {
      String className =
        functionName.substring(0, functionName.indexOf("."));
      if (staticRange.get(className) == null) {
        // The class is not implemented by a VM file - search for a
        // built-in implementation later. Display a popup to confirm
        // this as this is not a feature from the book but a later
        // addition.
        if (builtInAccessStatus == BUILTIN_ACCESS_UNDECIDED) {
          if (hasGUI && gui.confirmBuiltInAccess()) {
            builtInAccessStatus = BUILTIN_ACCESS_AUTHORIZED;
          } else {
            builtInAccessStatus = BUILTIN_ACCESS_DENIED;
          }
        }
        if (builtInAccessStatus == BUILTIN_ACCESS_AUTHORIZED) {
          return BUILTIN_FUNCTION_ADDRESS;
        }
      }
      // Either:
      // 1.The class is implemented by a VM file and no implementation
      //     for the function is found - don't override with built-in
      // - or -
      // 2.The user did not authorize using built-in implementations.
      throw new ProgramException(className + ".vm not found " +
                     "or function " + functionName +
                     " not found in " + className + ".vm");
    }
  }

    /**
     * Returns the next program counter.
     */
    public short getPC() {
        return nextPC;
    }

    /**
     * Returns the current value of the program counter.
     */
    public short getCurrentPC() {
        return currentPC;
    }

    /**
     * Returns the previous value of the program counter.
     */
    public short getPreviousPC() {
        return prevPC;
    }

    /**
     * Sets the program counter with the given address.
     */
    public void setPC(short address) {
        prevPC = currentPC;
        currentPC = nextPC;
        nextPC = address;
        setGUIPC();
    }

    /**
     * Sets the program counter to a specially created infinite loop in the
   * end of the programs for access by built-in functions, 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 setPCToInfiniteLoopForBuiltIns(String message) {
    if (hasGUI) {
      gui.notify(message);
    }
    setPC(infiniteLoopForBuiltInsAddress);
    }

    /**
     * Returns the next VMEmulatorInstruction and increments the PC by one.
     * The PC will be incremented by more if the next instruction is a label.
     */
    public VMEmulatorInstruction getNextInstruction() {
        VMEmulatorInstruction result = null;

        if (nextPC < instructionsLength) {
            result = instructions[nextPC];
            prevPC = currentPC;
            currentPC = nextPC;

            do {
                nextPC++;
            } while (nextPC < instructionsLength &&
                     instructions[nextPC].getOpCode() == HVMInstructionSet.LABEL_CODE);

            setGUIPC();
        }

        return result;
    }

    /**
     * Restarts the program from the beginning.
     */
    public void restartProgram() {
        currentPC = -999;
        prevPC = -999;
        nextPC = startAddress;
        setGUIPC();
    }

    /**
     * Resets the program (erases all commands).
     */
    public void reset() {
        instructions = new VMEmulatorInstruction[0];
    visibleInstructionsLength = instructionsLength = 0;
        currentPC = -999;
        prevPC = -999;
        nextPC = -1;
        setGUIContents();
    }

    /**
     * Returns the GUI of the computer part.
     */
    public ComputerPartGUI getGUI() {
        return gui;
    }

    /**
     * Called when the current program file/directory is changed.
     * The event contains the source object, the event type and the program's file/dir (if any).
     */
    public void programChanged(ProgramEvent event) {
        switch (event.getType()) {
            case ProgramEvent.LOAD:
                LoadProgramTask task = new LoadProgramTask(event.getProgramFileName());
                Thread t = new Thread(task);
                t.start();
                break;
            case ProgramEvent.CLEAR:
                reset();
                notifyProgramListeners(ProgramEvent.CLEAR, null);
                break;
        }
    }

    // Returns the numeric representation of the given string segment.
    // Throws an exception if unknown segment.
    private byte translateSegment(String segment, HVMInstructionSet instructionSet,
                                  String fileName)
     throws ProgramException {
        byte code = instructionSet.segmentVMStringToCode(segment);
        if (code == HVMInstructionSet.UNKNOWN_SEGMENT)
            throw new ProgramException(": Illegal memory segment - "
                                       + segment);

        return code;
    }

    // Sets the gui's contents (if a gui exists)
    private void setGUIContents() {
        if (displayChanges) {
            gui.setContents(instructions, visibleInstructionsLength);
            gui.setCurrentInstruction(nextPC);
        }
    }

    // Sets the GUI's current instruction index
    private void setGUIPC() {
        if (displayChanges)
            gui.setCurrentInstruction(nextPC);
    }

    // The task that loads a new program into the emulator
    class LoadProgramTask implements Runnable {

        private String fileName;

        public LoadProgramTask(String fileName) {
            this.fileName = fileName;
        }

        public void run() {
            clearErrorListeners();
            try {
                loadProgram(fileName);
            } catch (ProgramException pe) {
                notifyErrorListeners(pe.getMessage());
            }
        }
    }

    public void refreshGUI() {
        if (displayChanges) {
            gui.setContents(instructions, visibleInstructionsLength);
            gui.setCurrentInstruction(nextPC);
        }
    }

    /**
     * Registers the given ProgramEventListener as a listener to this GUI.
     */
    public void addProgramListener(ProgramEventListener listener) {
        listeners.add(listener);
    }

    /**
     * Un-registers the given ProgramEventListener from being a listener to this GUI.
     */
    public void removeProgramListener(ProgramEventListener listener) {
        listeners.remove(listener);
    }

    /**
     * Notifies all the ProgramEventListeners on a change in the VM's program by creating
     * a ProgramEvent (with the new event type and program's file name) and sending it using the
     * programChanged function to all the listeners.
     */
    protected void notifyProgramListeners(byte eventType, String programFileName) {
        ProgramEvent event = new ProgramEvent(this, eventType, programFileName);

        for (int i = 0; i < listeners.size(); i++) {
            ((ProgramEventListener)listeners.elementAt(i)).programChanged(event);
        }
    }
}
TOP

Related Classes of Hack.VMEmulator.VMProgram$LoadProgramTask

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.