B::JVM::Jasmin - Perl Backend module for generating JVM code via Jasmin Assembler
use B::JVM::Jasmin; compile(KEEP_INTERMEDIATE_FILES_FLAG, [MAIN_CLASS_NAME]); OR perl -MO=JVM::Jasmin,KEEP_INTERMEDIATE_FILES_FLAG, [MAIN_CLASS_NAME] file.plx
The B::JVM::Jasmin module is a Perl backend module that generates Jasmin assembler code (which can then be compiled into JVM code with jasmin(1)) for a Perl program.
Bradley M. Kuhn, bkuhn@ebb.org, http://www.ebb.org/bkuhn
Copyright (C) 1999, Bradley M. Kuhn, All Rights Reserved.
You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the LICENSE file that was shipped with this distribution.
perl(1), jasmin(1).
Version number of B::JVM::Jasmin
Canonical @ISA array, currently only derives from Exporter
Canonical @EXPORT array
Canonical @EXPORT_OK array
Reference to a B::JVM::Jasmin::CompileState object for the current state of this compiler
Of course, we must use the B module to overide its functions and interface with O.
usage: B::JVM::Jasmin::compile(KEEP_INTERMEDIATE_FILES_FLAG, [MAIN_CLASS_NAME])
This is the default method that O.pm will call when this backend is used. The first argument is a flag that indicates whether or not to keep the intermediate temporary files that are generated by the compilation process. The second argument
usage: B::JVM::Jasmin::compile($op, $level)
This method walks the op-tree and does pre and post processing on the op codes. In some cases, the pre-processing will call WalkOPTree for any sub-ops that exist.
This method handles pre-processing on plain OPs. Note that for all these OPs, there are no recursive calls to WalkOPTree, since plain OPs should never have sub-OPs (I think :).
The following OPs are currently supported:
Currently, nothing is done on an "enter" OP. This may change in the future, but I haven't seen a use for them (yet :).
On an "pushmark" OP, the LIST_MARK is pushed onto the JVM operand stack. This indicates to other OPs that expect a list that the list ends here.
A empty operation. Just send a "nop".
Currently, there is no post processing done on plain OPs.
Pre-processing on LISTOPs requires that any we recursively call WalkOPTree, since LISTOPs can have children.
WalkOPTree
The following LISTOPs are currently supported:
A "leave" LISTOP will be the parent of a number of OPs. Therefore, we process all the sub-OPs in order on the pre-processing step.
For a "print" LISTOP, we need to process the kids in reverse order, save pushing the LIST_MARK, which must happen first (the "print" post-processing depends on the mark being there). As in many instances, Perl appears to its own stack like a queue at times, and this causes problems, since we are using the JVM operand stack. In other words, for a "print" LISTOP, the Perl stack looks like this (left is top):
LIST_MARK
LIST_MARK, "1", "2\n"
However, this would print the string "12\n", not "2\n1". It's as if Perl first finds the mark, and then processes from the mark to the end of the stack as a queue! (This behavior is probably documented somewhere else, but I just discovered it serendipitously (perhaps I should read documentation more :)). So, to process a "print" statement, we grab the "pushmark" OP first, process that, and then call WalkOPTree recursively in reverse for the rest of the sub-OPs.
On a "list" LISTOP, there is no need to "pushmark" (I think :). When the "list" LISTOP completes (at least from analyzing perl -Dts output) the mark simply disappears. It doesn't appear to be there for any other operation but the "list" LISTOP itself. If this isn't correct, this code will need to be corrected.
In addition, the list is reversed on the JVM operand stack, just as is done with the "print" LISTOP.
No post-processing is needed (I think :), because the "list" LISTOP just sets up arguments for another OP.
A "scope" LISTOP will be the parent of a number of OPs. Therefore, we process all the sub-OPs in order on the pre-processing step. May processing may be needed, but I haven't discovered that it is yet.
Post-processing on LISTOPs is often required, since the sub-OPs often set up arguments for processing.
The following LISTOPs currently have post-processing done:
At this point, the pre-processing should have set up the arguments to "print" on the JVM operand stack. A LIST_MARK should mark the end of the argument list on the JVM operand stack. A temporary variable is used to store the return value of the print statement. It is and'ed with each Functions.print call return.
Functions.print
At the end, we build a StackElement containing a Scalar that represents the final integer return value of the print statement.
StackElement
Scalar
The following COPs are currently supported:
The "nextstate" COP clears the JVM operand stack. It finds the STATE_MARK, which indicates where and remaining data ends.
STATE_MARK
The STATE_MARK is left on the stack.
The following SVOPs are currently supported:
On a "const" SVOP, a Scalar and a StackElement to hold it must be built. This is done by grabbing the cstring() of the PV associated with the SV we are given. I don't know if every SV supports the PV() method, my thought is that it doesn't and this will need to be rewritten to handle other types of SVs.
cstring()
All this is done in pre-processing, no post-processing is necessary; the StackElement need only be left on the stack for whatever OP needs it.
No post-processing is currently required for SVOPs.
Pre-processing on BINOPs requires that any we recursively call WalkOPTree, for the two sub-ops
The following BINOPs are currently supported:
To pre-process an "sassign" BINOP, we need only process the two sub-OPs. We do so in, so that in post-processing, we can assume that the left-hand-side of the assignment is first on the stack, and the right-hand-side is the second down element on the stack.
The post-processing actually does the assignment.
To pre-process a "concat" BINOP, we need only process the two sub-OPs. We do so in order. Post processing would be easier if it was done in reverse order (since the top of the JVM operand stack would be the result we need to keep), but it is imperative we go in order for cascading operations to have the proper semantics.
To pre-process a "seq" and "sne" BINOPs, we need to process the two sub-OPs. We do so in order. Post processing would be easier if it was done in reverse order (since the top of the JVM operand stack would be the result we need to keep), but it is imperative we go in order for cascading operations to have the proper semantics.
Currently, post-processing occurs on the following BINOPs:
On post-processing, the "sassign" BINOP expects there to be two StackElements on the JVM operand stack. The top is the left-hand-side of the "sassign", and below that (on the operand stack) is the right-hand-side of the "sassign". Both Scalar quantities will be wrapped in StackElements.
StackElements
So, both StackElements are converted into Scalar. To do this, the function TurnStackElementsToScalars is called, and told to leave the stack elements in an array. The array that is returned is used set up the Scalars for assignment.
TurnStackElementsToScalars
Then, to to perform the actual assignment, assignFromScalar is used.
assignFromScalar
Finally, note as well that the left-hand-side StackElement is saved, as this should be the "return value" of the "sassign". It is left on the JVM operand stack at the end.
On post-processing, the "concat" BINOP expects there to be two StackElements on the JVM operand stack. The top is the right-operand of the "concat", and below that (on the operand stack) is the left-operand of the "concat". Both Scalar quantities will be wrapped in StackElements.
So, both StackElements are converted into Scalar. To do this, the function TurnStackElementsToScalars is called, and is told to leave the stack elements in an array. The array that is returned is used set up the Scalars for concatenation.
Then, to perform the actual concatenation, Scalar.concat function is used.
Scalar.concat
Finally, note as well that the left-operand StackElement is saved, as this should be the "return value" of the "concat". It is left on the JVM operand stack at the end.
On post-processing, the "seq" and "sne" BINOPs expect there to be two StackElements on the JVM operand stack. The top is the right-operand of the "seq", and below that (on the operand stack) is the left-operand of the "seq". Both Scalar quantities will be wrapped in StackElements.
So, both StackElements are converted into Scalar. To do this, the function TurnStackElementsToScalars is called, and is told to leave the stack elements in an array. The array that is returned is used set up the Scalars for comparison.
Then, to perform the actual comparison, Scalar.seq function is used.
Scalar.seq
Then, we use that return value to create a new Scalar, wrapped in a StackElement, which is left on the stack at the end.
Pre-processing on UNOPs requires that any we recursively call WalkOPTree, for the two sub-ops
The following UNOPs are currently supported:
On "null" UNOPs, we simply process the sub-OP. More interesting stuff happens in the post-processing.
Post-processing on UNOPs is required because some flags needed to be checked. The results of the UNOP's child may require manipulation based on these flags.
The following UNOPs currently require post-processing:
On a "null" UNOP, we need to check to see if the OPf_MOD flag is on. If it is not, then we need to make a copy of the Scalar result (wrapped in a StackElement on the stack. We canno inadvertently modify the l-value of a variable, so we make the copy.
If OPf_MOD is on, we need an l-value on the stack anyway, so things can be left alone.
The following GVOPs are currently supported:
To pre-process a "gvsv" GVOP, we must find the correct Scalar object.
First, the DEF_STASH is put on the JVM operand stack. Then, if this is not a variable in "main" namespace, we use that DEF_STASH to find the proper Stash (this part not yet working FIXME). Once the required Stash is found, the proper GV is obtained, using methods from the Stash class). The Scalar is retrieved from that GV. The Scalar is then wrapped in a StackElement object.
DEF_STASH
Stash
GV
Post-processing is not currently required on any GVOPs.
Pre-processing on LOGOPs requires that any we recursively call WalkOPTree, for the 3 sub-OPs.
The following LOGOPs are currently supported:
For an "cond_expr" LOGOP, we should have exactly three sub-OPs. This is a basic "if-else" structure (I think :). The first sub-OP will be the condition, the second sub-OP will be the block that should be executed if the condition is false, and the third is the block that should be executed if the condition is true.
So, we process each OP, and surround it with the logic that is required to carry out the conditional.
Currently, post-processing occurs on the following LOGOPs:
usage: ClearOperandStackToStateMark($emitter, $curMethod, $leaveStateMark);
ClearOperandStackToStateMark($emitter, $curMethod, $leaveStateMark);
This subroutine emits the required code to clear the JVM operand stack until a StackElement which is a state mark is reached. It emits the code using the given $emitter and to the method, $curMethod.
$emitter
$curMethod
If $leaveStateMark is defined and true, then the STATE_MARK is left on the stack, otherwise the STATE_MARK is removed.
$leaveStateMark
usage: TurnStackElementsToScalars($emitter, $curMethod, $count, $leaveArray);
TurnStackElementsToScalars($emitter, $curMethod, $count, $leaveArray);
This subroutine emits the required code to take $count StackElements off the stack and turn them back into Scalars.
$count
It is completely required, that this function be called only if the top $count elements on the JVM operand stack be StackElement objects who have valid elements.
To do this, an array of Scalars is built. A loop runs, getting the Scalar elements from the StackElements on the stack.
If $leaveArray is defined and true, then the variable name of the array is simply returned. The array variable returned as yet to be freed.
$leaveArray
If $leaveArray not defined or is false, then, another loop runs to push all the Scalars back onto the stack in the same order. The array varable is not returned (an empty string is returned instead). The array variable will be freed in this case.
To install B::JVM::Jasmin, copy and paste the appropriate command in to your terminal.
cpanm
cpanm B::JVM::Jasmin
CPAN shell
perl -MCPAN -e shell install B::JVM::Jasmin
For more information on module installation, please visit the detailed CPAN module installation guide.