HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

"Dynamic Code Generation"
Vol. 7, Issue 2, p. 60

	


Listing 1:

 SimpleCalculator.javaimport java.util.ArrayList;
 
import java.util.Stack;
import java.util.StringTokenizer;


public class SimpleCalculator
  implements Calculator
{
  String[] _toks;


  public SimpleCalculator(String expression) {
    ArrayList list =  new ArrayList();
    StringTokenizer tokenizer =
        new StringTokenizer(expression);


    while (tokenizer.hasMoreTokens()) {
      list.add(tokenizer.nextToken());
    }


    _toks = (String[]) list.toArray(
        new String[list.size()]);
  }


  public int evaluate(int[] args)
  {
    Stack stack = new Stack();


    for (int i=0; i < _toks.length; i++) {
      String tok = _toks[i];


      if (tok.startsWith("$")) {
        int varnum = Integer.
          parseInt(tok.substring(1));
        stack.push(new Integer(args[varnum]));
      } else {
        char opchar = tok.charAt(0);
        int  op = "+-*/".indexOf(opchar);


        if (op == -1) {
          stack.push(Integer.valueOf(tok));
        } else {
          int arg2 = ((Integer) stack.pop())
              .intValue();
          int arg1 = ((Integer) stack.pop())
              .intValue();


          switch(op) {
            case 0:
              stack.push(new Integer(arg1 + arg2));
              break;


            case 1:
              stack.push(new Integer(arg1 - arg2));
              break;


            case 2:
              stack.push(new Integer(arg1 * arg2));
              break;


            case 3:
              stack.push(new Integer(arg1 / arg2));
              break;


            default:
              throw new RuntimeException(
                "invalid operator: " + tok);
          }
        }
      }
    }


    return ((Integer) stack.pop()).intValue();
  }
}




Listing 2: CalculatorParser.java

import java.util.Stack;
import java.util.StringTokenizer;


public class CalculatorParser
{
  public Calculator parse(String expression)
  {
    Stack stack = new Stack();
    StringTokenizer toks =
      new StringTokenizer(expression);


    while (toks.hasMoreTokens()) {
      String tok = toks.nextToken();


      if (tok.startsWith("$")) {
        int varnum = Integer.
            parseInt(tok.substring(1));
        stack.push(new VariableValue(varnum));


      } else {
        int op = "+-*/".indexOf(tok.charAt(0));


        if (op == -1) {
          int val = Integer.parseInt(tok);
          stack.push(new ConstantValue(val));
        } else {
          Calculator node2 =
            (Calculator) stack.pop();
          Calculator node1 =
            (Calculator) stack.pop();


          stack.push(new Operation(tok.charAt(0),
            node1, node2));
        }
      }
    }


    return (Calculator) stack.pop();
  }


  static class ConstantValue
    implements Calculator
  {
    private int _value;


    ConstantValue(int value) {
      _value = value;
    }


    public int evaluate(int[] args) {
      return _value;
    }
  }



  static class VariableValue
    implements Calculator
  {
    private int _varnum;


    VariableValue(int varnum) {
      _varnum = varnum;
    }


    public int evaluate(int[] args) {
      return args[_varnum];
    }
  }


  static class Operation
    implements Calculator
  {
    char     _op;
    Calculator _arg1;
    Calculator _arg2;


    Operation(char op, Calculator arg1,
              Calculator arg2)
    {
      _op   = op;
      _arg1 = arg1;
      _arg2 = arg2;
    }


    public int evaluate(int args[]) {
      int val1 = _arg1.evaluate(args);
      int val2 = _arg2.evaluate(args);


      if (_op == '+') {
        return val1 + val2;
      } else if (_op == '-') {
        return val1 - val2;
      } else if (_op == '*') {
        return val1 * val2;
      } else if (_op == '/') {
        return val1 / val2;
      } else {
        throw new RuntimeException(
          "invalid operator: " + _op);
      }
    }
  }


}




Listing 3:

 CalculatorCompiler.javaimport java.util.Stack;
 
import java.util.StringTokenizer;


import java.io.*;


