Podejdźmy do tego od innej strony: jaki program nie generuje kodu maszynowego?
Jak ktoś jest ciekaw czy rzeczywiście interpreter bajtkodu z HotSpota (OpenJDK/ OracleJDK) jest analogiczny do przedstawionego wcześniej interpretera Brainfucka to zapraszam do objerzenia: http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/9ce27f0a4683/src/share/vm/interpreter/bytecodeInterpreter.cpp Jest w nim analogicznie wczytywanie bajtkodu jeden po drugim i wykonywanie odpowiedniego case z wielkiego switcha (ten interpreter Brainfucka co podałem używa drabinki ifów, ale da się trywialnie zamienić na switcha):
#ifndef USELABELS
while (1)
#endif
{
#ifndef PREFETCH_OPCCODE
opcode = *pc; // tutaj wczytujemy kolejny kawałek bajtkodu
#endif
// Seems like this happens twice per opcode. At worst this is only
// need at entry to the loop.
// DEBUGGER_SINGLE_STEP_NOTIFY();
/* Using this labels avoids double breakpoints when quickening and
* when returing from transition frames.
*/
opcode_switch:
assert(istate == orig, "Corrupted istate");
/* QQQ Hmm this has knowledge of direction, ought to be a stack method */
assert(topOfStack >= istate->stack_limit(), "Stack overrun");
assert(topOfStack < istate->stack_base(), "Stack underrun");
#ifdef USELABELS
DISPATCH(opcode);
#else
switch (opcode) // tutaj odpalamy jebutnego switcha, który implementuje semantykę poszczególnych instrukcji bajtkodu
#endif
{
CASE(_nop):
UPDATE_PC_AND_CONTINUE(1);
/* Push miscellaneous constants onto the stack. */
CASE(_aconst_null):
SET_STACK_OBJECT(NULL, 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);
#undef OPC_CONST_n
#define OPC_CONST_n(opcode, const_type, value) \
CASE(opcode): \
SET_STACK_ ## const_type(value, 0); \
UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);
OPC_CONST_n(_iconst_m1, INT, -1);
OPC_CONST_n(_iconst_0, INT, 0);
OPC_CONST_n(_iconst_1, INT, 1);
OPC_CONST_n(_iconst_2, INT, 2);
OPC_CONST_n(_iconst_3, INT, 3);
OPC_CONST_n(_iconst_4, INT, 4);
OPC_CONST_n(_iconst_5, INT, 5);
OPC_CONST_n(_fconst_0, FLOAT, 0.0);
OPC_CONST_n(_fconst_1, FLOAT, 1.0);
OPC_CONST_n(_fconst_2, FLOAT, 2.0);
#undef OPC_CONST2_n
#define OPC_CONST2_n(opcname, value, key, kind) \
CASE(_##opcname): \
{ \
SET_STACK_ ## kind(VM##key##Const##value(), 1); \
UPDATE_PC_AND_TOS_AND_CONTINUE(1, 2); \
}
OPC_CONST2_n(dconst_0, Zero, double, DOUBLE);
OPC_CONST2_n(dconst_1, One, double, DOUBLE);
OPC_CONST2_n(lconst_0, Zero, long, LONG);
OPC_CONST2_n(lconst_1, One, long, LONG);
/* Load constant from constant pool: */
/* Push a 1-byte signed integer value onto the stack. */
CASE(_bipush):
SET_STACK_INT((jbyte)(pc[1]), 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1);
/* Push a 2-byte signed integer constant onto the stack. */
CASE(_sipush):
SET_STACK_INT((int16_t)Bytes::get_Java_u2(pc + 1), 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 1);
/* load from local variable */
CASE(_aload):
VERIFY_OOP(LOCALS_OBJECT(pc[1]));
SET_STACK_OBJECT(LOCALS_OBJECT(pc[1]), 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1);
CASE(_iload):
CASE(_fload):
SET_STACK_SLOT(LOCALS_SLOT(pc[1]), 0);
UPDATE_PC_AND_TOS_AND_CONTINUE(2, 1);
CASE(_lload):
SET_STACK_LONG_FROM_ADDR(LOCALS_LONG_AT(pc[1]), 1);
UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2);
CASE(_dload):
SET_STACK_DOUBLE_FROM_ADDR(LOCALS_DOUBLE_AT(pc[1]), 1);
UPDATE_PC_AND_TOS_AND_CONTINUE(2, 2);