Bug 452138 - Reduce number of error-prone manual steps needed to support
new versions of XULRunner

Provide an overload of VtblCall which can generically call the majority
of methods used by SWT. Provide a way for interfaces to invoke VtblCall
without having to manually provide vtbl offsets.

Change-Id: Ide35e0e258d0903319ca4e3e9789362148480ce6
Signed-off-by: Neil Rashbrook <neil@parkwaycc.co.uk>
Signed-off-by: Lakshmi Shanmugam <lshanmug@in.ibm.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/library/xpcom.cpp b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/library/xpcom.cpp
index a8ee3cc..89c9d19 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/library/xpcom.cpp
+++ b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/library/xpcom.cpp
@@ -12,6 +12,10 @@
 #include "swt.h"
 #include "xpcom_structs.h"
 #include "xpcom_stats.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "xptinfo.h"
+#include "xptcall.h"
 
 #ifndef XPCOM_NATIVE
 #define XPCOM_NATIVE(func) Java_org_eclipse_swt_internal_mozilla_XPCOM_##func
@@ -6813,6 +6817,142 @@
 }
 #endif
 
+#if (!defined(NO__1VtblCall__II_3Ljava_lang_Object_2) && !defined(JNI64)) || (!defined(NO__1VtblCall__IJ_3Ljava_lang_Object_2) && defined(JNI64))
+#ifndef JNI64
+extern "C" JNIEXPORT jint JNICALL XPCOM_NATIVE(_1VtblCall__II_3Ljava_lang_Object_2)(JNIEnv *env, jclass that, jint fnNumber, jintLong supports, jobjectArray args);
+JNIEXPORT jint JNICALL XPCOM_NATIVE(_1VtblCall__II_3Ljava_lang_Object_2)(JNIEnv *env, jclass that, jint fnNumber, jintLong supports, jobjectArray args)
+#else
+extern "C" JNIEXPORT jint JNICALL XPCOM_NATIVE(_1VtblCall__IJ_3Ljava_lang_Object_2)(JNIEnv *env, jclass that, jint fnNumber, jintLong supports, jobjectArray args);
+JNIEXPORT jint JNICALL XPCOM_NATIVE(_1VtblCall__IJ_3Ljava_lang_Object_2)(JNIEnv *env, jclass that, jint fnNumber, jintLong supports, jobjectArray args)
+#endif
+{
+#ifndef JNI64
+	XPCOM_NATIVE_ENTER(env, that, _1VtblCall__II_3Ljava_lang_Object_2_FUNC);
+#else
+	XPCOM_NATIVE_ENTER(env, that, _1VtblCall__IJ_3Ljava_lang_Object_2_FUNC);
+#endif
+
+	jsize count = env->GetArrayLength(args);
+	nsXPTCVariant *variants = new nsXPTCVariant[count];
+	memset(variants, 0, count * sizeof(nsXPTCVariant));
+
+	for (jsize index = 0; index < count; index++) {
+		jobject arg = env->GetObjectArrayElement(args, index);
+		jclass clazz = env->GetObjectClass(arg);
+		jmethodID methodID = env->GetMethodID(env->GetObjectClass(clazz), "getName", "()Ljava/lang/String;");
+		jstring nameStr = (jstring)env->CallObjectMethod(clazz, methodID);
+		char name[20];
+		env->GetStringUTFRegion(nameStr, 0, env->GetStringLength(nameStr), name);
+
+		if (name[0] == '[') {
+			variants[index].SetPtrIsData();
+			switch (name[1]) {
+				case 'B':
+					variants[index].ptr = env->GetByteArrayElements((jbyteArray)arg, NULL);
+					variants[index].type = TD_INT8;
+					break;
+				case 'C':
+					variants[index].ptr = env->GetCharArrayElements((jcharArray)arg, NULL);
+					variants[index].type = TD_WCHAR;
+					break;
+				case 'D':
+					variants[index].ptr = env->GetDoubleArrayElements((jdoubleArray)arg, NULL);
+					variants[index].type = TD_DOUBLE;
+					break;
+				case 'F':
+					variants[index].ptr = env->GetFloatArrayElements((jfloatArray)arg, NULL);
+					variants[index].type = TD_FLOAT;
+					break;
+				case 'I':
+					variants[index].ptr = env->GetIntArrayElements((jintArray)arg, NULL);
+					variants[index].type = TD_INT32;
+					break;
+				case 'L':
+					variants[index].ptr = env->GetLongArrayElements((jlongArray)arg, NULL);
+					variants[index].type = TD_INT64;
+					break;
+				case 'S':
+					variants[index].ptr = env->GetShortArrayElements((jshortArray)arg, NULL);
+					variants[index].type = TD_INT16;
+					break;
+			}
+		} else if (strncmp(name, "java.lang.", 10) == 0) {
+			switch (name[10]) {
+				case 'B':
+					variants[index].val.i8 = env->CallByteMethod(arg, env->GetMethodID(clazz, "byteValue", "()B"));
+					variants[index].type = TD_INT8;
+					break;
+				case 'C':
+					variants[index].val.wc = env->CallCharMethod(arg, env->GetMethodID(clazz, "charValue", "()C"));
+					variants[index].type = TD_WCHAR;
+					break;
+				case 'D':
+					variants[index].val.d = env->CallDoubleMethod(arg, env->GetMethodID(clazz, "doubleValue", "()D"));
+					variants[index].type = TD_DOUBLE;
+					break;
+				case 'F':
+					variants[index].val.f = env->CallFloatMethod(arg, env->GetMethodID(clazz, "floatValue", "()F"));
+					variants[index].type = TD_FLOAT;
+					break;
+				case 'I':
+					variants[index].val.i32 = env->CallIntMethod(arg, env->GetMethodID(clazz, "intValue", "()I"));
+					variants[index].type = TD_INT32;
+					break;
+				case 'L':
+					variants[index].val.i64 = env->CallLongMethod(arg, env->GetMethodID(clazz, "longValue", "()L"));
+					variants[index].type = TD_INT64;
+					break;
+				case 'S':
+					variants[index].val.i16 = env->CallShortMethod(arg, env->GetMethodID(clazz, "shortValue", "()S"));
+					variants[index].type = TD_INT16;
+					break;
+			}
+		}
+	}
+
+	jint rc = NS_InvokeByIndex((nsISupports*)supports, fnNumber, count, variants);
+
+	for (jsize index = 0; index < count; index++) {
+		if (variants[index].IsPtrData()) {
+			jobject arg = env->GetObjectArrayElement(args, index);
+			void *ptr = variants[index].ptr;
+
+			switch (variants[index].type) {
+				case TD_INT8:
+					env->ReleaseByteArrayElements((jbyteArray)arg, (jbyte*)ptr, 0);
+					break;
+				case TD_WCHAR:
+					env->ReleaseCharArrayElements((jcharArray)arg, (jchar*)ptr, 0);
+					break;
+				case TD_DOUBLE:
+					env->ReleaseDoubleArrayElements((jdoubleArray)arg, (jdouble*)ptr, 0);
+					break;
+				case TD_FLOAT:
+					env->ReleaseFloatArrayElements((jfloatArray)arg, (jfloat*)ptr, 0);
+					break;
+				case TD_INT32:
+					env->ReleaseIntArrayElements((jintArray)arg, (jint*)ptr, 0);
+					break;
+				case TD_INT64:
+					env->ReleaseLongArrayElements((jlongArray)arg, (jlong*)ptr, 0);
+					break;
+				case TD_INT16:
+					env->ReleaseShortArrayElements((jshortArray)arg, (jshort*)ptr, 0);
+					break;
+			}
+		}
+	}
+	delete[] variants;
+
+#ifndef JNI64
+	XPCOM_NATIVE_EXIT(env, that, _1VtblCall__II_3Ljava_lang_Object_2_FUNC);
+#else
+	XPCOM_NATIVE_EXIT(env, that, _1VtblCall__IJ_3Ljava_lang_Object_2_FUNC);
+#endif
+	return rc;
+}
+#endif
+
 #ifndef NO__1XPCOMGlueLoadXULFunctions
 extern "C" JNIEXPORT jint JNICALL XPCOM_NATIVE(_1XPCOMGlueLoadXULFunctions)(JNIEnv *env, jclass that, jintLong arg0);
 JNIEXPORT jint JNICALL XPCOM_NATIVE(_1XPCOMGlueLoadXULFunctions)