public class CalculatorCompiler
  extends ClassLoader
{
  String _compiler;
  String _classpath;


  public CalculatorCompiler() {
    super(ClassLoader.getSystemClassLoader());


    _compiler = System.getProperty(
      "calc.compiler");
    if (_compiler == null) {
      _compiler = "javac";
    }


    _classpath = ".";
    String extraclasspath =
      System.getProperty("calc.classpath");


    if (extraclasspath != null) {
      _classpath = _classpath +
        System.getProperty("path.separator") +
        extraclasspath;
    }
  }


  public Calculator compile(String expression)
  {
    String jtext   = javaExpression(expression);


    String filename  = "";
    String classname = "";


    try {
      File javafile = File.createTempFile(
        "compiled_", ".java", new File("."));


      filename  = javafile.getName();
      classname = filename.substring(0,
        filename.lastIndexOf("."));


      generateJavaFile(javafile, classname,
        expression);
      invokeCompiler(javafile);


      byte[] buf = readBytes(classname + ".class");
      Class  c   = defineClass(buf, 0, buf.length);
      try {
        return (Calculator) c.newInstance();
      } catch (IllegalAccessException e) {
        throw new RuntimeException(e.getMessage());
      } catch (InstantiationException e) {
        throw new RuntimeException(e.getMessage());
      }


    } catch(IOException e) {
      throw new RuntimeException(e.getMessage());
    }
  }


  void generateJavaFile(File javafile,
    String classname, String expression)
    throws IOException
  {
    FileOutputStream out =
      new FileOutputStream(javafile);


    String text = "public class " + classname +
      " implements Calculator {" +
      "  public int evaluate(int[] args) {" +
      "    " + javaExpression(expression) +
      "  }" +
      "}";


    out.write(text.getBytes());
    out.close();
  }



  void invokeCompiler(File javafile)
    throws IOException
  {
    String[] cmd   = {_compiler, "-classpath",
      _classpath,  javafile.getName()};
    Process  process = Runtime.getRuntime()
      .exec(cmd);


    try {
      process.waitFor();
    } catch (InterruptedException e) {
    }


    int val = process.exitValue();
    if (val != 0) {
      throw new RuntimeException(
        "compile error: " +
        "compiler return code is " + val);
    }
  }



  byte[] readBytes(String filename)
    throws IOException
  {
    File classfile = new File(filename);
    byte[] buf =
      new byte[(int) classfile.length()];


    FileInputStream in =
      new FileInputStream(classfile);
    in.read(buf);
    in.close();


    return buf;
  }



  String javaExpression(String expression)
  {
    Stack stack = new Stack();
    StringTokenizer toks =
      new StringTokenizer(expression);


    while (toks.hasMoreTokens()) {
      String tok = toks.nextToken();


      if (tok.startsWith("$")) {
        stack.push("args[" +
          Integer.parseInt(tok.substring(1)) +
          "]");


      } else {
        int op = "+-*/".indexOf(tok.charAt(0));


        if (op == -1) {
          stack.push(tok);
        } else {
          String arg2 = (String) stack.pop();
          String arg1 = (String) stack.pop();


          stack.push("(" + arg1 + " " +
            tok.charAt(0) + " " + arg2 + ")");
        }
      }
    }


    return "return " + (String) stack.pop() + ";";
  }
}



Listing 4: CalculatorGenerator.java

import java.io.*;


import java.util.Stack;
import java.util.StringTokenizer;


import de.fub.bytecode.classfile.*;
import de.fub.bytecode.generic.*;
import de.fub.bytecode.Constants;


public class CalculatorGenerator
  extends ClassLoader
{
  public Calculator generate(String expression)
  {
    String   classname = "Calc_" +
      System.currentTimeMillis();
    ClassGen classgen  = new ClassGen(classname,
      "java.lang.Object", "",
      Constants.ACC_PUBLIC | Constants.ACC_SUPER,
      new String[] {"Calculator"});


    classgen.addEmptyConstructor(
        Constants.ACC_PUBLIC);
    addEvalMethod(classgen, expression);


    byte[] data = classgen.getJavaClass()
        .getBytes();
    Class  c  = defineClass(data, 0, data.length);


    try {
      return (Calculator) c.newInstance();
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e.getMessage());
    } catch (InstantiationException e) {
      throw new RuntimeException(e.getMessage());
    }
  }


  private void addEvalMethod(ClassGen classgen,
    String expression)
  {
    ConstantPoolGen cp =
      classgen.getConstantPool();
    InstructionList il = new InstructionList();
    StringTokenizer toks =
      new StringTokenizer(expression);
    int stacksize = 0;
    int maxstack  = 0;


    while (toks.hasMoreTokens()) {
      String tok = toks.nextToken();


      if (tok.startsWith("$")) {
        int varnum = Integer.
          parseInt(tok.substring(1));


        // load array reference
        il.append(InstructionConstants.ALOAD_1);
        // load array index
        il.append(new PUSH(cp, varnum));
        // pull value from array
        il.append(InstructionConstants.IALOAD);


      } else {
        int op = "+-*/".indexOf(tok.charAt(0));


        switch(op) {
          case -1:
            int val = Integer.parseInt(tok);
            il.append(new PUSH(cp, val));
            break;
          case 0:
            il.append(InstructionConstants.IADD);
            break;
          case 1:
            il.append(InstructionConstants.ISUB);
            break;
          case 2:
            il.append(InstructionConstants.IMUL);
            break;
          case 3:
            il.append(InstructionConstants.IDIV);
            break;
          default:
            throw new RuntimeException(
              "invalid operator");
        }
      }
    }


    il.append(InstructionConstants.IRETURN);



    MethodGen method = new MethodGen(
      Constants.ACC_PUBLIC, Type.INT,
      new Type[] {Type.getType("[I")},
      new String[] {"args"},
      "evaluate", classgen.getClassName(),
      il, cp);


    method.setMaxStack();
    method.setMaxLocals();


    classgen.addMethod(method.getMethod());
  }
}

  
 
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.