]> source.dussan.org Git - vaadin-framework.git/commitdiff
Make Invoker support non-public methods (#12959)
authorLeif Åstrand <leif@vaadin.com>
Thu, 14 Nov 2013 22:13:13 +0000 (00:13 +0200)
committerVaadin Code Review <review@vaadin.com>
Tue, 1 Apr 2014 08:15:56 +0000 (08:15 +0000)
Change-Id: Ie449489f3c9222bbe4a4221841c4ebc20693f969

client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java
client/src/com/vaadin/client/metadata/JsniInvoker.java [new file with mode: 0644]

index 1c06cea3fadb35c2f8ca730072cec8a9795a4bcb..edbf5e260c09b16d904513cb3e3c78f6ad17d915 100644 (file)
@@ -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<JClassType, Set<JMethod>> needsInvoker = bundle.getNeedsInvoker();
         for (Entry<JClassType, Set<JMethod>> entry : needsInvoker.entrySet()) {
             JClassType type = entry.getKey();
 
+            TreeLogger typeLogger = logger.branch(Type.DEBUG,
+                    "Creating invokers for " + type);
+
             Set<JMethod> 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<Object> 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 (file)
index 0000000..4692a18
--- /dev/null
@@ -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<Object> 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 <code>null</code> if
+     *         the target method return type is <code>void</code>.
+     */
+    protected abstract Object jsniInvoke(Object target,
+            JsArrayObject<Object> params);
+
+}