=begin pod

=head1 NAME

MoarVM::Bytecode - Provide introspection into MoarVM bytecode

=head1 SYNOPSIS

=begin code :lang<raku>
use MoarVM::Bytecode;

my $M = MoarVM::Bytecode.new($filename);  # or letter or IO or Blob

say $M.hll-name;     # most likely "Raku"

say $M.strings[99];  # the 100th string on the string heap
=end code

=head1 DESCRIPTION

MoarVM::Bytecode provides an object oriented interface to the MoarVM
bytecode format, based on the information provided in
L<docs/bytecode.markdown|https://github.com/MoarVM/MoarVM/blob/main/docs/bytecode.markdown#bytecode>.

=head1 CLASS METHODS

=head2 new

=begin code :lang<raku>
my $M = MoarVM::Bytecode.new("c");             # the 6.c setting

my $M = MoarVM::Bytecode.new("foo/bar");       # path as string

my $M = MoarVM::Bytecode.new($filename.IO);    # path as IO object

my $M = MoarVM::Bytecode.new($buf);            # a Buf object
=end code

Create an instance of the C<MoarVM::Bytecode> object from a letter
(assumed to be a Raku version letter such as "c", "d" or "e"), a
filename, an C<IO::Path> or a C<Buf>/C<Blob> object.

=head2 files

=begin code :lang<raku>
.say for MoarVM::Bytecode.files;

.say for MoarVM::Bytecode.files(:instantiate);
=end code

Returns a sorted list of paths of MoarVM bytecode files that could be found
in the installation of the currently running C<rakudo> executable.

Optionally accepts a C<:instantiate> named argument to return
a sorted list of instantiated C<MoarVM::Bytecode> objects instead of
just paths.

=head2 root

=begin code :lang<raku>
my $rootdir = MoarVM::Bytecode.rootdir;
=end code

Returns an C<IO::Path> of the root directory of the installation of the
currently running C<rakudo> executable.

=head2 setting

=begin code :lang<raku>
my $setting = MoarVM::Bytecode.setting;

my $setting = MoarVM::Bytecode.setting("d");
=end code

Returns an C<IO::Path> of the bytecode file of the given setting letter.
Assumes the currently lowest supported setting by default.

=head1 HELPER SCRIPTS

=head2 bceval

=begin code
$ bceval c '.strings.grep(*.contains("zip"))'
&zip
zip
zip-latest
=end code

