# HG changeset patch # User sundar # Date 1449149679 -19800 # Node ID 81a65a2faef3b25332c3afb23c679680de159e28 # Parent 84eaea8d05744d50d6528f56c9c0772ba69f3504 8144519: Add a sample for pluggable dynalink linker that demonstrats beans linker delegation Reviewed-by: mhaupt, jlaskey diff -r 84eaea8d0574 -r 81a65a2faef3 nashorn/samples/META-INF/services/jdk.dynalink.linker.GuardingDynamicLinkerExporter --- 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 diff -r 84eaea8d0574 -r 81a65a2faef3 nashorn/samples/MissingMethodExample.java --- /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; + } +} + diff -r 84eaea8d0574 -r 81a65a2faef3 nashorn/samples/MissingMethodHandler.java --- /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); +} diff -r 84eaea8d0574 -r 81a65a2faef3 nashorn/samples/MissingMethodLinkerExporter.java --- /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 get() { + final ArrayList 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; + } +} diff -r 84eaea8d0574 -r 81a65a2faef3 nashorn/samples/missing_method.js --- /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) diff -r 84eaea8d0574 -r 81a65a2faef3 nashorn/samples/missing_method_linker.js --- /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)