8144519: Add a sample for pluggable dynalink linker that demonstrats beans linker delegation
Reviewed-by: mhaupt, jlaskey
--- a/nashorn/samples/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter Wed Dec 02 09:37:26 2015 -0800
+++ b/nashorn/samples/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter Thu Dec 03 19:04:39 2015 +0530
@@ -1,2 +1,3 @@
DOMLinkerExporter
UnderscoreNameLinkerExporter
+MissingMethodLinkerExporter
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/MissingMethodExample.java Thu Dec 03 19:04:39 2015 +0530
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.util.ArrayList;
+
+// This is an example class that implements MissingMethodHandler
+// to receive "doesNotUnderstand" calls on 'missing methods'
+public class MissingMethodExample extends ArrayList
+ implements MissingMethodHandler {
+
+ @Override
+ public Object doesNotUnderstand(String name, Object... args) {
+ // This dummy doesNotUnderstand just prints method name and args.
+ // You can put useful method routing logic here.
+ System.out.println("you called " + name);
+ if (args.length != 0) {
+ System.out.println("arguments are: ");
+ for (Object arg : args) {
+ System.out.println(" " + arg);
+ }
+ }
+ return this;
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/MissingMethodHandler.java Thu Dec 03 19:04:39 2015 +0530
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// interface to be implemented by any Java class that wants
+// to trap "missing method" calls.
+public interface MissingMethodHandler {
+ // name is "missing" method name. "args" is the array of arguments passed
+ public Object doesNotUnderstand(String name, Object... args);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/MissingMethodLinkerExporter.java Thu Dec 03 19:04:39 2015 +0530
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.ArrayList;
+import java.util.List;
+import jdk.dynalink.CallSiteDescriptor;
+import jdk.dynalink.CompositeOperation;
+import jdk.dynalink.NamedOperation;
+import jdk.dynalink.Operation;
+import jdk.dynalink.StandardOperation;
+import jdk.dynalink.beans.BeansLinker;
+import jdk.dynalink.linker.GuardingDynamicLinker;
+import jdk.dynalink.linker.GuardingDynamicLinkerExporter;
+import jdk.dynalink.linker.GuardedInvocation;
+import jdk.dynalink.linker.TypeBasedGuardingDynamicLinker;
+import jdk.dynalink.linker.LinkRequest;
+import jdk.dynalink.linker.LinkerServices;
+import jdk.dynalink.linker.support.Guards;
+import jdk.dynalink.linker.support.Lookup;
+
+/**
+ * This is a dynalink pluggable linker (see http://openjdk.java.net/jeps/276).
+ * This linker routes missing methods to Smalltalk-style doesNotUnderstand method.
+ * Object of any Java class that implements MissingMethodHandler is handled by this linker.
+ * For any method call, if a matching Java method is found, it is called. If there is no
+ * method by that name, then MissingMethodHandler.doesNotUnderstand is called.
+ */
+public final class MissingMethodLinkerExporter extends GuardingDynamicLinkerExporter {
+ static {
+ System.out.println("pluggable dynalink missing method linker loaded");
+ }
+
+ // represents a MissingMethod - just stores as name and also serves a guard type
+ public static class MissingMethod {
+ private final String name;
+
+ public MissingMethod(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ // MissingMethodHandler.doesNotUnderstand method
+ private static final MethodHandle DOES_NOT_UNDERSTAND;
+
+ // type of MissingMethodHandler - but "this" and String args are flipped
+ private static final MethodType FLIPPED_DOES_NOT_UNDERSTAND_TYPE;
+
+ // "is this a MissingMethod?" guard
+ private static final MethodHandle IS_MISSING_METHOD;
+
+ // MissingMethod object->it's name filter
+ private static final MethodHandle MISSING_METHOD_TO_NAME;
+
+ static {
+ DOES_NOT_UNDERSTAND = Lookup.PUBLIC.findVirtual(
+ MissingMethodHandler.class,
+ "doesNotUnderstand",
+ MethodType.methodType(Object.class, String.class, Object[].class));
+ FLIPPED_DOES_NOT_UNDERSTAND_TYPE =
+ MethodType.methodType(Object.class, String.class, MissingMethodHandler.class, Object[].class);
+ IS_MISSING_METHOD = Guards.isOfClass(MissingMethod.class,
+ MethodType.methodType(Boolean.TYPE, Object.class));
+ MISSING_METHOD_TO_NAME = Lookup.PUBLIC.findVirtual(MissingMethod.class,
+ "getName", MethodType.methodType(String.class));
+ }
+
+ // locate the first standard operation from the call descriptor
+ private static StandardOperation getFirstStandardOperation(final CallSiteDescriptor desc) {
+ final Operation base = NamedOperation.getBaseOperation(desc.getOperation());
+ if (base instanceof StandardOperation) {
+ return (StandardOperation)base;
+ } else if (base instanceof CompositeOperation) {
+ final CompositeOperation cop = (CompositeOperation)base;
+ for(int i = 0; i < cop.getOperationCount(); ++i) {
+ final Operation op = cop.getOperation(i);
+ if (op instanceof StandardOperation) {
+ return (StandardOperation)op;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public List<GuardingDynamicLinker> get() {
+ final ArrayList<GuardingDynamicLinker> linkers = new ArrayList<>();
+ final BeansLinker beansLinker = new BeansLinker();
+ linkers.add(new TypeBasedGuardingDynamicLinker() {
+ // only handles MissingMethodHandler and MissingMethod objects
+ @Override
+ public boolean canLinkType(final Class<?> type) {
+ return
+ MissingMethodHandler.class.isAssignableFrom(type) ||
+ type == MissingMethod.class;
+ }
+
+ @Override
+ public GuardedInvocation getGuardedInvocation(LinkRequest request,
+ LinkerServices linkerServices) throws Exception {
+ final Object self = request.getReceiver();
+ CallSiteDescriptor desc = request.getCallSiteDescriptor();
+
+ // any method call is done by two steps. Step (1) GET_METHOD and (2) is CALL
+ // For step (1), we check if GET_METHOD can succeed by Java linker, if so
+ // we return that method object. If not, we return a MissingMethod object.
+ if (self instanceof MissingMethodHandler) {
+ // Check if this is a named GET_METHOD first.
+ boolean isGetMethod = getFirstStandardOperation(desc) == StandardOperation.GET_METHOD;
+ Object name = NamedOperation.getName(desc.getOperation());
+ if (isGetMethod && name instanceof String) {
+ GuardingDynamicLinker javaLinker = beansLinker.getLinkerForClass(self.getClass());
+ GuardedInvocation inv;
+ try {
+ inv = javaLinker.getGuardedInvocation(request, linkerServices);
+ } catch (final Throwable th) {
+ inv = null;
+ }
+
+ String nameStr = name.toString();
+ if (inv == null) {
+ // use "this" for just guard and drop it -- return a constant Method handle
+ // that returns a newly created MissingMethod object
+ MethodHandle mh = MethodHandles.constant(Object.class, new MissingMethod(nameStr));
+ inv = new GuardedInvocation(
+ MethodHandles.dropArguments(mh, 0, Object.class),
+ Guards.isOfClass(self.getClass(), MethodType.methodType(Boolean.TYPE, Object.class)));
+ }
+
+ return inv;
+ }
+ } else if (self instanceof MissingMethod) {
+ // This is step (2). We call MissingMethodHandler.doesNotUnderstand here
+ // Check if this is this a CALL first.
+ boolean isCall = getFirstStandardOperation(desc) == StandardOperation.CALL;
+ if (isCall) {
+ MethodHandle mh = DOES_NOT_UNDERSTAND;
+
+ // flip "this" and method name (String)
+ mh = MethodHandles.permuteArguments(mh, FLIPPED_DOES_NOT_UNDERSTAND_TYPE, 1, 0, 2);
+
+ // collect rest of the arguments as vararg
+ mh = mh.asCollector(Object[].class, desc.getMethodType().parameterCount() - 2);
+
+ // convert MissingMethod object to it's name
+ mh = MethodHandles.filterArguments(mh, 0, MISSING_METHOD_TO_NAME);
+ return new GuardedInvocation(mh, IS_MISSING_METHOD);
+ }
+ }
+
+ return null;
+ }
+ });
+ return linkers;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/missing_method.js Thu Dec 03 19:04:39 2015 +0530
@@ -0,0 +1,51 @@
+# Usage: jjs -cp missing_method_linker.js missing_method.js
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// MissingMethodExample class extends ArrayList and
+// implements MissingMethodHandler interface
+var MME = Java.type("MissingMethodExample")
+
+var obj = new MME()
+
+// can call any ArrayList method
+obj.add("hello")
+obj.add("world")
+print(obj.size())
+print(obj)
+
+// "foo", "bar" are 'missing' methods
+// these will result in doesNotUnderstand calls
+obj.foo(1, "www", true)
+obj.bar("http", "dynalink")
+
+// another valid ArrayList method call
+obj.forEach(print)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/samples/missing_method_linker.js Thu Dec 03 19:04:39 2015 +0530
@@ -0,0 +1,49 @@
+#! missing method linker example
+
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of Oracle nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// This script assumes you've built jdk9 or using latest
+// jdk9 image and put the 'bin' directory in the PATH
+
+$EXEC.throwOnError=true
+
+// compile MissingMethodLinkerExporter
+`javac -cp ../dist/nashorn.jar MissingMethodLinkerExporter.java MissingMethodHandler.java MissingMethodExample.java`
+
+// make a jar file out of pluggable linker
+`jar cvf missing_method_linker.jar MissingMethod*.class META-INF/`
+
+// run a sample script that uses pluggable linker
+// but make sure classpath points to the pluggable linker jar!
+
+`jjs -cp missing_method_linker.jar missing_method.js`
+print($OUT)