/*******************************************************************************
 * Copyright (c) 2000, 2012 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
 
/**
 * Callback implementation.
 */
#include "callback.h"
#include <string.h>

#ifndef CALLBACK_NATIVE
#define CALLBACK_NATIVE(func) Java_org_eclipse_swt_internal_Callback_##func
#endif

/* define this to print out debug statements */
/* #define DEBUG_CALL_PRINTS */

/* --------------- callback globals ----------------- */

static JavaVM *jvm = NULL;
static CALLBACK_DATA callbackData[MAX_CALLBACKS];
static int callbackEnabled = 1;
static int callbackEntryCount = 0;
static int initialized = 0;
static jint JNI_VERSION = 0;

#ifdef DEBUG_CALL_PRINTS
static int counter = 0;
#endif

#ifdef ATOMIC
#include <libkern/OSAtomic.h>
#define ATOMIC_INC(value) OSAtomicIncrement32(&value);
#define ATOMIC_DEC(value) OSAtomicDecrement32(&value);
#else
#define ATOMIC_INC(value) value++;
#define ATOMIC_DEC(value) value--;
#endif

jintLong callback(int index, ...);

#ifdef USE_ASSEMBLER

#if !(defined (_WIN32) || defined (_WIN32_WCE))
#include <sys/mman.h>
#endif

static unsigned char *callbackCode = NULL;
#define CALLBACK_THUNK_SIZE 64

#else

/* --------------- callback functions --------------- */


/* Function name from index and number of arguments */
#define FN(index, args) fn##index##_##args

/**
 * Functions templates
 *
 * NOTE: If the maximum number of arguments changes (MAX_ARGS), the number
 *       of function templates has to change accordingly.
 */

/* Function template with no arguments */
#define FN_0(index) RETURN_TYPE FN(index, 0)() { return RETURN_CAST callback(index); }

/* Function template with 1 argument */
#define FN_1(index) RETURN_TYPE FN(index, 1)(jintLong p1) { return RETURN_CAST callback(index, p1); }

/* Function template with 2 arguments */
#define FN_2(index) RETURN_TYPE FN(index, 2)(jintLong p1, jintLong p2) { return RETURN_CAST callback(index, p1, p2); }

/* Function template with 3 arguments */
#define FN_3(index) RETURN_TYPE FN(index, 3)(jintLong p1, jintLong p2, jintLong p3) { return RETURN_CAST callback(index, p1, p2, p3); }

/* Function template with 4 arguments */
#define FN_4(index) RETURN_TYPE FN(index, 4)(jintLong p1, jintLong p2, jintLong p3, jintLong p4) { return RETURN_CAST callback(index, p1, p2, p3, p4); }

/* Function template with 5 arguments */
#define FN_5(index) RETURN_TYPE FN(index, 5)(jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5); }

/* Function template with 6 arguments */
#define FN_6(index) RETURN_TYPE FN(index, 6)(jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6); }

/* Function template with 7 arguments */
#define FN_7(index) RETURN_TYPE FN(index, 7)(jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6, jintLong p7) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6, p7); }

/* Function template with 8 arguments */
#define FN_8(index) RETURN_TYPE FN(index, 8)(jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6, jintLong p7, jintLong p8) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6, p7, p8); }

/* Function template with 9 arguments */
#define FN_9(index) RETURN_TYPE FN(index, 9)(jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6, jintLong p7, jintLong p8, jintLong p9) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6, p7, p8, p9); }

/* Function template with 10 arguments */
#define FN_10(index) RETURN_TYPE FN(index, 10) (jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6, jintLong p7, jintLong p8, jintLong p9, jintLong p10) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); }

/* Function template with 11 arguments */
#define FN_11(index) RETURN_TYPE FN(index, 11) (jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6, jintLong p7, jintLong p8, jintLong p9, jintLong p10, jintLong p11) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); }

/* Function template with 12 arguments */
#define FN_12(index) RETURN_TYPE FN(index, 12) (jintLong p1, jintLong p2, jintLong p3, jintLong p4, jintLong p5, jintLong p6, jintLong p7, jintLong p8, jintLong p9, jintLong p10, jintLong p11, jintLong p12) { return RETURN_CAST callback(index, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); }

/**
 * Define all functions with the specified number of arguments.
 *
 * NOTE: If the maximum number of callbacks changes (MAX_CALLBACKS),
 *       this macro has to be updated. 
 */
#if MAX_CALLBACKS == 16
#define FN_BLOCK(args) \
	FN_##args(0) \
	FN_##args(1) \
	FN_##args(2) \
	FN_##args(3) \
	FN_##args(4) \
	FN_##args(5) \
	FN_##args(6) \
	FN_##args(7) \
	FN_##args(8) \
	FN_##args(9) \
	FN_##args(10) \
	FN_##args(11) \
	FN_##args(12) \
	FN_##args(13) \
	FN_##args(14) \
	FN_##args(15)
