The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

B::JVM::Jasmin - Perl Backend module for generating JVM code via Jasmin Assembler

SYNOPSIS

  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

DESCRIPTION

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.

AUTHOR

Bradley M. Kuhn, bkuhn@ebb.org, http://www.ebb.org/bkuhn

COPYRIGHT

Copyright (C) 1999, Bradley M. Kuhn, All Rights Reserved.

LICENSE

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.

SEE ALSO

perl(1), jasmin(1).

DETAILED DOCUMENTATION

B::JVM::Jasmin Package Variables

$VERSION

Version number of B::JVM::Jasmin

@ISA

Canonical @ISA array, currently only derives from Exporter

@EXPORT

Canonical @EXPORT array

@EXPORT_OK

Canonical @EXPORT_OK array

$STATE

Reference to a B::JVM::Jasmin::CompileState object for the current state of this compiler

Modules used by B::JVM::Jasmin

B

Of course, we must use the B module to overide its functions and interface with O.

Methods in B::JVM::Jasmin

B::JVM::Jasmin::compile

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

B::JVM::Jasmin::WalkOPTree

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.

B::OP::JVMJasminPre

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:

enter

Currently, nothing is done on an "enter" OP. This may change in the future, but I haven't seen a use for them (yet :).

pushmark

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.

null

A empty operation. Just send a "nop".

B::OP::JVMJasminPost

Currently, there is no post processing done on plain OPs.

B::LISTOP::JVMJasminPre

Pre-processing on LISTOPs requires that any we recursively call WalkOPTree, since LISTOPs can have children.

The following LISTOPs are currently supported:

leave

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.

print

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, "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.

list

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.

scope

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.

B::LISTOP::JVMJasminPost

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:

print

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.

At the end, we build a StackElement containing a Scalar that represents the final integer return value of the print statement.

B::COP::JVMJasminPre

The following COPs are currently supported:

nextstate

The "nextstate" COP clears the JVM operand stack. It finds the STATE_MARK, which indicates where and remaining data ends.

The STATE_MARK is left on the stack.

B::COP::JVMJasminPost
B::SVOP::JVMJasminPre

The following SVOPs are currently supported:

const

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.

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.

B::SVOP::JVMJasminPost

No post-processing is currently required for SVOPs.

B::BINOP::JVMJasminPre

Pre-processing on BINOPs requires that any we recursively call WalkOPTree, for the two sub-ops

The following BINOPs are currently supported:

sassign

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.

concat

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.

seq and sne

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.

B::BINOP::JVMJasminPost

Currently, post-processing occurs on the following BINOPs:

sassign

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.

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.

Then, to to perform the actual assignment, assignFromScalar is used.

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.

concat

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.

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.

seq and sne

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.

Then, we use that return value to create a new Scalar, wrapped in a StackElement, which is left on the stack at the end.

B::UNOP::JVMJasminPre

Pre-processing on UNOPs requires that any we recursively call WalkOPTree, for the two sub-ops

The following UNOPs are currently supported:

null

On "null" UNOPs, we simply process the sub-OP. More interesting stuff happens in the post-processing.

B::UNOP::JVMJasminPost

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:

null

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.

B::GVOP::JVMJasminPre

The following GVOPs are currently supported:

gvsv

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.

B::GVOP::JVMJasminPost

Post-processing is not currently required on any GVOPs.

B::LOGOP::JVMJasminPre

Pre-processing on LOGOPs requires that any we recursively call WalkOPTree, for the 3 sub-OPs.

The following LOGOPs are currently supported:

cond_expr

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.

B::LOGOP::JVMJasminPost

Currently, post-processing occurs on the following LOGOPs:

""
ClearOperandStackToStateMark

usage: 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.

If $leaveStateMark is defined and true, then the STATE_MARK is left on the stack, otherwise the STATE_MARK is removed.

TurnStackElementsToScalars

usage: TurnStackElementsToScalars($emitter, $curMethod, $count, $leaveArray);

This subroutine emits the required code to take $count StackElements off the stack and turn them back into Scalars.

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.

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.