File: CODEGEN_HOW_TO

package info (click to toggle)
jel 2.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 1,040 kB
  • ctags: 998
  • sloc: java: 6,703; xml: 1,753; makefile: 6
file content (123 lines) | stat: -rw-r--r-- 4,852 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
This is a brief explanation on how to drive JEL's code generator in
order to generate Java bytecode bypassing JEL's parser. If you plan to
generate expressions by a program (as opposed to letting user to type
them) this document might help you to eliminate parsing overhead. It
might be also useful to someone who wishes to build a compiler for
more complex languages on top of JEL.

Why would you want to use JEL's code generator, as opposed to other
Java assemblers/code generators ? Two main reasons for this are

1) Size. Up to my knowledge JEL is smallest of Java assemblers.

2) Performance. Due to the table-driven design instructions are
fetched from pre-built tables instead of computing them at run-time.

2) Convenience. JEL can match data types to instructions to
automatically generate the correct Java instructions for a particular
operation on a particular data type. It does widening data type
conversions automatically.

There are two modes in which you can use JEL's code generator. The
first is RAW assembler, it is provided by gnu.jel.ClassFile, and the
second is type-matching assembler (implemented in gnu.jel.OPxxx
classes).


I. RAW MODE.

By manipulating methods of gnu.jel.ClassFile you can create almost
arbitrary Java classfiles (not only descendants of
gnu.jel.CompiledExpression). The only feature currently absent from
ClassFile is writing Exception tables (but this can be easily
added). The ClassFile frees user from paying attention to the details
of the Java class file format (like constant pool handling) and
concentrate directly on the bytecode.


II. TYPE-MATCHING ASSEMBLER.

This helps to generate the bytecode once you have started a method of
your class using ClassFile. It only works for expressions right now,
but can be extended in obvious way to handle the control structures,
which was not implemented because it would blow up the size compiler
size and go out of a niche intended for JEL. I do have plans (when
time permits) to make another project for a full featured programming
language compiler on top of JEL.

In order to generate the code for an expression you need first write
it in a reverse Polish notation, then, set up an empty paramOPs stack,
and then instantiate the OPxxx classes for each operand/operation in
order. After passing through all your expression from left to right
you'll have only a single OP in paramOPs stack, which represents all
your expression.

For example suppose you want to add a numbers

1+2+2

in reverse Polish notation this reads as

2 2 1 + +

to create JEL's internal representation of this expression you do

java.util.Stack paramOps= new java.util.Stack();

paramOPs.push(new OPLoad(new Byte(2)));
paramOPs.push(new OPLoad(new Integer(2)));
paramOPs.push(new OPLoad(new Long(1)));
paramOPs.push(new OPbinary(paramOPs,0)); // 0 codes for '+' operation
paramOPs.push(new OPbinary(paramOPs,0));

your code is now

OP code=(OP) paramOPs.pop();

The type matching and widening conversions are done automatically by
JEL, you can query the result type by checking code.resType field. In
the case of the expression above the result will be equal to
Class.forName("java.lang.Long").

You can perform constants folding on an expression by doing

code.eval();

and throwing away a possibly emerged exception, which only signals
that the code can't be evaluated completely. For the expression given
above there will be no exception, because it is constant.

Finally, you can flush the Java bytecode into the ClassFile (say,
named cf) you have already set up.

code.compile(cf);

It will be appended to the code for the current method you already
have there.


III. WHAT INTERFACES MAY OR MAY NOT CHANGE.

The reason for writing this document was that JEL internal interfaces
have reached more or less optimal shape. Nevertheless, some things may
change in future. In particular, the additional (convenience)
bytecodes defined in ClassFile.code(long) method may change. The
ClassFile.newMethod() will certainly change when exception tables will
be implemented (however a compatible version may be provided).

The methods of gnu.jel.OP will not change (there can be some more
added though), and the constructors of all existing gnu.jel.OPxxx
classes will not change (they are all what is needed to know to use
JEL's code generator for expressions).

If you are interested in using JEL's code generator directly, please
drop me a note, so that I can inform you about interface changes.

I hope this text was helpful. If not, send your suggestions to
metlov@fti.dn.ua . Please, contact me also if I forgot to make some of
the methods described above (or required otherwise) public.

                               Copyright (C) 2009 Konstantin L. Metlov

This file is licensed to you under the terms of GNU Free Documentation
License as given in the appendix of JEL Manual.