#elif MAX_CALLBACKS == 128
#define FN_BLOCK(args) \
	FN_##args(0) \
	FN_##args(1) \
	FN_##args(2) \
	FN_##args(3) \
	FN_##args(4) \
	FN_##args(5) \
	FN_##args(6) \
	FN_##args(7) \
	FN_##args(8) \
	FN_##args(9) \
	FN_##args(10) \
	FN_##args(11) \
	FN_##args(12) \
	FN_##args(13) \
	FN_##args(14) \
	FN_##args(15) \
	FN_##args(16) \
	FN_##args(17) \
	FN_##args(18) \
	FN_##args(19) \
	FN_##args(20) \
	FN_##args(21) \
	FN_##args(22) \
	FN_##args(23) \
	FN_##args(24) \
	FN_##args(25) \
	FN_##args(26) \
	FN_##args(27) \
	FN_##args(28) \
	FN_##args(29) \
	FN_##args(30) \
	FN_##args(31) \
	FN_##args(32) \
	FN_##args(33) \
	FN_##args(34) \
	FN_##args(35) \
	FN_##args(36) \
	FN_##args(37) \
	FN_##args(38) \
	FN_##args(39) \
	FN_##args(40) \
	FN_##args(41) \
	FN_##args(42) \
	FN_##args(43) \
	FN_##args(44) \
	FN_##args(45) \
	FN_##args(46) \
	FN_##args(47) \
	FN_##args(48) \
	FN_##args(49) \
	FN_##args(50) \
	FN_##args(51) \
	FN_##args(52) \
	FN_##args(53) \
	FN_##args(54) \
	FN_##args(55) \
	FN_##args(56) \
	FN_##args(57) \
	FN_##args(58) \
	FN_##args(59) \
	FN_##args(60) \
	FN_##args(61) \
	FN_##args(62) \
	FN_##args(63) \
	FN_##args(64) \
	FN_##args(65) \
	FN_##args(66) \
	FN_##args(67) \
	FN_##args(68) \
	FN_##args(69) \
	FN_##args(70) \
	FN_##args(71) \
	FN_##args(72) \
	FN_##args(73) \
	FN_##args(74) \
	FN_##args(75) \
	FN_##args(76) \
	FN_##args(77) \
	FN_##args(78) \
	FN_##args(79) \
	FN_##args(80) \
	FN_##args(81) \
	FN_##args(82) \
	FN_##args(83) \
	FN_##args(84) \
	FN_##args(85) \
	FN_##args(86) \
	FN_##args(87) \
	FN_##args(88) \
	FN_##args(89) \
	FN_##args(90) \
	FN_##args(91) \
	FN_##args(92) \
	FN_##args(93) \
	FN_##args(94) \
	FN_##args(95) \
	FN_##args(96) \
	FN_##args(97) \
	FN_##args(98) \
	FN_##args(99) \
	FN_##args(100) \
	FN_##args(101) \
	FN_##args(102) \
	FN_##args(103) \
	FN_##args(104) \
	FN_##args(105) \
	FN_##args(106) \
	FN_##args(107) \
	FN_##args(108) \
	FN_##args(109) \
	FN_##args(110) \
	FN_##args(111) \
	FN_##args(112) \
	FN_##args(113) \
	FN_##args(114) \
	FN_##args(115) \
	FN_##args(116) \
	FN_##args(117) \
	FN_##args(118) \
	FN_##args(119) \
	FN_##args(120) \
	FN_##args(121) \
	FN_##args(122) \
	FN_##args(123) \
	FN_##args(124) \
	FN_##args(125) \
	FN_##args(126) \
	FN_##args(127)
