blob: 6b4abb40b99a84b0f78930d9cf53815839c969e9 [file] [log] [blame]
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.shell.ie;
import com.google.gwt.dev.shell.CompilingClassLoader;
import com.google.gwt.dev.shell.ModuleSpace;
import com.google.gwt.dev.util.log.AbstractTreeLogger;
import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.internal.ole.win32.COMObject;
import org.eclipse.swt.internal.ole.win32.DISPPARAMS;
import org.eclipse.swt.internal.ole.win32.GUID;
import org.eclipse.swt.internal.win32.OS;
import org.eclipse.swt.ole.win32.OleAutomation;
import org.eclipse.swt.ole.win32.Variant;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Basic IDispatch implementation for use by
* {@link com.google.gwt.shell.ie.IDispatchProxy} and
* {@link com.google.gwt.shell.ie.IDispatchStatic}.
*/
abstract class IDispatchImpl extends COMObject {
/**
* An exception for wrapping bad HR's.
*/
protected static class HResultException extends Exception {
private int hr;
private String source;
/**
* Constructs a standard bad HR exception.
*/
public HResultException(int hr) {
super(Integer.toString(hr));
this.hr = hr;
source = "Java";
}
/**
* Constructs a DISP_E_EXCEPTION bad HR.
*/
public HResultException(String message) {
super(message);
hr = COM.DISP_E_EXCEPTION;
source = "Java";
}
/**
* Constructs a DISP_E_EXCEPTION bad HR.
*/
public HResultException(Throwable e) {
super(AbstractTreeLogger.getStackTraceAsString(e), e);
hr = COM.DISP_E_EXCEPTION;
source = "Java";
}
/**
* If the HR is DISP_E_EXCEPTION, this method will fill in the EXCEPINFO
* structure. Otherwise, it does nothing.
*/
public void fillExcepInfo(int pExcepInfo) {
if (hr == COM.DISP_E_EXCEPTION) {
SwtOleGlue.setEXCEPINFO(pExcepInfo, hr, source, getMessage(), 0);
}
}
/**
* Gets the HR.
*/
public int getHResult() {
return hr;
}
}
// This one isn't defined in SWT for some reason.
protected static final int DISP_E_UNKNOWNNAME = 0x80020006;
protected static Variant callMethod(CompilingClassLoader cl, Object jthis,
Variant[] params, Method method) throws InvocationTargetException,
HResultException {
Object[] javaParams = SwtOleGlue.convertVariantsToObjects(
method.getParameterTypes(), params, "Calling method '"
+ method.getName() + "'");
Object result = null;
try {
try {
result = method.invoke(jthis, javaParams);
} catch (IllegalAccessException e) {
// should never, ever happen
e.printStackTrace();
throw new RuntimeException(e);
}
} catch (NullPointerException e) {
/*
* The JavaScript expected the method to be static, having forgotten an
* instance reference (most often "this.").
*/
StringBuffer sb = new StringBuffer();
sb.append("Instance method '");
sb.append(method.getName());
sb.append("' needed a qualifying instance ");
sb.append("(did you forget to prefix the call with 'this.'?)");
throw new HResultException(sb.toString());
} finally {
for (int i = 0; i < javaParams.length; i++) {
if (javaParams[i] instanceof OleAutomation) {
OleAutomation tmp = (OleAutomation) javaParams[i];
tmp.dispose();
}
}
}
// Convert it to a variant (if the return type is void, return
// a VT_EMPTY variant -- 'undefined' in JavaScript).
//
Class returnType = method.getReturnType();
if (returnType.equals(Void.TYPE)) {
return new Variant();
}
return SwtOleGlue.convertObjectToVariant(cl, returnType, result);
}
protected int refCount;
public IDispatchImpl() {
super(new int[] {2, 0, 0, 1, 3, 5, 8});
}
// CHECKSTYLE_OFF
public int AddRef() {
return ++refCount;
}
// CHECKSTYLE_ON
public int method0(int[] args) {
return QueryInterface(args[0], args[1]);
}
public int method1(int[] args) {
return AddRef();
}
// method3 GetTypeInfoCount - not implemented
// method4 GetTypeInfo - not implemented
public int method2(int[] args) {
return Release();
}
public int method5(int[] args) {
return GetIDsOfNames(args[0], args[1], args[2], args[3], args[4]);
}
public int method6(int[] args) {
return Invoke(args[0], args[1], args[2], args[3], args[4], args[5],
args[6], args[7]);
}
// CHECKSTYLE_OFF
public int QueryInterface(int riid, int ppvObject) {
if (riid == 0 || ppvObject == 0) {
return COM.E_NOINTERFACE;
}
GUID guid = new GUID();
COM.MoveMemory(guid, riid, GUID.sizeof);
if (COM.IsEqualGUID(guid, COM.IIDIUnknown)) {
COM.MoveMemory(ppvObject, new int[] {getAddress()}, 4);
AddRef();
return COM.S_OK;
}
if (COM.IsEqualGUID(guid, COM.IIDIDispatch)) {
COM.MoveMemory(ppvObject, new int[] {getAddress()}, 4);
AddRef();
return COM.S_OK;
}
COM.MoveMemory(ppvObject, new int[] {0}, 4);
return COM.E_NOINTERFACE;
}
public int Release() {
if (--refCount == 0) {
dispose();
}
return refCount;
}
// CHECKSTYLE_ON
/**
* Override this method to implement GetIDsOfNames().
*/
protected abstract void getIDsOfNames(String[] names, int[] ids)
throws HResultException;
/**
* Override this method to implement Invoke().
*/
protected abstract Variant invoke(int dispId, int flags, Variant[] params)
throws HResultException, InvocationTargetException;
private Variant[] extractVariantArrayFromDispParamsPtr(int pDispParams) {
DISPPARAMS dispParams = new DISPPARAMS();
COM.MoveMemory(dispParams, pDispParams, DISPPARAMS.sizeof);
Variant[] variants = new Variant[dispParams.cArgs];
// Reverse the order as we pull the variants in.
for (int i = 0, n = dispParams.cArgs; i < n; ++i) {
int varArgAddr = dispParams.rgvarg + Variant.sizeof * i;
variants[n - i - 1] = Variant.win32_new(varArgAddr);
}
return variants;
}
// CHECKSTYLE_OFF
private final int GetIDsOfNames(int riid, int rgszNames, int cNames,
int lcid, int rgDispId) {
try {
if (cNames < 1) {
return COM.E_INVALIDARG;
}
// Extract the requested names and build an answer array init'ed with -1.
//
String[] names = SwtOleGlue.extractStringArrayFromOleCharPtrPtr(
rgszNames, cNames);
int[] ids = new int[names.length];
Arrays.fill(ids, -1);
getIDsOfNames(names, ids);
OS.MoveMemory(rgDispId, ids, ids.length * 4);
} catch (HResultException e) {
return e.getHResult();
} catch (Throwable e) {
e.printStackTrace();
return COM.E_FAIL;
}
return COM.S_OK;
}
private int Invoke(int dispIdMember, int riid, int lcid, int dwFlags,
int pDispParams, int pVarResult, int pExcepInfo, int pArgErr) {
HResultException ex = null;
Variant[] vArgs = null;
Variant result = null;
try {
vArgs = extractVariantArrayFromDispParamsPtr(pDispParams);
result = invoke(dispIdMember, dwFlags, vArgs);
if (pVarResult != 0) {
Variant.win32_copy(pVarResult, result);
}
} catch (HResultException e) {
// Log to the console for detailed examination.
//
e.printStackTrace();
ex = e;
} catch (InvocationTargetException e) {
// If we get here, it means an exception is being thrown from
// Java back into JavaScript
Throwable t = e.getTargetException();
RuntimeException re;
if (t instanceof RuntimeException) {
re = (RuntimeException) t;
} else {
re = new RuntimeException("Checked exception thrown into JavaScript"
+ " (web mode behavior may differ)", t);
}
ex = new HResultException(re);
ModuleSpace.setThrownJavaException(re);
} catch (Exception e) {
// Log to the console for detailed examination.
//
e.printStackTrace();
ex = new HResultException(e);
} finally {
// We allocated variants for all arguments, so we must dispose them all.
//
for (int i = 0; i < vArgs.length; ++i) {
if (vArgs[i] != null) {
vArgs[i].dispose();
}
}
if (result != null) {
result.dispose();
}
}
if (ex != null) {
// Set up an exception for IE to throw.
//
ex.fillExcepInfo(pExcepInfo);
return ex.getHResult();
}
return COM.S_OK;
}
// CHECKSTYLE_ON
}