From a636bb70c20277228468345017a2f8d42d39857c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Leif=20=C3=85strand?= Date: Fri, 15 Nov 2013 00:13:13 +0200 Subject: [PATCH] Make Invoker support non-public methods (#12959) Change-Id: Ie449489f3c9222bbe4a4221841c4ebc20693f969 --- .../ConnectorBundleLoaderFactory.java | 164 ++++++++++++++---- .../vaadin/client/metadata/JsniInvoker.java | 53 ++++++ 2 files changed, 186 insertions(+), 31 deletions(-) create mode 100644 client/src/com/vaadin/client/metadata/JsniInvoker.java diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java index 1c06cea3fa..edbf5e260c 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java +++ b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java @@ -38,11 +38,13 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JMethod; import com.google.gwt.core.ext.typeinfo.JParameterizedType; +import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.NotFoundException; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.client.JsArrayObject; import com.vaadin.client.ServerConnector; import com.vaadin.client.metadata.ConnectorBundleLoader; import com.vaadin.client.metadata.InvokationHandler; @@ -449,7 +451,7 @@ public class ConnectorBundleLoaderFactory extends Generator { writeIdentifiers(w, bundle); writeGwtConstructors(w, bundle); writeReturnTypes(w, bundle); - writeInvokers(w, bundle); + writeInvokers(logger, w, bundle); writeParamTypes(w, bundle); writeProxys(w, bundle); writeDelayedInfo(w, bundle); @@ -700,57 +702,157 @@ public class ConnectorBundleLoaderFactory extends Generator { } } - private void writeInvokers(SplittingSourceWriter w, ConnectorBundle bundle) { + private void writeInvokers(TreeLogger logger, SplittingSourceWriter w, + ConnectorBundle bundle) throws UnableToCompleteException { Map> needsInvoker = bundle.getNeedsInvoker(); for (Entry> entry : needsInvoker.entrySet()) { JClassType type = entry.getKey(); + TreeLogger typeLogger = logger.branch(Type.DEBUG, + "Creating invokers for " + type); + Set methods = entry.getValue(); for (JMethod method : methods) { w.print("store.setInvoker("); writeClassLiteral(w, type); w.print(", \""); w.print(escape(method.getName())); - w.println("\", new Invoker() {"); - w.indent(); + w.print("\","); - w.println("public Object invoke(Object target, Object[] params) {"); - w.indent(); + if (method.isPublic()) { + typeLogger.log(Type.DEBUG, "Invoking " + method.getName() + + " using java"); - JType returnType = method.getReturnType(); - boolean hasReturnType = !"void".equals(returnType - .getQualifiedSourceName()); - if (hasReturnType) { - w.print("return "); + writeJavaInvoker(w, type, method); + } else { + TreeLogger methodLogger = typeLogger.branch(Type.DEBUG, + "Invoking " + method.getName() + " using jsni"); + // Must use JSNI to access non-public methods + writeJsniInvoker(methodLogger, w, type, method); } - JType[] parameterTypes = method.getParameterTypes(); - - w.print("((" + type.getQualifiedSourceName() + ") target)." - + method.getName() + "("); - for (int i = 0; i < parameterTypes.length; i++) { - JType parameterType = parameterTypes[i]; - if (i != 0) { - w.print(", "); - } - String parameterTypeName = getBoxedTypeName(parameterType); - w.print("(" + parameterTypeName + ") params[" + i + "]"); - } w.println(");"); - if (!hasReturnType) { - w.println("return null;"); - } + w.splitIfNeeded(); + } + } + } - w.outdent(); - w.println("}"); + private void writeJsniInvoker(TreeLogger logger, SplittingSourceWriter w, + JClassType type, JMethod method) throws UnableToCompleteException { + w.println("new JsniInvoker() {"); + w.indent(); - w.outdent(); - w.println("});"); + w.println( + "protected native Object jsniInvoke(Object target, %s params) /*-{ ", + JsArrayObject.class.getName()); + w.indent(); - w.splitIfNeeded(); + JType returnType = method.getReturnType(); + boolean hasReturnType = !"void".equals(returnType + .getQualifiedSourceName()); + + // Note that void is also a primitive type + boolean hasPrimitiveReturnType = hasReturnType + && returnType.isPrimitive() != null; + + if (hasReturnType) { + w.print("return "); + + if (hasPrimitiveReturnType) { + // Integer.valueOf(expression); + w.print("@%s::valueOf(%s)(", returnType.isPrimitive() + .getQualifiedBoxedSourceName(), returnType + .getJNISignature()); + + // Implementation tested briefly, but I don't dare leave it + // enabled since we are not using it in the framework and we + // have not tests for it. + logger.log(Type.ERROR, + "JSNI invocation is not yet supported for methods with " + + "primitive return types. Change your method " + + "to public to be able to use conventional" + + " Java invoking instead."); + throw new UnableToCompleteException(); + } + } + + JType[] parameterTypes = method.getParameterTypes(); + + w.print("target.@%s::" + method.getName() + "(*)(", method + .getEnclosingType().getQualifiedSourceName()); + for (int i = 0; i < parameterTypes.length; i++) { + if (i != 0) { + w.print(", "); + } + + w.print("params[" + i + "]"); + + JPrimitiveType primitive = parameterTypes[i].isPrimitive(); + if (primitive != null) { + // param.intValue(); + w.print(".@%s::%sValue()()", + primitive.getQualifiedBoxedSourceName(), + primitive.getQualifiedSourceName()); } } + + if (hasPrimitiveReturnType) { + assert hasReturnType; + w.print(")"); + } + + w.println(");"); + + if (!hasReturnType) { + w.println("return null;"); + } + + w.outdent(); + w.println("}-*/;"); + + w.outdent(); + w.print("}"); + } + + private void writeJavaInvoker(SplittingSourceWriter w, JClassType type, + JMethod method) { + w.println("new Invoker() {"); + w.indent(); + + w.println("public Object invoke(Object target, Object[] params) {"); + w.indent(); + + JType returnType = method.getReturnType(); + boolean hasReturnType = !"void".equals(returnType + .getQualifiedSourceName()); + if (hasReturnType) { + w.print("return "); + } + + JType[] parameterTypes = method.getParameterTypes(); + + w.print("((" + type.getQualifiedSourceName() + ") target)." + + method.getName() + "("); + for (int i = 0; i < parameterTypes.length; i++) { + JType parameterType = parameterTypes[i]; + if (i != 0) { + w.print(", "); + } + String parameterTypeName = getBoxedTypeName(parameterType); + w.print("(" + parameterTypeName + ") params[" + i + "]"); + } + w.println(");"); + + if (!hasReturnType) { + w.println("return null;"); + } + + w.outdent(); + w.println("}"); + + w.outdent(); + w.print("}"); } private void writeReturnTypes(SplittingSourceWriter w, diff --git a/client/src/com/vaadin/client/metadata/JsniInvoker.java b/client/src/com/vaadin/client/metadata/JsniInvoker.java new file mode 100644 index 0000000000..4692a18cfe --- /dev/null +++ b/client/src/com/vaadin/client/metadata/JsniInvoker.java @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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.vaadin.client.metadata; + +import com.google.gwt.core.client.JavaScriptObject; +import com.vaadin.client.JsArrayObject; + +/** + * Special {@link Invoker} that uses JSNI to invoke methods with limited + * visibility. + * + * @since 7.2 + * @author Vaadin Ltd + */ +public abstract class JsniInvoker implements Invoker { + + @Override + public Object invoke(Object target, Object... params) { + JsArrayObject jsParams = JavaScriptObject.createArray().cast(); + for (Object object : params) { + jsParams.add(object); + } + return jsniInvoke(target, jsParams); + } + + /** + * Abstract method that will be generated to contain JSNI for invoking the + * actual method. + * + * @param target + * the object upon which to invoke the method + * @param params + * a js array with arguments to pass to the method + * @return the value returned by the invoked method, or null if + * the target method return type is void. + */ + protected abstract Object jsniInvoke(Object target, + JsArrayObject params); + +} -- 2.39.5