#elif MAX_CALLBACKS == 256
#define FN_BLOCK(args) \
	FN_##args(0) \
	FN_##args(1) \
	FN_##args(2) \
	FN_##args(3) \
	FN_##args(4) \
	FN_##args(5) \
	FN_##args(6) \
	FN_##args(7) \
	FN_##args(8) \
	FN_##args(9) \
	FN_##args(10) \
	FN_##args(11) \
	FN_##args(12) \
	FN_##args(13) \
	FN_##args(14) \
	FN_##args(15) \
	FN_##args(16) \
	FN_##args(17) \
	FN_##args(18) \
	FN_##args(19) \
	FN_##args(20) \
	FN_##args(21) \
	FN_##args(22) \
	FN_##args(23) \
	FN_##args(24) \
	FN_##args(25) \
	FN_##args(26) \
	FN_##args(27) \
	FN_##args(28) \
	FN_##args(29) \
	FN_##args(30) \
	FN_##args(31) \
	FN_##args(32) \
	FN_##args(33) \
	FN_##args(34) \
	FN_##args(35) \
	FN_##args(36) \
	FN_##args(37) \
	FN_##args(38) \
	FN_##args(39) \
	FN_##args(40) \
	FN_##args(41) \
	FN_##args(42) \
	FN_##args(43) \
	FN_##args(44) \
	FN_##args(45) \
	FN_##args(46) \
	FN_##args(47) \
	FN_##args(48) \
	FN_##args(49) \
	FN_##args(50) \
	FN_##args(51) \
	FN_##args(52) \
	FN_##args(53) \
	FN_##args(54) \
	FN_##args(55) \
	FN_##args(56) \
	FN_##args(57) \
	FN_##args(58) \
	FN_##args(59) \
	FN_##args(60) \
	FN_##args(61) \
	FN_##args(62) \
	FN_##args(63) \
	FN_##args(64) \
	FN_##args(65) \
	FN_##args(66) \
	FN_##args(67) \
	FN_##args(68) \
	FN_##args(69) \
	FN_##args(70) \
	FN_##args(71) \
	FN_##args(72) \
	FN_##args(73) \
	FN_##args(74) \
	FN_##args(75) \
	FN_##args(76) \
	FN_##args(77) \
	FN_##args(78) \
	FN_##args(79) \
	FN_##args(80) \
	FN_##args(81) \
	FN_##args(82) \
	FN_##args(83) \
	FN_##args(84) \
	FN_##args(85) \
	FN_##args(86) \
	FN_##args(87) \
	FN_##args(88) \
	FN_##args(89) \
	FN_##args(90) \
	FN_##args(91) \
	FN_##args(92) \
	FN_##args(93) \
	FN_##args(94) \
	FN_##args(95) \
	FN_##args(96) \
	FN_##args(97) \
	FN_##args(98) \
	FN_##args(99) \
	FN_##args(100) \
	FN_##args(101) \
	FN_##args(102) \
	FN_##args(103) \
	FN_##args(104) \
	FN_##args(105) \
	FN_##args(106) \
	FN_##args(107) \
	FN_##args(108) \
	FN_##args(109) \
	FN_##args(110) \
	FN_##args(111) \
	FN_##args(112) \
	FN_##args(113) \
	FN_##args(114) \
	FN_##args(115) \
	FN_##args(116) \
	FN_##args(117) \
	FN_##args(118) \
	FN_##args(119) \
	FN_##args(120) \
	FN_##args(121) \
	FN_##args(122) \
	FN_##args(123) \
	FN_##args(124) \
	FN_##args(125) \
	FN_##args(126) \
	FN_##args(127) \
	FN_##args(128) \
	FN_##args(129) \
	FN_##args(130) \
	FN_##args(131) \
	FN_##args(132) \
	FN_##args(133) \
	FN_##args(134) \
	FN_##args(135) \
	FN_##args(136) \
	FN_##args(137) \
	FN_##args(138) \
	FN_##args(139) \
	FN_##args(140) \
	FN_##args(141) \
	FN_##args(142) \
	FN_##args(143) \
	FN_##args(144) \
	FN_##args(145) \
	FN_##args(146) \
	FN_##args(147) \
	FN_##args(148) \
	FN_##args(149) \
	FN_##args(150) \
	FN_##args(151) \
	FN_##args(152) \
	FN_##args(153) \
	FN_##args(154) \
	FN_##args(155) \
	FN_##args(156) \
	FN_##args(157) \
	FN_##args(158) \
	FN_##args(159) \
	FN_##args(160) \
	FN_##args(161) \
	FN_##args(162) \
	FN_##args(163) \
	FN_##args(164) \
	FN_##args(165) \
	FN_##args(166) \
	FN_##args(167) \
	FN_##args(168) \
	FN_##args(169) \
	FN_##args(170) \
	FN_##args(171) \
	FN_##args(172) \
	FN_##args(173) \
	FN_##args(174) \
	FN_##args(175) \
	FN_##args(176) \
	FN_##args(177) \
	FN_##args(178) \
	FN_##args(179) \
	FN_##args(180) \
	FN_##args(181) \
	FN_##args(182) \
	FN_##args(183) \
	FN_##args(184) \
	FN_##args(185) \
	FN_##args(186) \
	FN_##args(187) \
	FN_##args(188) \
	FN_##args(189) \
	FN_##args(190) \
	FN_##args(191) \
	FN_##args(192) \
	FN_##args(193) \
	FN_##args(194) \
	FN_##args(195) \
	FN_##args(196) \
	FN_##args(197) \
	FN_##args(198) \
	FN_##args(199) \
	FN_##args(200) \
	FN_##args(201) \
	FN_##args(202) \
	FN_##args(203) \
	FN_##args(204) \
	FN_##args(205) \
	FN_##args(206) \
	FN_##args(207) \
	FN_##args(208) \
	FN_##args(209) \
	FN_##args(210) \
	FN_##args(211) \
	FN_##args(212) \
	FN_##args(213) \
	FN_##args(214) \
	FN_##args(215) \
	FN_##args(216) \
	FN_##args(217) \
	FN_##args(218) \
	FN_##args(219) \
	FN_##args(220) \
	FN_##args(221) \
	FN_##args(222) \
	FN_##args(223) \
	FN_##args(224) \
	FN_##args(225) \
	FN_##args(226) \
	FN_##args(227) \
	FN_##args(228) \
	FN_##args(229) \
	FN_##args(230) \
	FN_##args(231) \
	FN_##args(232) \
	FN_##args(233) \
	FN_##args(234) \
	FN_##args(235) \
	FN_##args(236) \
	FN_##args(237) \
	FN_##args(238) \
	FN_##args(239) \
	FN_##args(240) \
	FN_##args(241) \
	FN_##args(242) \
	FN_##args(243) \
	FN_##args(244) \
	FN_##args(245) \
	FN_##args(246) \
	FN_##args(247) \
	FN_##args(248) \
	FN_##args(249) \
	FN_##args(250) \
	FN_##args(251) \
	FN_##args(252) \
	FN_##args(253) \
	FN_##args(254) \
	FN_##args(255)
#else
#error Invalid MAX_CALLBACKS
#endif /* MAX_CALLBACKS == 16 */

/**
 * Define all callback functions.
 *
 * NOTE: If the maximum number of arguments changes (MAX_ARGS), the following
 *       has to change accordinglly.
 */
FN_BLOCK(0)
FN_BLOCK(1)
FN_BLOCK(2)
FN_BLOCK(3)
FN_BLOCK(4)
FN_BLOCK(5)
FN_BLOCK(6)
FN_BLOCK(7)
FN_BLOCK(8)
FN_BLOCK(9)
FN_BLOCK(10)
FN_BLOCK(11)
FN_BLOCK(12)

/**
 * Initialize the function pointers for the callback routines.
 *
 * NOTE: If MAX_ARGS or MAX_CALLBACKS changes, the following has to be updated.
 */