@@ -7209,4 +7349,3 @@
 	return rc;
 }
 #endif
-
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/XPCOM.java b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/XPCOM.java
index 77d3a4f..00e19a9 100755
--- a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/XPCOM.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/XPCOM.java
@@ -106,6 +106,7 @@
 	public static final String NS_FILEPICKER_CONTRACTID = "@mozilla.org/filepicker;1"; //$NON-NLS-1$
 	public static final String NS_FOCUSMANAGER_CONTRACTID = "@mozilla.org/focus-manager;1"; //$NON-NLS-1$
 	public static final String NS_HELPERAPPLAUNCHERDIALOG_CONTRACTID = "@mozilla.org/helperapplauncherdialog;1"; //$NON-NLS-1$
+	public static final String NS_INTERFACEINFOMANAGER_CONTRACTID = "@mozilla.org/xpti/interfaceinfomanager-service;1";
 	public static final String NS_MEMORY_CONTRACTID = "@mozilla.org/xpcom/memory-service;1"; //$NON-NLS-1$
 	public static final String NS_MIMEINPUTSTREAM_CONTRACTID = "@mozilla.org/network/mime-input-stream;1"; //$NON-NLS-1$
 	public static final String NS_SCRIPTSECURITYMANAGER_CONTRACTID = "@mozilla.org/scriptsecuritymanager;1"; //$NON-NLS-1$
