On Tue, 2009-04-14 at 23:05 +0200, Tomek Grabiec wrote:
> Signed-off-by: Tomek Grabiec <[email protected]>
Applied, thanks! Please cc the mailing list for future patches.
> Makefile | 6 +-
> arch/x86/emit-code_32.c | 95
> +++++++++++++++++++++++-
> arch/x86/include/arch/emit-code.h | 3 +-
> arch/x86/include/arch/memory.h | 11 +++
> arch/x86/insn-selector_32.brg | 26 ++++++-
> include/jit/compiler.h | 26 ++++++-
> include/vm/method.h | 5 +
> jamvm/class.c | 2 +-
> jit/emit.c | 3 +
> jit/fixup-site.c | 57 ++++++++++++++
> jit/trace-jit.c | 11 +++
> jit/trampoline.c | 5 +-
> regression/jvm/TrampolineBackpatchingTest.java | 62 +++++++++++++++
> regression/run-suite.sh | 1 +
> test/arch-x86/Makefile | 1 +
> test/arch-x86/insn-selector-test_32.c | 8 +-
> vm/jato.c | 6 +-
> 17 files changed, 313 insertions(+), 15 deletions(-)
> create mode 100644 arch/x86/include/arch/memory.h
> create mode 100644 jit/fixup-site.c
> create mode 100644 regression/jvm/TrampolineBackpatchingTest.java
>
> diff --git a/Makefile b/Makefile
> index 84adf3a..9d4445e 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -64,7 +64,8 @@ JIT_OBJS = \
> jit/trampoline.o \
> jit/tree-printer.o \
> jit/typeconv-bc.o \
> - jit/vtable.o
> + jit/vtable.o \
> + jit/fixup-site.o
>
> VM_OBJS = \
> vm/bitset.o \
> @@ -195,7 +196,8 @@ REGRESSION_TEST_SUITE_CLASSES = \
> regression/jamvm/SynchronizationTest.class \
> regression/jamvm/MethodInvocationAndReturnTest.class \
> regression/jamvm/ConversionTest.class \
> - regression/jvm/PutstaticTest.class
> + regression/jvm/PutstaticTest.class \
> + regression/jvm/TrampolineBackpatchingTest.class \
>
> lib: $(CLASSPATH_CONFIG)
> make -C lib/ JAVAC=$(JAVAC) GLIBJ=$(GLIBJ)
> diff --git a/arch/x86/emit-code_32.c b/arch/x86/emit-code_32.c
> index e3f6230..a838b05 100644
> --- a/arch/x86/emit-code_32.c
> +++ b/arch/x86/emit-code_32.c
> @@ -9,17 +9,23 @@
>
> #include <jit/basic-block.h>
> #include <jit/statement.h>
> +#include <jit/compilation-unit.h>
> +#include <jit/compiler.h>
>
> +#include <vm/list.h>
> #include <vm/buffer.h>
> +#include <vm/method.h>
>
> #include <arch/emit-code.h>
> #include <arch/instruction.h>
> +#include <arch/memory.h>
>
> #include <assert.h>
> #include <errno.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <stdbool.h>
> +#include <string.h>
>
> /*
> * __encode_reg: Encode register to be used in IA-32 instruction.
> @@ -214,6 +220,17 @@ static void __emit_push_reg(struct buffer *buf, enum
> machine_reg reg)
> emit(buf, 0x50 + __encode_reg(reg));
> }
>
> +static void __emit_push_membase(struct buffer *buf, enum machine_reg src_reg,
> + unsigned long disp)
> +{
> + __emit_membase_reg(buf, 0xff, src_reg, disp, REG_ESI);
> +}
> +
> +static void __emit_pop_reg(struct buffer *buf, enum machine_reg reg)
> +{
> + emit(buf, 0x58 + __encode_reg(reg));
> +}
> +
> static void emit_push_reg(struct buffer *buf, struct operand *operand)
> {
> __emit_push_reg(buf, mach_reg(&operand->reg));
> @@ -870,11 +887,85 @@ void emit_body(struct basic_block *bb, struct buffer
> *buf)
> bb->is_emitted = true;
> }
>
> -void emit_trampoline(struct compilation_unit *cu, void *call_target,
> - struct buffer *buf)
> +/*
> + * This fixes relative calls generated by EXPR_INVOKE.
> + *
> + * Please note, that this code does not care about icache flushing in
> + * MP environment. This may lead to a GPF when one CPU modifies code
> + * already prefetched by another CPU on some bogus Intel CPUs (see
> + * section 7.1.3 of "Intel 64 and IA-32 Architectures Software
> + * Developers Manual Volume 3A"). It is required for other CPUs to
> + * execute a serializing instruction (to flush instruction cache)
> + * between modification and execution of new instruction. To achieve
> + * this, we could suspend all threads before patching, and force them
> + * to execute flush_icache() on resume.
> + */
> +static void fixup_invoke(struct jit_trampoline *t, unsigned long target)
> {
> + struct fixup_site *this, *next;
> +
> + pthread_mutex_lock(&t->mutex);
> +
> + list_for_each_entry_safe(this, next, &t->fixup_site_list,
> + fixup_list_node) {
> + unsigned char *site_addr;
> + uint32_t new_target;
> +
> + site_addr = fixup_site_addr(this);
> + new_target = target - ((uint32_t)site_addr + CALL_INSN_SIZE);
> + cpu_write_u32(site_addr+1, new_target);
> +
> + list_del(&this->fixup_list_node);
> + free_fixup_site(this);
> + }
> +
> + pthread_mutex_unlock(&t->mutex);
> +}
> +
> +/*
> + * This function replaces pointers in vtable so that they point
> + * directly to compiled code instead of trampoline code.
> + */
> +static void fixup_invokevirtual(struct compilation_unit *cu,
> + struct object *objref,
> + void *target)
> +{
> + struct classblock *cb = CLASS_CB(objref->class);
> +
> + cb->vtable.native_ptr[cu->method->method_table_index] = target;
> +}
> +
> +void emit_trampoline(struct compilation_unit *cu,
> + void *call_target,
> + struct jit_trampoline *trampoline)
> +{
> + struct buffer *buf = trampoline->objcode;
> +
> + /* This is for __builtin_return_address() to work and to access
> + call arguments in correct manner. */
> + __emit_push_reg(buf, REG_EBP);
> + __emit_mov_reg_reg(buf, REG_ESP, REG_EBP);
> +
> __emit_push_imm(buf, (unsigned long)cu);
> __emit_call(buf, call_target);
> +
> + __emit_push_reg(buf, REG_EAX);
> +
> + if ((cu->method->access_flags & ACC_STATIC) ||
> + method_is_constructor(cu->method)) {
> + __emit_push_imm(buf, (unsigned long)trampoline);
> + __emit_call(buf, fixup_invoke);
> + __emit_add_imm_reg(buf, 0x4, REG_ESP);
> + } else {
> + __emit_push_membase(buf, REG_EBP, 0x08);
> + __emit_push_imm(buf, (unsigned long)cu);
> + __emit_call(buf, fixup_invokevirtual);
> + __emit_add_imm_reg(buf, 0x08, REG_ESP);
> + }
> +
> + __emit_pop_reg(buf, REG_EAX);
> +
> __emit_add_imm_reg(buf, 0x04, REG_ESP);
> + __emit_pop_reg(buf, REG_EBP);
> emit_indirect_jump_reg(buf, REG_EAX);
> }
> diff --git a/arch/x86/include/arch/emit-code.h
> b/arch/x86/include/arch/emit-code.h
> index c7ebb43..52d1ef4 100644
> --- a/arch/x86/include/arch/emit-code.h
> +++ b/arch/x86/include/arch/emit-code.h
> @@ -1,6 +1,7 @@
> #ifndef __X86_EMIT_CODE_H
> #define __X86_EMIT_CODE_H
>
> +struct jit_trampoline;
> struct compilation_unit;
> struct basic_block;
> struct buffer;
> @@ -8,6 +9,6 @@ struct buffer;
> void emit_prolog(struct buffer *, unsigned long);
> void emit_epilog(struct buffer *, unsigned long);
> void emit_body(struct basic_block *, struct buffer *);
> -void emit_trampoline(struct compilation_unit *, void *, struct buffer *);
> +void emit_trampoline(struct compilation_unit *, void *, struct
> jit_trampoline*);
>
> #endif /* __X86_EMIT_CODE */
> diff --git a/arch/x86/include/arch/memory.h b/arch/x86/include/arch/memory.h
> new file mode 100644
> index 0000000..e27c31b
> --- /dev/null
> +++ b/arch/x86/include/arch/memory.h
> @@ -0,0 +1,11 @@
> +#ifndef _MEMORY_H_
> +#define _MEMORY_H_
> +
> +#include <stdint.h>
> +
> +static inline void cpu_write_u32(unsigned char *p, uint32_t val)
> +{
> + *((uint32_t*)p) = val;
> +}
> +
> +#endif
> diff --git a/arch/x86/insn-selector_32.brg b/arch/x86/insn-selector_32.brg
> index c11ec6a..c31cd99 100644
> --- a/arch/x86/insn-selector_32.brg
> +++ b/arch/x86/insn-selector_32.brg
> @@ -361,10 +361,21 @@ reg: EXPR_INVOKE(arg) 1
> struct expression *expr;
> struct var_info *var;
> void *target;
> + struct insn *call_insn;
> + bool is_compiled;
>
> expr = to_expr(tree);
> method = expr->target_method;
> - target = trampoline_ptr(method);
> +
> + pthread_mutex_lock(&method->compilation_unit->mutex);
> + is_compiled = method->compilation_unit->is_compiled;
> +
> + if (is_compiled)
> + target = method_native_ptr(method);
> + else
> + target = method_trampoline_ptr(method);
> +
> + pthread_mutex_unlock(&method->compilation_unit->mutex);
>
> var = get_fixed_var(s->b_parent, REG_EAX);
> state->reg1 = var;
> @@ -374,7 +385,18 @@ reg: EXPR_INVOKE(arg) 1
> state->reg2 = var;
> }
>
> - bb_add_insn(s, rel_insn(INSN_CALL_REL, (unsigned long) target));
> + call_insn = rel_insn(INSN_CALL_REL, (unsigned long) target);
> + bb_add_insn(s, call_insn);
> +
> + if (!is_compiled) {
> + struct fixup_site *fixup;
> +
> + fixup = alloc_fixup_site();
> + fixup->cu = s->b_parent;
> + fixup->relcall_insn = call_insn;
> +
> + trampoline_add_fixup_site(method->trampoline, fixup);
> + }
>
> if (method->args_count)
> method_args_cleanup(s, method->args_count);
> diff --git a/include/jit/compiler.h b/include/jit/compiler.h
> index 89f2f29..8fadc29 100644
> --- a/include/jit/compiler.h
> +++ b/include/jit/compiler.h
> @@ -6,12 +6,25 @@
> #include <vm/buffer.h>
> #include <vm/stack.h>
> #include <vm/vm.h>
> +#include <pthread.h>
>
> struct buffer;
> struct compilation_unit;
>
> +struct fixup_site {
> + /* Compilation unit to which relcall_insn belongs */
> + struct compilation_unit *cu;
> + /* We need this, because we don't have native pointer at
> + instruction selection */
> + struct insn *relcall_insn;
> + struct list_head fixup_list_node;
> +};
> +
> struct jit_trampoline {
> struct buffer *objcode;
> + struct list_head fixup_site_list;
> + /* This mutex is protecting operations on fixup_site_list */
> + pthread_mutex_t mutex;
> };
>
> struct parse_context {
> @@ -41,7 +54,17 @@ void free_jit_trampoline(struct jit_trampoline *);
>
> int jit_prepare_method(struct methodblock *);
>
> -static inline void *trampoline_ptr(struct methodblock *method)
> +struct fixup_site *alloc_fixup_site(void);
> +void free_fixup_site(struct fixup_site *);
> +void trampoline_add_fixup_site(struct jit_trampoline *, struct fixup_site *);
> +unsigned char *fixup_site_addr(struct fixup_site *);
> +
> +static inline void *method_native_ptr(struct methodblock *method)
> +{
> + return buffer_ptr(method->compilation_unit->objcode);
> +}
> +
> +static inline void *method_trampoline_ptr(struct methodblock *method)
> {
> return buffer_ptr(method->trampoline->objcode);
> }
> @@ -52,6 +75,7 @@ extern bool opt_trace_tree_ir;
> extern bool opt_trace_liveness;
> extern bool opt_trace_regalloc;
> extern bool opt_trace_machine_code;
> +extern bool opt_trace_magic_trampoline;
>
> void trace_method(struct compilation_unit *);
> void trace_cfg(struct compilation_unit *);
> diff --git a/include/vm/method.h b/include/vm/method.h
> index 6c0af65..142a8c9 100644
> --- a/include/vm/method.h
> +++ b/include/vm/method.h
> @@ -12,4 +12,9 @@ static inline enum vm_type method_return_type(struct
> methodblock *method)
> return str_to_type(return_type);
> }
>
> +static inline bool method_is_constructor(struct methodblock *method)
> +{
> + return strcmp(method->name,"<init>") == 0;
> +}
> +
> #endif
> diff --git a/jamvm/class.c b/jamvm/class.c
> index 1e09908..b546f5a 100644
> --- a/jamvm/class.c
> +++ b/jamvm/class.c
> @@ -984,7 +984,7 @@ void linkClass(Class *class) {
> vtable_init(&cb->vtable, method_table_size);
> for (i = 0; i < method_table_size; i++) {
> mb = method_table[i];
> - vtable_setup_method(&cb->vtable, mb->method_table_index,
> trampoline_ptr(mb));
> + vtable_setup_method(&cb->vtable, mb->method_table_index,
> method_trampoline_ptr(mb));
> }
> }
>
> diff --git a/jit/emit.c b/jit/emit.c
> index 0e7c990..1c8af96 100644
> --- a/jit/emit.c
> +++ b/jit/emit.c
> @@ -61,6 +61,9 @@ struct jit_trampoline *alloc_jit_trampoline(void)
> if (!trampoline->objcode)
> goto failed;
>
> + INIT_LIST_HEAD(&trampoline->fixup_site_list);
> + pthread_mutex_init(&trampoline->mutex, NULL);
> +
> return trampoline;
>
> failed:
> diff --git a/jit/fixup-site.c b/jit/fixup-site.c
> new file mode 100644
> index 0000000..9a63f28
> --- /dev/null
> +++ b/jit/fixup-site.c
> @@ -0,0 +1,57 @@
> +/*
> + * Copyright (c) 2009 Tomasz Grabiec
> + *
> + * This file is released under the GPL version 2 with the following
> + * clarification and special exception:
> + *
> + * Linking this library statically or dynamically with other modules is
> + * making a combined work based on this library. Thus, the terms and
> + * conditions of the GNU General Public License cover the whole
> + * combination.
> + *
> + * As a special exception, the copyright holders of this library give you
> + * permission to link this library with independent modules to produce an
> + * executable, regardless of the license terms of these independent
> + * modules, and to copy and distribute the resulting executable under
> terms
> + * of your choice, provided that you also meet, for each linked
> independent
> + * module, the terms and conditions of the license of that module. An
> + * independent module is a module which is not derived from or based on
> + * this library. If you modify this library, you may extend this
> exception
> + * to your version of the library, but you are not obligated to do so. If
> + * you do not wish to do so, delete this exception statement from your
> + * version.
> + *
> + * Please refer to the file LICENSE for details.
> + */
> +
> +#include <jit/compiler.h>
> +#include <memory.h>
> +#include <malloc.h>
> +
> +struct fixup_site *alloc_fixup_site(void)
> +{
> + struct fixup_site *site;
> +
> + site = malloc(sizeof(*site));
> + memset(site, 0, sizeof(*site));
> +
> + return site;
> +}
> +
> +void free_fixup_site(struct fixup_site *site)
> +{
> + free(site);
> +}
> +
> +void trampoline_add_fixup_site(struct jit_trampoline *trampoline,
> + struct fixup_site *site)
> +{
> + pthread_mutex_lock(&trampoline->mutex);
> + list_add_tail(&site->fixup_list_node, &trampoline->fixup_site_list);
> + pthread_mutex_unlock(&trampoline->mutex);
> +}
> +
> +unsigned char *fixup_site_addr(struct fixup_site *site)
> +{
> + return buffer_ptr(site->cu->objcode) + site->relcall_insn->mach_offset;
> +}
> diff --git a/jit/trace-jit.c b/jit/trace-jit.c
> index c678ffc..5bdd36e 100644
> --- a/jit/trace-jit.c
> +++ b/jit/trace-jit.c
> @@ -27,6 +27,7 @@ bool opt_trace_tree_ir;
> bool opt_trace_liveness;
> bool opt_trace_regalloc;
> bool opt_trace_machine_code;
> +bool opt_trace_magic_trampoline;
>
> void trace_method(struct compilation_unit *cu)
> {
> @@ -159,3 +160,13 @@ void trace_machine_code(struct compilation_unit *cu)
> disassemble(start, end);
> printf("\n");
> }
> +
> +void trace_magic_trampoline(struct compilation_unit *cu)
> +{
> + printf("jit_magic_trampoline: ret0=%p, ret1=%p: %s.%s #%d\n",
> + __builtin_return_address(1),
> + __builtin_return_address(2),
> + CLASS_CB(cu->method->class)->name,
> + cu->method->name,
> + cu->method->method_table_index);
> +}
> diff --git a/jit/trampoline.c b/jit/trampoline.c
> index 73c4438..cab6c09 100644
> --- a/jit/trampoline.c
> +++ b/jit/trampoline.c
> @@ -61,6 +61,9 @@ void *jit_magic_trampoline(struct compilation_unit *cu)
>
> pthread_mutex_lock(&cu->mutex);
>
> + if (opt_trace_magic_trampoline)
> + trace_magic_trampoline(cu);
> +
> if (cu->method->access_flags & ACC_NATIVE)
> ret = jit_native_trampoline(cu);
> else
> @@ -77,6 +80,6 @@ struct jit_trampoline *build_jit_trampoline(struct
> compilation_unit *cu)
>
> trampoline = alloc_jit_trampoline();
> if (trampoline)
> - emit_trampoline(cu, jit_magic_trampoline, trampoline->objcode);
> + emit_trampoline(cu, jit_magic_trampoline, trampoline);
> return trampoline;
> }
> diff --git a/regression/jvm/TrampolineBackpatchingTest.java
> b/regression/jvm/TrampolineBackpatchingTest.java
> new file mode 100644
> index 0000000..cc6aecb
> --- /dev/null
> +++ b/regression/jvm/TrampolineBackpatchingTest.java
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright (C) 2009 Tomasz Grabiec
> + *
> + * This file is released under the GPL version 2. Please refer to the file
> + * LICENSE for details.
> + */
> +package jvm;
> +
> +import jamvm.TestCase;
> +
> +/**
> + * @author Tomasz Grabiec
> + */
> +public class TrampolineBackpatchingTest extends TestCase {
> +
> + public static int test(int x) {
> + return x+x;
> + }
> +
> + public static int testBackpatchingOnTheFly(int x) {
> + /* No trampoline call should be generated for this,
> + because method test() is already compiled */
> + return test(x);
> + }
> +
> + static void testVTableBackpatching() {
> + /* Instantiating RuntimeException calls virtual method
> + Throwable.fillInStackTrace. This tests proper functioning
> + of vtable back-patching */
> + new RuntimeException("foo");
> + }
> +
> + public int testArg(int a) {
> + return a+1;
> + }
> +
> + public static void main(String [] args) {
> + int x;
> + TrampolineBackpatchingTest t = new TrampolineBackpatchingTest();
> +
> + /* Test backpatching of multiple call sites
> + at once */
> + x = 1;
> + x = test(x);
> + x = test(x);
> + x = test(x);
> + assertEquals(x, 8);
> +
> + x = testBackpatchingOnTheFly(8);
> + assertEquals(x, 16);
> +
> + testVTableBackpatching();
> +
> + /* Another invokevirtual backpatching test */
> + x = 1;
> + x = t.testArg(x);
> + x = t.testArg(x);
> + assertEquals(x, 3);
> +
> + Runtime.getRuntime().halt(retval);
> + }
> +}
> diff --git a/regression/run-suite.sh b/regression/run-suite.sh
> index 31a6740..af8b14d 100755
> --- a/regression/run-suite.sh
> +++ b/regression/run-suite.sh
> @@ -61,6 +61,7 @@ if [ -z "$CLASS_LIST" ]; then
> run_java jamvm.MethodInvocationAndReturnTest 0
> run_java jamvm.ControlTransferTest 0
> run_java jvm.PutstaticTest 0
> + run_java jvm.TrampolineBackpatchingTest 0
> else
> for i in $CLASS_LIST; do
> run_java $i 0
> diff --git a/test/arch-x86/Makefile b/test/arch-x86/Makefile
> index aba1a3b..11a30e3 100644
> --- a/test/arch-x86/Makefile
> +++ b/test/arch-x86/Makefile
> @@ -41,6 +41,7 @@ OBJS = \
> ../../jit/stack-slot.o \
> ../../jit/statement.o \
> ../../jit/tree-printer.o \
> + ../../jit/fixup-site.o \
> ../../arch/x86/emit-code$(ARCH_POSTFIX).o \
> ../../arch/x86/instruction.o \
> ../../arch/x86/insn-selector$(ARCH_POSTFIX).o \
> diff --git a/test/arch-x86/insn-selector-test_32.c
> b/test/arch-x86/insn-selector-test_32.c
> index fac248d..accb81f 100644
> --- a/test/arch-x86/insn-selector-test_32.c
> +++ b/test/arch-x86/insn-selector-test_32.c
> @@ -535,7 +535,7 @@ void test_select_invoke_without_arguments(void)
> select_instructions(bb->b_parent);
>
> insn = list_first_entry(&bb->insn_list, struct insn, insn_list_node);
> - assert_rel_insn(INSN_CALL_REL, (unsigned long) trampoline_ptr(&mb),
> insn);
> + assert_rel_insn(INSN_CALL_REL, (unsigned long)
> method_trampoline_ptr(&mb), insn);
>
> free_jit_trampoline(mb.trampoline);
> free_compilation_unit(mb.compilation_unit);
> @@ -575,7 +575,7 @@ void test_select_invoke_with_arguments(void)
> assert_imm_insn(INSN_PUSH_IMM, 0x01, insn);
>
> insn = list_next_entry(&insn->insn_list_node, struct insn,
> insn_list_node);
> - assert_rel_insn(INSN_CALL_REL, (unsigned long) trampoline_ptr(&mb),
> insn);
> + assert_rel_insn(INSN_CALL_REL, (unsigned long)
> method_trampoline_ptr(&mb), insn);
>
> insn = list_next_entry(&insn->insn_list_node, struct insn,
> insn_list_node);
> assert_imm_reg_insn(INSN_ADD_IMM_REG, 8, REG_ESP, insn);
> @@ -621,13 +621,13 @@ void
> test_select_method_return_value_passed_as_argument(void)
> select_instructions(bb->b_parent);
>
> insn = list_first_entry(&bb->insn_list, struct insn, insn_list_node);
> - assert_rel_insn(INSN_CALL_REL, (unsigned long)
> trampoline_ptr(&nested_mb), insn);
> + assert_rel_insn(INSN_CALL_REL, (unsigned long)
> method_trampoline_ptr(&nested_mb), insn);
>
> insn = list_next_entry(&insn->insn_list_node, struct insn,
> insn_list_node);
> assert_reg_insn(INSN_PUSH_REG, REG_EAX, insn);
>
> insn = list_next_entry(&insn->insn_list_node, struct insn,
> insn_list_node);
> - assert_rel_insn(INSN_CALL_REL, (unsigned long) trampoline_ptr(&mb),
> insn);
> + assert_rel_insn(INSN_CALL_REL, (unsigned long)
> method_trampoline_ptr(&mb), insn);
>
> free_jit_trampoline(mb.trampoline);
> free_compilation_unit(mb.compilation_unit);
> diff --git a/vm/jato.c b/vm/jato.c
> index eb361c0..5db42bc 100644
> --- a/vm/jato.c
> +++ b/vm/jato.c
> @@ -233,6 +233,10 @@ int parseCommandLine(int argc, char *argv[], InitArgs
> *args) {
> opt_trace_liveness = true;
> opt_trace_regalloc = true;
> opt_trace_machine_code = true;
> + opt_trace_magic_trampoline = true;
> +
> + } else if (strcmp(argv[i], "-Xtrace:trampoline") == 0) {
> + opt_trace_magic_trampoline = true;
>
> } else if(strcmp(argv[i], "-Xtrace:asm") == 0) {
> opt_trace_method = true;
> @@ -348,7 +352,7 @@ int main(int argc, char *argv[]) {
> if (run_with_interpreter)
> executeStaticMethod(main_class, mb, array);
> else {
> - java_main_fn java_main = trampoline_ptr(mb);
> + java_main_fn java_main = method_trampoline_ptr(mb);
> java_main();
> }
> }
------------------------------------------------------------------------------
This SF.net email is sponsored by:
High Quality Requirements in a Collaborative Environment.
Download a free trial of Rational Requirements Composer Now!
http://p.sf.net/sfu/www-ibm-com
_______________________________________________
Jatovm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jatovm-devel