#if MAX_CALLBACKS == 16
#define FN_A_BLOCK(args) { \
	(jintLong)FN(0, args), \
	(jintLong)FN(1, args), \
	(jintLong)FN(2, args), \
	(jintLong)FN(3, args), \
	(jintLong)FN(4, args), \
	(jintLong)FN(5, args), \
	(jintLong)FN(6, args), \
	(jintLong)FN(7, args), \
	(jintLong)FN(8, args), \
	(jintLong)FN(9, args), \
	(jintLong)FN(10, args), \
	(jintLong)FN(11, args), \
	(jintLong)FN(12, args), \
	(jintLong)FN(13, args), \
	(jintLong)FN(14, args), \
	(jintLong)FN(15, args), \
},
#elif MAX_CALLBACKS == 128
#define FN_A_BLOCK(args) { \
	(jintLong)FN(0, args), \
	(jintLong)FN(1, args), \
	(jintLong)FN(2, args), \
	(jintLong)FN(3, args), \
	(jintLong)FN(4, args), \
	(jintLong)FN(5, args), \
	(jintLong)FN(6, args), \
	(jintLong)FN(7, args), \
	(jintLong)FN(8, args), \
	(jintLong)FN(9, args), \
	(jintLong)FN(10, args), \
	(jintLong)FN(11, args), \
	(jintLong)FN(12, args), \
	(jintLong)FN(13, args), \
	(jintLong)FN(14, args), \
	(jintLong)FN(15, args), \
	(jintLong)FN(16, args), \
	(jintLong)FN(17, args), \
	(jintLong)FN(18, args), \
	(jintLong)FN(19, args), \
	(jintLong)FN(20, args), \
	(jintLong)FN(21, args), \
	(jintLong)FN(22, args), \
	(jintLong)FN(23, args), \
	(jintLong)FN(24, args), \
	(jintLong)FN(25, args), \
	(jintLong)FN(26, args), \
	(jintLong)FN(27, args), \
	(jintLong)FN(28, args), \
	(jintLong)FN(29, args), \
	(jintLong)FN(30, args), \
	(jintLong)FN(31, args), \
	(jintLong)FN(32, args), \
	(jintLong)FN(33, args), \
	(jintLong)FN(34, args), \
	(jintLong)FN(35, args), \
	(jintLong)FN(36, args), \
	(jintLong)FN(37, args), \
	(jintLong)FN(38, args), \
	(jintLong)FN(39, args), \
	(jintLong)FN(40, args), \
	(jintLong)FN(41, args), \
	(jintLong)FN(42, args), \
	(jintLong)FN(43, args), \
	(jintLong)FN(44, args), \
	(jintLong)FN(45, args), \
	(jintLong)FN(46, args), \
	(jintLong)FN(47, args), \
	(jintLong)FN(48, args), \
	(jintLong)FN(49, args), \
	(jintLong)FN(50, args), \
	(jintLong)FN(51, args), \
	(jintLong)FN(52, args), \
	(jintLong)FN(53, args), \
	(jintLong)FN(54, args), \
	(jintLong)FN(55, args), \
	(jintLong)FN(56, args), \
	(jintLong)FN(57, args), \
	(jintLong)FN(58, args), \
	(jintLong)FN(59, args), \
	(jintLong)FN(60, args), \
	(jintLong)FN(61, args), \
	(jintLong)FN(62, args), \
	(jintLong)FN(63, args), \
	(jintLong)FN(64, args), \
	(jintLong)FN(65, args), \
	(jintLong)FN(66, args), \
	(jintLong)FN(67, args), \
	(jintLong)FN(68, args), \
	(jintLong)FN(69, args), \
	(jintLong)FN(70, args), \
	(jintLong)FN(71, args), \
	(jintLong)FN(72, args), \
	(jintLong)FN(73, args), \
	(jintLong)FN(74, args), \
	(jintLong)FN(75, args), \
	(jintLong)FN(76, args), \
	(jintLong)FN(77, args), \
	(jintLong)FN(78, args), \
	(jintLong)FN(79, args), \
	(jintLong)FN(80, args), \
	(jintLong)FN(81, args), \
	(jintLong)FN(82, args), \
	(jintLong)FN(83, args), \
	(jintLong)FN(84, args), \
	(jintLong)FN(85, args), \
	(jintLong)FN(86, args), \
	(jintLong)FN(87, args), \
	(jintLong)FN(88, args), \
	(jintLong)FN(89, args), \
	(jintLong)FN(90, args), \
	(jintLong)FN(91, args), \
	(jintLong)FN(92, args), \
	(jintLong)FN(93, args), \
	(jintLong)FN(94, args), \
	(jintLong)FN(95, args), \
	(jintLong)FN(96, args), \
	(jintLong)FN(97, args), \
	(jintLong)FN(98, args), \
	(jintLong)FN(99, args), \
	(jintLong)FN(100, args), \
	(jintLong)FN(101, args), \
	(jintLong)FN(102, args), \
	(jintLong)FN(103, args), \
	(jintLong)FN(104, args), \
	(jintLong)FN(105, args), \
	(jintLong)FN(106, args), \
	(jintLong)FN(107, args), \
	(jintLong)FN(108, args), \
	(jintLong)FN(109, args), \
	(jintLong)FN(110, args), \
	(jintLong)FN(111, args), \
	(jintLong)FN(112, args), \
	(jintLong)FN(113, args), \
	(jintLong)FN(114, args), \
	(jintLong)FN(115, args), \
	(jintLong)FN(116, args), \
	(jintLong)FN(117, args), \
	(jintLong)FN(118, args), \
	(jintLong)FN(119, args), \
	(jintLong)FN(120, args), \
	(jintLong)FN(121, args), \
	(jintLong)FN(122, args), \
	(jintLong)FN(123, args), \
	(jintLong)FN(124, args), \
	(jintLong)FN(125, args), \
	(jintLong)FN(126, args), \
	(jintLong)FN(127, args), \
},
#elif MAX_CALLBACKS == 256
#define FN_A_BLOCK(args) { \
	(jintLong)FN(0, args), \
	(jintLong)FN(1, args), \
	(jintLong)FN(2, args), \
	(jintLong)FN(3, args), \
	(jintLong)FN(4, args), \
	(jintLong)FN(5, args), \
	(jintLong)FN(6, args), \
	(jintLong)FN(7, args), \
	(jintLong)FN(8, args), \
	(jintLong)FN(9, args), \
	(jintLong)FN(10, args), \
	(jintLong)FN(11, args), \
	(jintLong)FN(12, args), \
	(jintLong)FN(13, args), \
	(jintLong)FN(14, args), \
	(jintLong)FN(15, args), \
	(jintLong)FN(16, args), \
	(jintLong)FN(17, args), \
	(jintLong)FN(18, args), \
	(jintLong)FN(19, args), \
	(jintLong)FN(20, args), \
	(jintLong)FN(21, args), \
	(jintLong)FN(22, args), \
	(jintLong)FN(23, args), \
	(jintLong)FN(24, args), \
	(jintLong)FN(25, args), \
	(jintLong)FN(26, args), \
	(jintLong)FN(27, args), \
	(jintLong)FN(28, args), \
	(jintLong)FN(29, args), \
	(jintLong)FN(30, args), \
	(jintLong)FN(31, args), \
	(jintLong)FN(32, args), \
	(jintLong)FN(33, args), \
	(jintLong)FN(34, args), \
	(jintLong)FN(35, args), \
	(jintLong)FN(36, args), \
	(jintLong)FN(37, args), \
	(jintLong)FN(38, args), \
	(jintLong)FN(39, args), \
	(jintLong)FN(40, args), \
	(jintLong)FN(41, args), \
	(jintLong)FN(42, args), \
	(jintLong)FN(43, args), \
	(jintLong)FN(44, args), \
	(jintLong)FN(45, args), \
	(jintLong)FN(46, args), \
	(jintLong)FN(47, args), \
	(jintLong)FN(48, args), \
	(jintLong)FN(49, args), \
	(jintLong)FN(50, args), \
	(jintLong)FN(51, args), \
	(jintLong)FN(52, args), \
	(jintLong)FN(53, args), \
	(jintLong)FN(54, args), \
	(jintLong)FN(55, args), \
	(jintLong)FN(56, args), \
	(jintLong)FN(57, args), \
	(jintLong)FN(58, args), \
	(jintLong)FN(59, args), \
	(jintLong)FN(60, args), \
	(jintLong)FN(61, args), \
	(jintLong)FN(62, args), \
	(jintLong)FN(63, args), \
	(jintLong)FN(64, args), \
	(jintLong)FN(65, args), \
	(jintLong)FN(66, args), \
	(jintLong)FN(67, args), \
	(jintLong)FN(68, args), \
	(jintLong)FN(69, args), \
	(jintLong)FN(70, args), \
	(jintLong)FN(71, args), \
	(jintLong)FN(72, args), \
	(jintLong)FN(73, args), \
	(jintLong)FN(74, args), \
	(jintLong)FN(75, args), \
	(jintLong)FN(76, args), \
	(jintLong)FN(77, args), \
	(jintLong)FN(78, args), \
	(jintLong)FN(79, args), \
	(jintLong)FN(80, args), \
	(jintLong)FN(81, args), \
	(jintLong)FN(82, args), \
	(jintLong)FN(83, args), \
	(jintLong)FN(84, args), \
	(jintLong)FN(85, args), \
	(jintLong)FN(86, args), \
	(jintLong)FN(87, args), \
	(jintLong)FN(88, args), \
	(jintLong)FN(89, args), \
	(jintLong)FN(90, args), \
	(jintLong)FN(91, args), \
	(jintLong)FN(92, args), \
	(jintLong)FN(93, args), \
	(jintLong)FN(94, args), \
	(jintLong)FN(95, args), \
	(jintLong)FN(96, args), \
	(jintLong)FN(97, args), \
	(jintLong)FN(98, args), \
	(jintLong)FN(99, args), \
	(jintLong)FN(100, args), \
	(jintLong)FN(101, args), \
	(jintLong)FN(102, args), \
	(jintLong)FN(103, args), \
	(jintLong)FN(104, args), \
	(jintLong)FN(105, args), \
	(jintLong)FN(106, args), \
	(jintLong)FN(107, args), \
	(jintLong)FN(108, args), \
	(jintLong)FN(109, args), \
	(jintLong)FN(110, args), \
	(jintLong)FN(111, args), \
	(jintLong)FN(112, args), \
	(jintLong)FN(113, args), \
	(jintLong)FN(114, args), \
	(jintLong)FN(115, args), \
	(jintLong)FN(116, args), \
	(jintLong)FN(117, args), \
	(jintLong)FN(118, args), \
	(jintLong)FN(119, args), \
	(jintLong)FN(120, args), \
	(jintLong)FN(121, args), \
	(jintLong)FN(122, args), \
	(jintLong)FN(123, args), \
	(jintLong)FN(124, args), \
	(jintLong)FN(125, args), \
	(jintLong)FN(126, args), \
	(jintLong)FN(127, args), \
	(jintLong)FN(128, args), \
	(jintLong)FN(129, args), \
	(jintLong)FN(130, args), \
	(jintLong)FN(131, args), \
	(jintLong)FN(132, args), \
	(jintLong)FN(133, args), \
	(jintLong)FN(134, args), \
	(jintLong)FN(135, args), \
	(jintLong)FN(136, args), \
	(jintLong)FN(137, args), \
	(jintLong)FN(138, args), \
	(jintLong)FN(139, args), \
	(jintLong)FN(140, args), \
	(jintLong)FN(141, args), \
	(jintLong)FN(142, args), \
	(jintLong)FN(143, args), \
	(jintLong)FN(144, args), \
	(jintLong)FN(145, args), \
	(jintLong)FN(146, args), \
	(jintLong)FN(147, args), \
	(jintLong)FN(148, args), \
	(jintLong)FN(149, args), \
	(jintLong)FN(150, args), \
	(jintLong)FN(151, args), \
	(jintLong)FN(152, args), \
	(jintLong)FN(153, args), \
	(jintLong)FN(154, args), \
	(jintLong)FN(155, args), \
	(jintLong)FN(156, args), \
	(jintLong)FN(157, args), \
	(jintLong)FN(158, args), \
	(jintLong)FN(159, args), \
	(jintLong)FN(160, args), \
	(jintLong)FN(161, args), \
	(jintLong)FN(162, args), \
	(jintLong)FN(163, args), \
	(jintLong)FN(164, args), \
	(jintLong)FN(165, args), \
	(jintLong)FN(166, args), \
	(jintLong)FN(167, args), \
	(jintLong)FN(168, args), \
	(jintLong)FN(169, args), \
	(jintLong)FN(170, args), \
	(jintLong)FN(171, args), \
	(jintLong)FN(172, args), \
	(jintLong)FN(173, args), \
	(jintLong)FN(174, args), \
	(jintLong)FN(175, args), \
	(jintLong)FN(176, args), \
	(jintLong)FN(177, args), \
	(jintLong)FN(178, args), \
	(jintLong)FN(179, args), \
	(jintLong)FN(180, args), \
	(jintLong)FN(181, args), \
	(jintLong)FN(182, args), \
	(jintLong)FN(183, args), \
	(jintLong)FN(184, args), \
	(jintLong)FN(185, args), \
	(jintLong)FN(186, args), \
	(jintLong)FN(187, args), \
	(jintLong)FN(188, args), \
	(jintLong)FN(189, args), \
	(jintLong)FN(190, args), \
	(jintLong)FN(191, args), \
	(jintLong)FN(192, args), \
	(jintLong)FN(193, args), \
	(jintLong)FN(194, args), \
	(jintLong)FN(195, args), \
	(jintLong)FN(196, args), \
	(jintLong)FN(197, args), \
	(jintLong)FN(198, args), \
	(jintLong)FN(199, args), \
	(jintLong)FN(200, args), \
	(jintLong)FN(201, args), \
	(jintLong)FN(202, args), \
	(jintLong)FN(203, args), \
	(jintLong)FN(204, args), \
	(jintLong)FN(205, args), \
	(jintLong)FN(206, args), \
	(jintLong)FN(207, args), \
	(jintLong)FN(208, args), \
	(jintLong)FN(209, args), \
	(jintLong)FN(210, args), \
	(jintLong)FN(211, args), \
	(jintLong)FN(212, args), \
	(jintLong)FN(213, args), \
	(jintLong)FN(214, args), \
	(jintLong)FN(215, args), \
	(jintLong)FN(216, args), \
	(jintLong)FN(217, args), \
	(jintLong)FN(218, args), \
	(jintLong)FN(219, args), \
	(jintLong)FN(220, args), \
	(jintLong)FN(221, args), \
	(jintLong)FN(222, args), \
	(jintLong)FN(223, args), \
	(jintLong)FN(224, args), \
	(jintLong)FN(225, args), \
	(jintLong)FN(226, args), \
	(jintLong)FN(227, args), \
	(jintLong)FN(228, args), \
	(jintLong)FN(229, args), \
	(jintLong)FN(230, args), \
	(jintLong)FN(231, args), \
	(jintLong)FN(232, args), \
	(jintLong)FN(233, args), \
	(jintLong)FN(234, args), \
	(jintLong)FN(235, args), \
	(jintLong)FN(236, args), \
	(jintLong)FN(237, args), \
	(jintLong)FN(238, args), \
	(jintLong)FN(239, args), \
	(jintLong)FN(240, args), \
	(jintLong)FN(241, args), \
	(jintLong)FN(242, args), \
	(jintLong)FN(243, args), \
	(jintLong)FN(244, args), \
	(jintLong)FN(245, args), \
	(jintLong)FN(246, args), \
	(jintLong)FN(247, args), \
	(jintLong)FN(248, args), \
	(jintLong)FN(249, args), \
	(jintLong)FN(250, args), \
	(jintLong)FN(251, args), \
	(jintLong)FN(252, args), \
	(jintLong)FN(253, args), \
	(jintLong)FN(254, args), \
	(jintLong)FN(255, args), \
},
#else
#error Invalid MAX_CALLBACKS
#endif /* MAX_CALLBACKS == 16 */