@@ -2659,4 +2660,14 @@
 	}
 }
 
+static final native int _VtblCall(int fnNumber, long /*int*/ ppVtbl, Object[] args);
+static final int VtblCall(int fnNumber, long /*int*/ ppVtbl, Object[] args) {
+	lock.lock();
+	try {
+		return _VtblCall(fnNumber, ppVtbl, args);
+	} finally {
+		lock.unlock();
+	}
+}
+
 }
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsIInterfaceInfo.java b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsIInterfaceInfo.java
new file mode 100644
index 0000000..955b05e
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsIInterfaceInfo.java
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by Netscape are Copyright (C) 1998-1999
+ * Netscape Communications Corporation.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * IBM
+ * -  Binding to permit interfacing between Mozilla and SWT
+ * -  Copyright (C) 2003, 2012 IBM Corp.  All Rights Reserved.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.eclipse.swt.internal.mozilla;
+
+
+public class nsIInterfaceInfo extends nsISupports {
+
+	static final int LAST_METHOD_ID = nsISupports.LAST_METHOD_ID + ((IsXULRVersionOrLater (MozillaVersion.VERSION_XR10)) ? 21 : 20);
+	
+	static final String NS_IINTERFACEINFO_IID_STR = "215dbe04-94a7-11d2-ba58-00805f8a5dd7";
+	static final String NS_IINTERFACEINFO_10_IID_STR = "1affa260-8965-4612-869a-78af4ccfb287";
+
+	static {
+		IIDStore.RegisterIID(nsIDocShell.class, MozillaVersion.VERSION_BASE, new nsID(NS_IINTERFACEINFO_IID_STR));
+		IIDStore.RegisterIID(nsIDocShell.class, MozillaVersion.VERSION_XR10, new nsID(NS_IINTERFACEINFO_10_IID_STR));
+	}
+
+	public nsIInterfaceInfo(long /*int*/ address) {
+		super(address);
+	}
+
+	public int GetMethodInfoForName(byte[] methodName, int [] index, long /*int*/[] result) {
+		return XPCOM.VtblCall(nsISupports.LAST_METHOD_ID + ((IsXULRVersionOrLater (MozillaVersion.VERSION_XR10)) ? 9 : 8), getAddress(), methodName, index, result);
+	}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsIInterfaceInfoManager.java b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsIInterfaceInfoManager.java
new file mode 100644
index 0000000..b74b174
--- /dev/null
+++ b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsIInterfaceInfoManager.java
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Communicator client code, released March 31, 1998.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by Netscape are Copyright (C) 1998-1999
+ * Netscape Communications Corporation.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * IBM
+ * -  Binding to permit interfacing between Mozilla and SWT
+ * -  Copyright (C) 2003, 2012 IBM Corp.  All Rights Reserved.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package org.eclipse.swt.internal.mozilla;
+
+
+public class nsIInterfaceInfoManager extends nsISupports {
+
+	static final int LAST_METHOD_ID = nsISupports.LAST_METHOD_ID + 6;
+	static final String NS_IINTERFACEINFOMANAGER_IID_STR = "1d53d8d9-1d92-428f-b5cc-198b55e897d7";
+
+	static {
+		IIDStore.RegisterIID(nsIInterfaceInfoManager.class, MozillaVersion.VERSION_BASE, new nsID(NS_IINTERFACEINFOMANAGER_IID_STR));
+	}
+
+	public nsIInterfaceInfoManager(long /*int*/ address) {
+		super(address);
+	}
+
+	public int GetInfoForIID(nsID iid, long /*int*/[] result) {
+		return XPCOM.VtblCall(nsISupports.LAST_METHOD_ID + 1, getAddress(), iid, result);
+	}
+
+	public int GetInfoForName(byte[] aName, long /*int*/[] result) {
+		return XPCOM.VtblCall(nsISupports.LAST_METHOD_ID + 2, getAddress(), aName, result);
+	}
+}
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsISupports.java b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsISupports.java
index a7482f0..07f6f0b 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsISupports.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Mozilla/common/org/eclipse/swt/internal/mozilla/nsISupports.java
@@ -50,6 +50,10 @@
 	protected static boolean IsXULRunner31 () {
 		return MozillaVersion.CheckVersion (MozillaVersion.VERSION_XR31, true);
 	}
+	
+	protected static boolean IsXULRVersionOrLater (int version) {
+		return MozillaVersion.CheckVersion (version, false);
+	}
 
 	public static final String NS_ISUPPORTS_IID_STR = "00000000-0000-0000-c000-000000000046";
 	
@@ -57,6 +61,55 @@
 		IIDStore.RegisterIID (nsISupports.class, MozillaVersion.VERSION_BASE, new nsID (NS_ISUPPORTS_IID_STR));	
 	}
 
