The format of the Java bytecode instructions is clearly specified in th
e VM Spec document, 2nd edition (authored by Tim Lindholm, a name we're all
familiar with by now!)
If you are writing a Java VM, this document is your
Bible--your VM must implement the semantics described in this document exactly,
or it won't be 100% compatible with Sun's Java. This document is from Java
version 2, but JDK 1.0 had the same structure (and very little has changed since
then, and any changes have to be 100% backward compatible anyway--the format of
the bytecodes described below will never be changed, for example). Anyway, if
we study this document, it will be clear that the Java bytecode instructions
definitely don't contain any symbolic references: they only refer to
symbolic references, which are stored separately, in the constant pool.
Section 4 of this document describes the constant pool. A bytecode
instruction that accesses a field, for example, would have a numeric index
referring to an entry in the constant pool, and then that constant pool entry
would have CONSTANT_Fieldref_info data in it (tag, class_index and
name_and_type_index). The tag byte would have the value 9 (CONSTANT_Fieldref)
and the class_index would be another numeric index pointing to another constant
pool entry (this one would contain info about the class that contains the field
we are resolving) and the name_and_type_index would contain *yet another*
numeric index pointing to yet another constant pool entry (and this one would
contain the name and type of the specific field being resolved). That will be
less confusing in a minute.
So the first time the VM executes this bytecode
instruction (or any similar bytecode instruction with the same numeric index in
it, i.e. the index pointing to this CONSTANT_Fieldref_info entry in the constant
pool), it triggers the process that dynamically resolves the symbols. This process is described in section 5.4.3, Resolution -- for this
specific case, it would be resolving a reference to a Field, so it would do the
stuff described in 5.4.3.2 Field Resolution. The resolution process for
constant pool entries of a certain type, is the same regardless of which
instruction triggered the resolution (i.e. its the constant pool entry that is
being "resolved", not the instruction. The instruction just needs the resolved
result in order to do its thing.)
Part of section 5.4.3 Resolution
reads:
The Java virtual machine instructions anewarray, checkcast,
getfield, getstatic, instanceof, invokeinterface, invokespecial, invokestatic,
invokevirtual, multianewarray, new, putfield, and putstatic make symbolic
references to the runtime constant pool. Execution of any of these
instructions requires resolution of its symbolic reference.
The
following sections describe the process of resolving a symbolic reference in
the runtime constant pool (§5.1) of a class or interface D. Details of
resolution differ with the kind of symbolic reference to be resolved.
(emphasis mine)
The first paragraph from this quote seems
imprecise. It says the bytecodes "make symbolic references" to the runtime
constant pool. But in fact what happens is that these bytecodes contain a
numeric index into the constant pool. So they only "make reference" to
it in the sense that they contain this numeric index, which is certainly NOT a
symbolic reference.
The second paragraph is more accurate but slightly
ambiguous -- one possible interpretation is that it means that the symbolic
reference being resolved is "in the runtime constant pool", and the other
possible interpretation is that it means that "the process of resolving a
symbolic reference" takes place "in the runtime constant pool". As it happens,
these interpretations are both true. It does not matter which bytecode
instruction is using the constant pool entry that ends up trigger resolution of
that constant pool entry; the constant pool entries can be shared by multiple
bytecode instructions, and the VM resolves the symbolic reference in a constant
pool entry the same way regardless of how it got there.
Some examples of the
Java bytecode format are found in the document in
section 7, Compiling for the Java Virtual Machine. Section 7.4 is specifically about accessing the runtime constant
pool.
The bytecodes described above as "mak[ing] symbolic references to the
runtime constant pool" all have their exact format specified in this document,
in Chapter 6. I'll link them all here:
anewarray - Creates a new array
checkcast - Check whether object is of given type
getfield - Fetch field from object
getstatic - Get static field from class
instanceof - Determine if object is of given type
invokeinterface - Invoke interface method
invokespecial - Invoke instance method (special handling
for superclass, private, clinit, etc)
invokestatic - Invoke a static method
invokevirtual - Invoke instance method; dispatch based on
class
multianewarray - Create new multidimensional array
new - Create new object
putfield - Set field in object
putstatic - Set static field in class
If you follow
any of those links, you'll see a description of the exact bytes that make up the
bytecode instruction. All of these instructions have two bytes labeled
"indexbyte1" and "indexbyte2". Together, those two bytes make up a 16-bit
numeric index into the constant pool. The constant pool entry is what contains
the symbolic reference (in one of the formats such as the CONSTANT_Fieldref_info
format). None of these bytecodes ever contains that symbolic information, they
just refer to it using a numeric index (i.e. NOT a "symbolic
reference", but just an ordinary numeric index). These bytecodes all "use a
symbolic reference", but the symbolic reference is in the constant pool.
If
you read the description of the individual bytecodes, they usually make this
clear too. For example, here is the first paragraph of the description of putstatic:
The unsigned indexbyte1 and indexbyte2
are used to construct an index into the runtime constant pool of the
current class (§3.6), where the value of the index is (indexbyte1 << 8) |
indexbyte2. The runtime constant pool item at that index must be a symbolic
reference to a field (§5.1), which gives the name and descriptor of the
field as well as a symbolic reference to the class or interface in which the
field is to be found. The referenced field is resolved
(§5.4.3.2).
(emphasis mine).
Where it says "The referenced
field is resolved", we can take that to mean "the field resolution process in
section 5.4.3.2 must be carried out before this putstatic bytecode can do what
it does" (which is specified later in the description). [ Reply to This | Parent | # ]
|