📜 ⬆️ ⬇️

Expression parsing, bytecode-way

Did you have to disassemble the expression ? To draw a graph of the function on the line entered by the user?

Agree, the occupation brings more headaches than the joy of the result. Perhaps you are familiar with the antlr or javacc libraries , then you will get rid of a little blood. But acquire the tail of ugly generated classes, which as soon as possible hide from prying eyes in the farthest package.

Having written yesterday about cglib , I noticed in the documentation a chapter on bytecode modification. And by itself, the question arises, is it possible at runtime to force the class to perform, what it really wants, and not what the class wants?

')
I honestly took up the study of documentation and quickly went to the popular bym-code editor asm . However, to my disappointment, modifying the byte-code can only be quite good knowing and understanding it. More precisely, this means that if you need to return the result of executing “x * x + x / 2”, you must compile this expression in the statement.

Not wanting to write a compiler, I still formulated a request for google and found what I was looking for.

To my luck, a similar compiler is already implemented in another javassist bytecode editor. By the way, javassist is more documented, however in the open-source community there is an opinion that cglib is much faster. In our case, the speed of creating a class is not as important as the speed of its methods. So, what needs to be done:

Copy Source | Copy HTML public interface Evaluator { public double eval( double x); }
  1. Copy Source | Copy HTML public interface Evaluator { public double eval( double x); }
  2. Copy Source | Copy HTML public interface Evaluator { public double eval( double x); }
  3. Copy Source | Copy HTML public interface Evaluator { public double eval( double x); }


Class creation:

Copy Source | Copy HTML
  1. ClassPool pool = ClassPool.getDefault ();
  2. CtClass evalClass = pool.makeClass ( "Formula" );
  3. evalClass.setInterfaces (
  4. new CtClass [] {
  5. pool.makeClass ( "com.micro.bench.Evaluator" )
  6. });
  7. String expession = "x * x + x / 2" ;
  8. evalClass.addMethod (
  9. CtNewMethod.make (
  10. "public double eval (double x) {return (" + expession + ");}" ,
  11. evalClass));
  12. Class clazz = evalClass.toClass ();
  13. runtime = (Evaluator) clazz.newInstance ();


Everything! In our hands, a class object that implements the Evaluator interface, which will return x * x + x / 2 to us

Well, now it would not be bad to compare performance. Thanks to TheShade for the comment , I modified Timer a bit and used it for testing. A test result showing that both methods run at the same time:

      total |  amount |  last |  last 5 |  last 10 |  avg |  dev |  operation
    6856.00 |  20.00 |  351.00 |  343.00 |  341.00 |  342.80 |  1.24 |  .. runtime calc
    6874.00 |  20.00 |  337.00 |  343.00 |  344.00 |  343.70 |  1.03 |  .. compile-time calc



Class Evaluation.java - Comparison of two methods
Class Timer.java - Measuring speed and statistics

Source: https://habr.com/ru/post/69728/


All Articles