+	private static byte[] toByteArray (String str) {
+		byte[] bytes = new byte[str.length() + 1];
+		for (int i = str.length (); i-- > 0; )
+			bytes[i] = (byte)str.charAt (i);
+		return bytes;
+	}
+
+	protected int callFunction (String methodString, boolean setter, Object[] args) {
+		int index = getMethodIndex (methodString);
+		if (setter) index++;
+		return XPCOM.VtblCall(index, getAddress(), args);
+	}
+	
+	protected int getGetterIndex (String attribute) {
+		return getMethodIndex (attribute);
+	}
+
+	protected int getSetterIndex (String attribute) {
+		return getMethodIndex (attribute) + 1;
+	}
+	
+	protected int getMethodIndex (String methodString) {
+		long /*int*/[] result = new long /*int*/[1];
+		result[0] = 0;
+		int rc = XPCOM.NS_GetServiceManager (result);
+		if (rc != XPCOM.NS_OK) return -1;
+
+		nsIServiceManager serviceManager = new nsIServiceManager (result[0]);
+		result[0] = 0;
+		rc = serviceManager.GetServiceByContractID (toByteArray (XPCOM.NS_INTERFACEINFOMANAGER_CONTRACTID), IIDStore.GetIID (nsIInterfaceInfoManager.class), result);
+		serviceManager.Release ();
+		if (rc != XPCOM.NS_OK) return -1;
+
+		nsIInterfaceInfoManager iim = new nsIInterfaceInfoManager (result[0]);
+		result[0] = 0;
+		rc = iim.GetInfoForName (toByteArray (getClass ().getSimpleName ()), result);
+		iim.Release ();
+		if (rc != XPCOM.NS_OK) return -1;
+
+		nsIInterfaceInfo info = new nsIInterfaceInfo (result[0]);
+		int[] index = new int [1];
+		long /*int*/[] dummy = new long /*int*/[1];
+		rc = info.GetMethodInfoForName (toByteArray (methodString), index, dummy);
+		info.Release ();
+		if (rc != XPCOM.NS_OK) return -1;
+		
+		return index[0];
+	}
+
 	long /*int*/ address;
 
 	public nsISupports(long /*int*/ address) {