Helper script to allow simple actions on a C<MoarVM::Bytecode> object
from the command line.  The first argument indicates the bytecode file
to load (either a C<use> target, setting letter or path.  The second
argument indicates the code to be executed.

The topic C<$_> is set with the C<MoarVM::Bytecode> object upon entry.

If the result is an C<Iterable>, then each iteration will be shown
on a separate line.  Otherwise the return value will be shown.

=head2 bcinfo

=begin code
$ bcinfo --help
Usage:
  bin/bcinfo <file> [--filename=<Str>] [--name=<Str>] [--opcode=<Str>] [--header] [--decomp] [--hexdump] [--verbose]

    <file>              use target, setting letter or bytecode path
    --filename=<Str>    select frames with given filename
    --name=<Str>        select frames with given name
    --opcode=<Str>      select frames containing opcode
    --header            show header information
    --decomp            de-compile file / selected frames
    --hexdump           show hexdump of selected frames
    --verbose           be verbose when possible
=end code

Produces various types of information about the given bytecode file,
specified by either a C<use> target, setting letter or path.

=head2 csites

=begin code
$ csites c 12
 12 $, $, N
=end code

Helper code to show the callsite info of the given callsite number.

=head2 opinfo

=begin code
$ opinfo if_i unless_i
 24 if_i r(int64),ins (8 bytes)
 25 unless_i r(int64),ins (8 bytes)

$ opinfo 42 666
 42 bindlex_nn str,r(num64) (8 bytes)
666 atpos2d_s w(str),r(obj),r(int64),r(int64) (10 bytes)
=end code

Helper script to show the gist of the given op name(s) or number(s).

=head2 sheap

=begin code
$ sheap e 3 4 5
    3 SETTING::src/core.e/core_prologue.rakumod
    4 language_revision_type
    5 lang-meth-call

$ sheap e byte
   42 byte
 2844 bytecode-size
=end code

Helper script for browsing the string heap of a given bytecode file
(specified by either a C<use> target, setting letter, or a path of a
bytecode file).

String arguments are interpreted as a key to do a .grep on the whole
string heap.  Numerical arguments are interpreted as indices into the
string heap.  If no arguments are given, the entire string heap will
be produced.

Shown are the string index and the string.

=head1 INSTANCE METHODS

=head2 coverables

=begin code :lang<raku>
# Show all files and number of lines in that file
say .key ~ ": " ~ +.value ~ "  lines"
  for $M.coverables.sort(*.key);
=end code

Returns a C<Map> with the filename(s) as keys, and a list of line
numbers in that file that have code in them that C<can> be produced
in a coverage report.

=head2 callsites

=begin code :lang<raku>
.say for $M.callsites[^10];  # show the first 10 callsites
=end code

Returns a list of L<Callsite|#Callsite> objects, which contains
information about the arguments at a given callsite.

=head2 de-compile

Returns a string with the opcodes and their arguments.

=head2 extension-ops

=begin code :lang<raku>
.say for $M.extension-ops;  # show all extension ops
=end code

Returns a list of NQP extension operators that have been added to this
bytecode.  Each element consists of an L<ExtensionOp> object.

=head2 frames

=begin code :lang<raku>
.say for $M.frames[^10];  # show the first 10 frames on the frame heap

my @frames := $M.frames.reify-all;
=end code

Returns a L<Frames> object that serves as a C<Positional> for all of the
frames on the frame heap.  Since the reification of a L<Frame|#Frame>
object is rather expensive, this is done lazily on each access.

To reify all C<Frame> objects at once, one can call the C<reify-all>
method, which also returns a list of the reified C<Frame> objects.

=head2 hll-name

=begin code :lang<raku>
say $M.hll-name;     # most likely "Raku"
=end code

Returns the HLL language name for this bytecode.  Most likely "Raku", or
"nqp".

=head2 op

=begin code :lang<raku>
say $M.op(0x102);     #  102 istype       w(int64),r(obj),r(obj)

say $M.op("istype");  #  102 istype       w(int64),r(obj),r(obj) 
=end code

Attempt to create an opcode object for the given name or opcode number.
Also includes any extension ops that may be defined in the bytecode
itself.

=head2 opcodes

A C<Buf> with the actual opcodes.

=head2 sc-dependencies

=begin code :lang<raku>
.say for $M.sc-dependencies;  # identifiers for Serialization Context
=end code

Returns a list of strings of the Serialization Contexts on which this
bytecode depends.

=head2 strings

=begin code :lang<raku>
.say for $M.strings[^10];  # The first 10 strings on the string heap
=end code

Returns a L<Strings> object that serves as a C<Positional> for all of the
strings on the string heap.

=head2 version

=begin code :lang<raku>
say $M.version;     # most likely 7
=end code

Returns the numeric version of this bytecode.  Most likely "7".

=head1 PRIMITIVES

=head2 bytecode

=begin code :lang<raku>
my $b = $M.bytecode;
=end code

Returns the C<Buf> with the bytecode.

=head2 hexdump

=begin code :lang<raku>
say $M.hexdump($M.string-heap-offset);  # defaults to 256

say $M.hexdump($M.string-heap-offset, 1024);
=end code

Returns a hexdump representation of the bytecode from the given byte
offset for the given number of bytes (256 by default).

=head2 slice

=begin code :lang<raku>
dd $M.slice(0, 8).chrs;     # "MOARVM\r\n"
=end code

Returns a C<List> of unsigned 32-bit integers from the given offset and
number of bytes.  Basically a shortcut for
C<$M,bytecode[$offset ..^ $offset + $bytes]>.  The number of bytes
defaults to C<256> if not specified.

=head2 str

=begin code :lang<raku>
say $M.str(76);  # Raku or nqp
=end code

Returns the string of which the index is the given offset.

=head2 subbuf

=begin code :lang<raku>
dd $M.subbuf(0, 8).decode;  # "MOARVM\r\n"
=end code

Calls C<subbuf> on the C<bytecode> and returns the result.  Basically a
shortcut for C<$M.bytecode.subbuf(...)>.

=head2 uint16

=begin code :lang<raku>
my $i = $M.uint16($offset);
=end code

Returns the unsigned 16-bit integer value at the given offset in the
bytecode.

=head2 uint16s

=begin code :lang<raku>
my @values := = $M.uint16s($M.string-heap-offset);  # 16 entries

my @values := $M.uint16s($M.string-heap-offset, $entries);
=end code

Returns an unsigned 16-bit integer array for the given number of
entries at the given offset in the bytecode.  The number of entries
defaults to 16 if not specified.

=head2 uint32

=begin code :lang<raku>
my $i = $M.uint32($offset);
=end code

Returns the unsigned 32-bit integer value at the given offset in the
bytecode.

=head2 uint32s

=begin code :lang<raku>
my @values := = $M.uint32s($offset);  # 16 entries

my @values := $M.uint32s($offset, $entries);
=end code

Returns an unsigned 32-bit integer array for the given number of
entries at the given offset in the bytecode.  The number of entries
defaults to 16 if not specified.

=head1 HEADER SHORTCUTS

The following methods provide shortcuts to the values in the bytecode header.
They are explained in the
L<MoarVM documentation|https://github.com/MoarVM/MoarVM/blob/main/docs/bytecode.markdown#bytecode>.

C<sc-dependencies-offset>, C<sc-dependencies-entries>,
C<extension-ops-offset>,   C<extension-ops-entries>,
C<frames-data-offset>,     C<frames-data-entries>,
C<callsites-data-offset>,  C<callsites-data-entries>,
C<string-heap-offset>,     C<string-heap-entries>,
C<sc-data-offset>,         C<sc-data-length>,
C<opcodes-offset>,         C<opcodes-length>,
C<annotation-data-offset>, C<annotation-data-length>,
C<main-entry-frame-index>,
C<library-load-frame-index>,
C<deserialization-frame-index>

=head1 SUBCLASSES

Instances of these classes are usually created automatically.

=head2 Argument

The C<Argument> class provides these methods:

=head3 flags

The raw 8-bit bitmap of flags.  The following bits have been defined:

=item   1 - object
=item   2 - native integer, signed
=item   4 - native floating point number
=item   8 - native NFG string (MVMString REPR)
=item  16 - literal
=item  32 - named argument
=item  64 - flattened argument
=item 128 - native integer, unsigned

=head3 is-flattened

Returns 1 if the argument is flattened, else 0.

=head3 is-literal

Returns 1 if the argument is a literal value, else 0.

=head3 name

The name of the argument if it is a named argument, else the empty string.

=head3 type

The type of the argument: possible values are C<Mu> (indicating a HLL
object of some kind), or any of the basic native types: C<str>, C<int>,
C<uint> or C<num>.

=head2 Callsite

The C<Callsite> class provides these methods:

=head3 arguments

The list of L<Argument> objects for this callsite, if any.

=head3 bytes

The number of bytes this callsite needs.

=head3 has-named-arg

Returns C<True> if the call site has a named argument with the
given name, else C<False>.

=head3 named

A C<Map> of named arguments, keyed by name.

=head2 ExtensionOp

The C<ExtensionOp> class provides these methods:

=head3 adverbs

Always an empty C<Map>.

=head3 annotation

Always the empty string.

=head3 bytes

The number of bytes this opcode uses.

=head3 name

The name with which the extension op can be called.

=head3 descriptor

An 8-byte C<Buf> with descriptor information.

=head3 is-sequence

Always C<False>.

=head2 Frame

The C<Frame> class provides these methods:

=head3 is-inlineable

Return C<Bool> whether the current frame is considered to be inlineable.

=head3 cuid

A string representing the compilation unit ID.

=head3 de-compile

Returns a string with the opcodes and their arguments of this frame.

=head3 flags

A 16-bit unsigned integer bitmap with flags of this frame.

=head3 handlers

A list of L<Handler|#Handler> objects, representing the handlers in this frame.

=head3 has-exit-handler

1 if this frame has an exit handler, otherwise 0.

=head3 hexdump

Return a hexdump of the opcodes of this frame.  Optionally takes a
named argument C<:highlight> which will highlight the bytes of the
actual opcodes (excluding any argument bytes following them).

=head3 index

A 16-bit unsigned integer indicating the frame index of this frame.

=head3 is-thunk

1 if this frame is a thunk (as opposed to a real scope), otherwise 0.

=head3 lexicals

A list of L<Lexical|#Lexical> objects, representing the lexicals in this frame.

=head3 locals

A list of L<Local|#Local> objects, representing the locals in this frame.

=head3 name

The name of this frame, if any.

=head3 no-outer

1 if this frame has no outer, otherwise 0.

=head3 opcodes

A C<Buf> with the actual bytecode of this frame.

=head3 outer-index

A 16-bit unsigned integer indicating the frame index of the outer frame.

=head3 sc-dependency-index

A 32-bit unsigned integer index into

=head3 sc-object-index

A 32-bit unsigned integer index into

=head3 statements

A list of L<Statement|#Statement> objects for this frame, may be empty.

=head2 Handler

=head3 start-protected-region
=head3 end-protected-region
=head3 category-mask
=head3 action
=head3 register-with-block
=head3 handler-goto

=head2 Lexical

=head3 name

The name of this lexical, if any.

=head3 type

The type of this lexical.

=head3 flags

A 16-bit unsigned integer bitmap for this lexical.

=head3 sc-dependency-index

Index of into the C<sc-dependencies> list.

=head3 sc-object-index

Index of into the C<sc-dependencies> list.

=head2 Local

=head3 name

The name of this local, if any.

=head3 type

The type of this local.

=head2 Statement

=head3 line

The line number of this statement.

=head3 offset

The opcode offset of this statement.

=head2 Op

=head3 all-adverbs

Return a C<List> of all possible adverbs.

=head3 all-ops

Return a C<List> of all possible ops.

=head3 annotation

The annotation of this operation.  Currently recognized annotations
are:

=item dispatch
=item jump
=item parameter
=item return
=item spesh

Absence of annotation if indicated by the empty string.  See also
L<is-sequence|#is-sequence>.

=head3 adverbs

A C<Map> of additional adverb strings.

=head3 bytes

=begin code :lang<raku>
my $bytes := $op.bytes($frame, $offset);
=end code

The number of bytes this op occupies in memory.  Returns B<0> if the
op has a variable size.

Some ops have a variable size depending on the callsite in the frame
it is residing.  For those cases, one can call the C<bytes> method
with the L<Frame|#Frame> object and the offset in the opcodes of
that frame to obtain the number of bytes for that instance.

=head3 index

The numerical index of this operation.

=head3 is-sequence

True if this op is the start of a sequence of ops that share the
same annotation.

=head3 name

The name of this operation.

=head3 new

=begin code :lang<raku>
my $op = MoarVM::Op.new(0);

my $op = MoarVM::Op.new("no_op");
=end code

Return an instantiated C<MoarVM::Op> object from the given name or
opcode number.

=head3 not-inlineable

Returns C<True> if the op causes the frame to which it belongs
to be not inlineable.  Otherwise returns C<False>.

=head3 operands

A C<List> of operands, if any.

=head1 AUTHOR

Elizabeth Mattijsen <liz@raku.rocks>

Source can be located at: https://github.com/lizmat/MoarVM-Bytecode .
Comments and Pull Requests are welcome.

If you like this module, or what I’m doing more generally, committing to a
L<small sponsorship|https://github.com/sponsors/lizmat/>  would mean a great
deal to me!

=head1 COPYRIGHT AND LICENSE

Copyright 2024, 2025, 2026 Elizabeth Mattijsen

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.

=end pod

# vim: expandtab shiftwidth=4
