157 lines
5.8 KiB
Java
157 lines
5.8 KiB
Java
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0
|
|
*
|
|
* 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 Rhino code, released
|
|
* May 6, 1999.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1997-1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Igor Bukanov
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* the GNU General Public License Version 2 or later (the "GPL"), in which
|
|
* case the provisions of the GPL are applicable instead of those above. If
|
|
* you wish to allow use of your version of this file only under the terms of
|
|
* the GPL and not to allow others to use your version of this file under the
|
|
* MPL, indicate your decision by deleting the provisions above and replacing
|
|
* them with the notice and other provisions required by the GPL. If you do
|
|
* not delete the provisions above, a recipient may use your version of this
|
|
* file under either the MPL or the GPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
package org.mozilla.javascript;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
/**
|
|
* Adapter to use JS function as implementation of Java interfaces with
|
|
* single method or multiple methods with the same signature.
|
|
*/
|
|
public class InterfaceAdapter
|
|
{
|
|
private final Object proxyHelper;
|
|
|
|
/**
|
|
* Make glue object implementing interface cl that will
|
|
* call the supplied JS function when called.
|
|
* Only interfaces were all methods have the same signature is supported.
|
|
*
|
|
* @return The glue object or null if <tt>cl</tt> is not interface or
|
|
* has methods with different signatures.
|
|
*/
|
|
static Object create(Context cx, Class<?> cl, Callable function)
|
|
{
|
|
if (!cl.isInterface()) throw new IllegalArgumentException();
|
|
|
|
Scriptable topScope = ScriptRuntime.getTopCallScope(cx);
|
|
ClassCache cache = ClassCache.get(topScope);
|
|
InterfaceAdapter adapter;
|
|
adapter = (InterfaceAdapter)cache.getInterfaceAdapter(cl);
|
|
ContextFactory cf = cx.getFactory();
|
|
if (adapter == null) {
|
|
Method[] methods = cl.getMethods();
|
|
if (methods.length == 0) {
|
|
throw Context.reportRuntimeError2(
|
|
"msg.no.empty.interface.conversion",
|
|
String.valueOf(function),
|
|
cl.getClass().getName());
|
|
}
|
|
boolean canCallFunction = false;
|
|
canCallFunctionChecks: {
|
|
Class<?>[] argTypes = methods[0].getParameterTypes();
|
|
// check that the rest of methods has the same signature
|
|
for (int i = 1; i != methods.length; ++i) {
|
|
Class<?>[] types2 = methods[i].getParameterTypes();
|
|
if (types2.length != argTypes.length) {
|
|
break canCallFunctionChecks;
|
|
}
|
|
for (int j = 0; j != argTypes.length; ++j) {
|
|
if (types2[j] != argTypes[j]) {
|
|
break canCallFunctionChecks;
|
|
}
|
|
}
|
|
}
|
|
canCallFunction= true;
|
|
}
|
|
if (!canCallFunction) {
|
|
throw Context.reportRuntimeError2(
|
|
"msg.no.function.interface.conversion",
|
|
String.valueOf(function),
|
|
cl.getClass().getName());
|
|
}
|
|
adapter = new InterfaceAdapter(cf, cl);
|
|
cache.cacheInterfaceAdapter(cl, adapter);
|
|
}
|
|
return VMBridge.instance.newInterfaceProxy(
|
|
adapter.proxyHelper, cf, adapter, function, topScope);
|
|
}
|
|
|
|
private InterfaceAdapter(ContextFactory cf, Class<?> cl)
|
|
{
|
|
this.proxyHelper
|
|
= VMBridge.instance.getInterfaceProxyHelper(
|
|
cf, new Class[] { cl });
|
|
}
|
|
|
|
public Object invoke(ContextFactory cf,
|
|
final Object target,
|
|
final Scriptable topScope,
|
|
final Method method,
|
|
final Object[] args)
|
|
{
|
|
ContextAction action = new ContextAction() {
|
|
public Object run(Context cx)
|
|
{
|
|
return invokeImpl(cx, target, topScope, method, args);
|
|
}
|
|
};
|
|
return cf.call(action);
|
|
}
|
|
|
|
Object invokeImpl(Context cx,
|
|
Object target,
|
|
Scriptable topScope,
|
|
Method method,
|
|
Object[] args)
|
|
{
|
|
int N = (args == null) ? 0 : args.length;
|
|
|
|
Callable function = (Callable)target;
|
|
Scriptable thisObj = topScope;
|
|
Object[] jsargs = new Object[N + 1];
|
|
jsargs[N] = method.getName();
|
|
if (N != 0) {
|
|
WrapFactory wf = cx.getWrapFactory();
|
|
for (int i = 0; i != N; ++i) {
|
|
jsargs[i] = wf.wrap(cx, topScope, args[i], null);
|
|
}
|
|
}
|
|
|
|
Object result = function.call(cx, topScope, thisObj, jsargs);
|
|
Class<?> javaResultType = method.getReturnType();
|
|
if (javaResultType == Void.TYPE) {
|
|
result = null;
|
|
} else {
|
|
result = Context.jsToJava(result, javaResultType);
|
|
}
|
|
return result;
|
|
}
|
|
}
|