jintLong fnx_array[MAX_ARGS+1][MAX_CALLBACKS] = { 
	FN_A_BLOCK(0)    
	FN_A_BLOCK(1)    
	FN_A_BLOCK(2)    
	FN_A_BLOCK(3)    
	FN_A_BLOCK(4)    
	FN_A_BLOCK(5)    
	FN_A_BLOCK(6)    
	FN_A_BLOCK(7)    
	FN_A_BLOCK(8)    
	FN_A_BLOCK(9)    
	FN_A_BLOCK(10)    
	FN_A_BLOCK(11)    
	FN_A_BLOCK(12)    
};

#endif /* USE_ASSEMBLER */

/* --------------- callback class calls --------------- */

JNIEXPORT jintLong JNICALL CALLBACK_NATIVE(bind)
  (JNIEnv *env, jclass that, jobject callbackObject, jobject object, jstring method, jstring signature, jint argCount, jboolean isStatic, jboolean isArrayBased, jintLong errorResult)
{
	int i;
	jmethodID mid = NULL;
	jclass javaClass = that;
	const char *methodString = NULL, *sigString = NULL;
	if (jvm == NULL) (*env)->GetJavaVM(env, &jvm);
	if (JNI_VERSION == 0) JNI_VERSION = (*env)->GetVersion(env);
	if (!initialized) {
		memset(&callbackData, 0, sizeof(callbackData));
		initialized = 1;
	}
	if (method) methodString = (const char *) (*env)->GetStringUTFChars(env, method, NULL);
	if (signature) sigString = (const char *) (*env)->GetStringUTFChars(env, signature, NULL);
	if (object && methodString && sigString) {
		if (isStatic) {
			mid = (*env)->GetStaticMethodID(env, object, methodString, sigString);
		} else {
			javaClass = (*env)->GetObjectClass(env, object);    
			mid = (*env)->GetMethodID(env, javaClass, methodString, sigString);
		}
	}
	if (method && methodString) (*env)->ReleaseStringUTFChars(env, method, methodString);
	if (signature && sigString) (*env)->ReleaseStringUTFChars(env, signature, sigString);
	if (mid == 0) goto fail;
	for (i=0; i<MAX_CALLBACKS; i++) {
		if (!callbackData[i].callback) {
			if ((callbackData[i].callback = (*env)->NewGlobalRef(env, callbackObject)) == NULL) goto fail;
			if ((callbackData[i].object = (*env)->NewGlobalRef(env, object)) == NULL) goto fail;
			callbackData[i].isStatic = isStatic;
			callbackData[i].isArrayBased = isArrayBased;
			callbackData[i].argCount = argCount;
			callbackData[i].errorResult = errorResult;
			callbackData[i].methodID = mid;
#ifndef USE_ASSEMBLER
			return (jintLong) fnx_array[argCount][i];
#else
			{
			int j = 0, k;
			unsigned char* code;
#ifdef __APPLE__
			int pad = 0;
#endif
			if (callbackCode == NULL) {
#if defined (_WIN32) || defined (_WIN32_WCE)
				callbackCode = VirtualAlloc(NULL, CALLBACK_THUNK_SIZE * MAX_CALLBACKS, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
				if (callbackCode == NULL) return 0;
#else 
				callbackCode = mmap(NULL, CALLBACK_THUNK_SIZE * MAX_CALLBACKS, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
				if (callbackCode == MAP_FAILED) return 0;
#endif
			}
			code = (unsigned char *)(callbackCode + (i * CALLBACK_THUNK_SIZE));

			//PUSH EBP - 1 byte
			code[j++] = 0x55;

			//MOV EBP,ESP - 2 bytes
			code[j++] = 0x8b;
			code[j++] = 0xec;

#ifdef __APPLE__
			/* darwin calling conventions require that the stack be aligned on a 16-byte boundary. */
			k = (argCount+3)*sizeof(jintLong);
			pad = ((k + 15) & ~15) - k;
			if (pad > 0) {
				//SUB ESP,pad - 3 bytes
				code[j++] = 0x83;
				code[j++] = 0xec;
				code[j++] = pad;
			}
#endif

			// 3*argCount bytes
			for (k=(argCount + 1) * sizeof(jintLong); k >= sizeof(jintLong)*2; k -= sizeof(jintLong)) {
				//PUSH SS:[EBP+k]
				code[j++] = 0xff;
				code[j++] = 0x75;
				code[j++] = k;
			}

			if (i > 127) {
				//PUSH i - 5 bytes
				code[j++] = 0x68;
				code[j++] = ((i >> 0) & 0xFF);
				code[j++] = ((i >> 8) & 0xFF);
				code[j++] = ((i >> 16) & 0xFF);
				code[j++] = ((i >> 24) & 0xFF);
			} else {
				//PUSH i - 2 bytes
				code[j++] = 0x6a;
				code[j++] = i;
			}

			//MOV EAX callback - 1 + sizeof(jintLong) bytes
			code[j++] = 0xb8;
			((jintLong *)&code[j])[0] = (jintLong)&callback;
			j += sizeof(jintLong);

			//CALL EAX - 2 bytes
			code[j++] = 0xff;
			code[j++] = 0xd0;

			//ADD ESP,(argCount + 1) * sizeof(jintLong) - 3 bytes
			code[j++] = 0x83;
			code[j++] = 0xc4;
#ifdef __APPLE__
			code[j++] = (unsigned char)(pad + ((argCount + 1) * sizeof(jintLong)));
#else
			code[j++] = (unsigned char)((argCount + 1) * sizeof(jintLong));
#endif

			//POP EBP - 1 byte
			code[j++] = 0x5d;

#if defined (_WIN32) || defined (_WIN32_WCE)
			//RETN argCount * sizeof(jintLong) - 3 bytes
			code[j++] = 0xc2;
			code[j++] = (unsigned char)(argCount * sizeof(jintLong));
			code[j++] = 0x00;
#else
			//RETN - 1 byte
			code[j++] = 0xc3;
#endif

			if (j > CALLBACK_THUNK_SIZE) {
				jclass errorClass = (*env)->FindClass(env, "java/lang/Error");
				(*env)->ThrowNew(env, errorClass, "Callback thunk overflow");
			}

			return (jintLong)code;
			}
#endif /* USE_ASSEMBLER */
		}
	}
fail:
    return 0;
}

JNIEXPORT void JNICALL CALLBACK_NATIVE(unbind)
  (JNIEnv *env, jclass that, jobject callback)
{
	int i;
    for (i=0; i<MAX_CALLBACKS; i++) {
        if (callbackData[i].callback != NULL && (*env)->IsSameObject(env, callback, callbackData[i].callback)) {
            if (callbackData[i].callback != NULL) (*env)->DeleteGlobalRef(env, callbackData[i].callback);
            if (callbackData[i].object != NULL) (*env)->DeleteGlobalRef(env, callbackData[i].object);
            memset(&callbackData[i], 0, sizeof(CALLBACK_DATA));
        }
    }
}

JNIEXPORT jboolean JNICALL CALLBACK_NATIVE(getEnabled)
  (JNIEnv *env, jclass that)
{
	return (jboolean)callbackEnabled;
}

JNIEXPORT jint JNICALL CALLBACK_NATIVE(getEntryCount)
  (JNIEnv *env, jclass that)
{
	return (jint)callbackEntryCount;
}

JNIEXPORT void JNICALL CALLBACK_NATIVE(setEnabled)
  (JNIEnv *env, jclass that, jboolean enable)
{
	callbackEnabled = enable;
}

JNIEXPORT void JNICALL CALLBACK_NATIVE(reset)
  (JNIEnv *env, jclass that)
{
    memset((void *)&callbackData, 0, sizeof(callbackData));
}

jintLong callback(int index, ...)
{
	if (!callbackEnabled) return 0;

	{
	JNIEnv *env = NULL;
	jmethodID mid = callbackData[index].methodID;
	jobject object = callbackData[index].object;
	jboolean isStatic = callbackData[index].isStatic;
	jboolean isArrayBased = callbackData[index].isArrayBased;
	jint argCount = callbackData[index].argCount;
	jintLong result = callbackData[index].errorResult;
	jthrowable ex;
	int detach = 0;
	va_list vl;

#ifdef DEBUG_CALL_PRINTS
	fprintf(stderr, "* callback starting %d\n", counter++);
#endif

#ifdef JNI_VERSION_1_2
	if (IS_JNI_1_2) {
		(*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_2);
	}
#endif
	
#ifdef JNI_VERSION_1_4
	if (env == NULL) {
		if (JNI_VERSION >= JNI_VERSION_1_4) {
			(*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, NULL);
		}
	}
#endif
	
	if (env == NULL) {
		(*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
		if (IS_JNI_1_2) detach = 1;
	}
	
	/* If the current thread is not attached to the VM, it is not possible to call into the VM */
	if (env == NULL) {
#ifdef DEBUG_CALL_PRINTS
		fprintf(stderr, "* could not get env\n");
#endif
		goto noEnv;
	}

	/* If an exception has occurred in previous callbacks do not call into the VM. */
	if ((ex = (*env)->ExceptionOccurred(env))) {
		(*env)->DeleteLocalRef(env, ex);
		goto done;
	}

	/* Call into the VM. */
	ATOMIC_INC(callbackEntryCount);
	va_start(vl, index);
	if (isArrayBased) {
		int i;
		jintLongArray argsArray = (*env)->NewIntLongArray(env, argCount);
		if (argsArray != NULL) {
			jintLong *elements = (*env)->GetIntLongArrayElements(env, argsArray, NULL);
			if (elements != NULL) {
				for (i=0; i<argCount; i++) {
					elements[i] = va_arg(vl, jintLong); 
				}
				(*env)->ReleaseIntLongArrayElements(env, argsArray, elements, 0);
				if (isStatic) {
					result = (*env)->CallStaticIntLongMethod(env, object, mid, argsArray);
				} else {
					result = (*env)->CallIntLongMethod(env, object, mid, argsArray);
				}
			}
			/*
			* This function may be called many times before returning to Java,
			* explicitly delete local references to avoid GP's in certain VMs.
			*/
			(*env)->DeleteLocalRef(env, argsArray);
		}
	} else {
		if (isStatic) {
			result = (*env)->CallStaticIntLongMethodV(env, object, mid, vl);
		} else {
			result = (*env)->CallIntLongMethodV(env, object, mid, vl);
		}
	}
	va_end(vl);
	ATOMIC_DEC(callbackEntryCount);

done:
	/* If an exception has occurred in Java, return the error result. */
	if ((ex = (*env)->ExceptionOccurred(env))) {
		(*env)->DeleteLocalRef(env, ex);
#ifdef DEBUG_CALL_PRINTS
		fprintf(stderr, "* java exception occurred\n");
		(*env)->ExceptionDescribe(env);
#endif
		result = callbackData[index].errorResult;
	}

	if (detach) {
		(*jvm)->DetachCurrentThread(jvm);
		env = NULL;
	}

noEnv:

#ifdef DEBUG_CALL_PRINTS
	fprintf(stderr, "* callback exiting %d\n", --counter);
#endif

	return result;
	}
}

/* ------------- callback class calls end --------------- */
