8020324: Implement Object.bindProperties(target, source) for beans

Mon, 15 Jul 2013 12:33:48 +0200

author
attila
date
Mon, 15 Jul 2013 12:33:48 +0200
changeset 439
973d78ee0728
parent 437
5cdf4352ee0b
child 440
62c552bcc342

8020324: Implement Object.bindProperties(target, source) for beans
Reviewed-by: hannesw, sundar

src/jdk/internal/dynalink/beans/AbstractJavaLinker.java file | annotate | diff | comparison | revisions
src/jdk/internal/dynalink/beans/BeansLinker.java file | annotate | diff | comparison | revisions
src/jdk/internal/dynalink/beans/StaticClassLinker.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/objects/NativeObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/Bootstrap.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java file | annotate | diff | comparison | revisions
test/script/basic/JDK-8020324.js file | annotate | diff | comparison | revisions
test/script/basic/JDK-8020324.js.EXPECTED file | annotate | diff | comparison | revisions
test/src/jdk/nashorn/test/models/PropertyBind.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Fri Jul 12 20:06:41 2013 +0530
     1.2 +++ b/src/jdk/internal/dynalink/beans/AbstractJavaLinker.java	Mon Jul 15 12:33:48 2013 +0200
     1.3 @@ -92,6 +92,8 @@
     1.4  import java.lang.reflect.Member;
     1.5  import java.lang.reflect.Method;
     1.6  import java.lang.reflect.Modifier;
     1.7 +import java.util.Collection;
     1.8 +import java.util.Collections;
     1.9  import java.util.HashMap;
    1.10  import java.util.List;
    1.11  import java.util.Map;
    1.12 @@ -194,6 +196,22 @@
    1.13  
    1.14      abstract FacetIntrospector createFacetIntrospector();
    1.15  
    1.16 +    Collection<String> getReadablePropertyNames() {
    1.17 +        return getUnmodifiableKeys(propertyGetters);
    1.18 +    }
    1.19 +
    1.20 +    Collection<String> getWritablePropertyNames() {
    1.21 +        return getUnmodifiableKeys(propertySetters);
    1.22 +    }
    1.23 +
    1.24 +    Collection<String> getMethodNames() {
    1.25 +        return getUnmodifiableKeys(methods);
    1.26 +    }
    1.27 +
    1.28 +    private static Collection<String> getUnmodifiableKeys(Map<String, ?> m) {
    1.29 +        return Collections.unmodifiableCollection(m.keySet());
    1.30 +    }
    1.31 +
    1.32      /**
    1.33       * Sets the specified dynamic method to be the property getter for the specified property. Note that you can only
    1.34       * use this when you're certain that the method handle does not belong to a caller-sensitive method. For properties
     2.1 --- a/src/jdk/internal/dynalink/beans/BeansLinker.java	Fri Jul 12 20:06:41 2013 +0530
     2.2 +++ b/src/jdk/internal/dynalink/beans/BeansLinker.java	Mon Jul 15 12:33:48 2013 +0200
     2.3 @@ -84,6 +84,8 @@
     2.4  package jdk.internal.dynalink.beans;
     2.5  
     2.6  import java.lang.invoke.MethodHandles;
     2.7 +import java.util.Collection;
     2.8 +import java.util.Collections;
     2.9  import jdk.internal.dynalink.CallSiteDescriptor;
    2.10  import jdk.internal.dynalink.DynamicLinkerFactory;
    2.11  import jdk.internal.dynalink.linker.GuardedInvocation;
    2.12 @@ -166,6 +168,72 @@
    2.13          return obj instanceof DynamicMethod;
    2.14      }
    2.15  
    2.16 +    /**
    2.17 +     * Returns a collection of names of all readable instance properties of a class.
    2.18 +     * @param clazz the class
    2.19 +     * @return a collection of names of all readable instance properties of a class.
    2.20 +     */
    2.21 +    public static Collection<String> getReadableInstancePropertyNames(Class<?> clazz) {
    2.22 +        TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
    2.23 +        if(linker instanceof BeanLinker) {
    2.24 +            return ((BeanLinker)linker).getReadablePropertyNames();
    2.25 +        }
    2.26 +        return Collections.emptySet();
    2.27 +    }
    2.28 +
    2.29 +    /**
    2.30 +     * Returns a collection of names of all writable instance properties of a class.
    2.31 +     * @param clazz the class
    2.32 +     * @return a collection of names of all writable instance properties of a class.
    2.33 +     */
    2.34 +    public static Collection<String> getWritableInstancePropertyNames(Class<?> clazz) {
    2.35 +        TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
    2.36 +        if(linker instanceof BeanLinker) {
    2.37 +            return ((BeanLinker)linker).getWritablePropertyNames();
    2.38 +        }
    2.39 +        return Collections.emptySet();
    2.40 +    }
    2.41 +
    2.42 +    /**
    2.43 +     * Returns a collection of names of all instance methods of a class.
    2.44 +     * @param clazz the class
    2.45 +     * @return a collection of names of all instance methods of a class.
    2.46 +     */
    2.47 +    public static Collection<String> getInstanceMethodNames(Class<?> clazz) {
    2.48 +        TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
    2.49 +        if(linker instanceof BeanLinker) {
    2.50 +            return ((BeanLinker)linker).getMethodNames();
    2.51 +        }
    2.52 +        return Collections.emptySet();
    2.53 +    }
    2.54 +
    2.55 +    /**
    2.56 +     * Returns a collection of names of all readable static properties of a class.
    2.57 +     * @param clazz the class
    2.58 +     * @return a collection of names of all readable static properties of a class.
    2.59 +     */
    2.60 +    public static Collection<String> getReadableStaticPropertyNames(Class<?> clazz) {
    2.61 +        return StaticClassLinker.getReadableStaticPropertyNames(clazz);
    2.62 +    }
    2.63 +
    2.64 +    /**
    2.65 +     * Returns a collection of names of all writable static properties of a class.
    2.66 +     * @param clazz the class
    2.67 +     * @return a collection of names of all writable static properties of a class.
    2.68 +     */
    2.69 +    public static Collection<String> getWritableStaticPropertyNames(Class<?> clazz) {
    2.70 +        return StaticClassLinker.getWritableStaticPropertyNames(clazz);
    2.71 +    }
    2.72 +
    2.73 +    /**
    2.74 +     * Returns a collection of names of all static methods of a class.
    2.75 +     * @param clazz the class
    2.76 +     * @return a collection of names of all static methods of a class.
    2.77 +     */
    2.78 +    public static Collection<String> getStaticMethodNames(Class<?> clazz) {
    2.79 +        return StaticClassLinker.getStaticMethodNames(clazz);
    2.80 +    }
    2.81 +
    2.82      @Override
    2.83      public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices)
    2.84              throws Exception {
     3.1 --- a/src/jdk/internal/dynalink/beans/StaticClassLinker.java	Fri Jul 12 20:06:41 2013 +0530
     3.2 +++ b/src/jdk/internal/dynalink/beans/StaticClassLinker.java	Mon Jul 15 12:33:48 2013 +0200
     3.3 @@ -88,10 +88,10 @@
     3.4  import java.lang.invoke.MethodType;
     3.5  import java.lang.reflect.Array;
     3.6  import java.util.Arrays;
     3.7 +import java.util.Collection;
     3.8  import jdk.internal.dynalink.CallSiteDescriptor;
     3.9  import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
    3.10  import jdk.internal.dynalink.linker.GuardedInvocation;
    3.11 -import jdk.internal.dynalink.linker.GuardingDynamicLinker;
    3.12  import jdk.internal.dynalink.linker.LinkRequest;
    3.13  import jdk.internal.dynalink.linker.LinkerServices;
    3.14  import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
    3.15 @@ -102,9 +102,9 @@
    3.16   * @author Attila Szegedi
    3.17   */
    3.18  class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
    3.19 -    private final ClassValue<GuardingDynamicLinker> linkers = new ClassValue<GuardingDynamicLinker>() {
    3.20 +    private static final ClassValue<SingleClassStaticsLinker> linkers = new ClassValue<SingleClassStaticsLinker>() {
    3.21          @Override
    3.22 -        protected GuardingDynamicLinker computeValue(Class<?> clazz) {
    3.23 +        protected SingleClassStaticsLinker computeValue(Class<?> clazz) {
    3.24              return new SingleClassStaticsLinker(clazz);
    3.25          }
    3.26      };
    3.27 @@ -160,6 +160,18 @@
    3.28          }
    3.29      }
    3.30  
    3.31 +    static Collection<String> getReadableStaticPropertyNames(Class<?> clazz) {
    3.32 +        return linkers.get(clazz).getReadablePropertyNames();
    3.33 +    }
    3.34 +
    3.35 +    static Collection<String> getWritableStaticPropertyNames(Class<?> clazz) {
    3.36 +        return linkers.get(clazz).getWritablePropertyNames();
    3.37 +    }
    3.38 +
    3.39 +    static Collection<String> getStaticMethodNames(Class<?> clazz) {
    3.40 +        return linkers.get(clazz).getMethodNames();
    3.41 +    }
    3.42 +
    3.43      @Override
    3.44      public GuardedInvocation getGuardedInvocation(LinkRequest request, LinkerServices linkerServices) throws Exception {
    3.45          final Object receiver = request.getReceiver();
     4.1 --- a/src/jdk/nashorn/internal/objects/NativeObject.java	Fri Jul 12 20:06:41 2013 +0530
     4.2 +++ b/src/jdk/nashorn/internal/objects/NativeObject.java	Mon Jul 15 12:33:48 2013 +0200
     4.3 @@ -29,8 +29,22 @@
     4.4  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
     4.5  
     4.6  import java.lang.invoke.MethodHandle;
     4.7 +import java.lang.invoke.MethodHandles;
     4.8 +import java.lang.invoke.MethodType;
     4.9  import java.util.ArrayList;
    4.10 +import java.util.Collection;
    4.11 +import java.util.HashSet;
    4.12 +import java.util.List;
    4.13 +import java.util.Set;
    4.14 +import jdk.internal.dynalink.beans.BeansLinker;
    4.15 +import jdk.internal.dynalink.beans.StaticClass;
    4.16 +import jdk.internal.dynalink.linker.GuardedInvocation;
    4.17 +import jdk.internal.dynalink.linker.GuardingDynamicLinker;
    4.18 +import jdk.internal.dynalink.linker.LinkRequest;
    4.19 +import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
    4.20 +import jdk.internal.dynalink.support.LinkRequestImpl;
    4.21  import jdk.nashorn.api.scripting.ScriptObjectMirror;
    4.22 +import jdk.nashorn.internal.lookup.Lookup;
    4.23  import jdk.nashorn.internal.objects.annotations.Attribute;
    4.24  import jdk.nashorn.internal.objects.annotations.Constructor;
    4.25  import jdk.nashorn.internal.objects.annotations.Function;
    4.26 @@ -58,6 +72,8 @@
    4.27  @ScriptClass("Object")
    4.28  public final class NativeObject {
    4.29      private static final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class);
    4.30 +    private static final MethodType MIRROR_GETTER_TYPE = MethodType.methodType(Object.class, ScriptObjectMirror.class);
    4.31 +    private static final MethodType MIRROR_SETTER_TYPE = MethodType.methodType(Object.class, ScriptObjectMirror.class, Object.class);
    4.32  
    4.33      // initialized by nasgen
    4.34      @SuppressWarnings("unused")
    4.35 @@ -577,14 +593,91 @@
    4.36              final AccessorProperty[] props = new AccessorProperty[keys.length];
    4.37              for (int idx = 0; idx < keys.length; idx++) {
    4.38                  final String name = keys[idx];
    4.39 -                final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, Object.class, ScriptObjectMirror.class);
    4.40 -                final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, Object.class, ScriptObjectMirror.class, Object.class);
    4.41 +                final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
    4.42 +                final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
    4.43                  props[idx] = (AccessorProperty.create(name, 0, getter, setter));
    4.44              }
    4.45  
    4.46              targetObj.addBoundProperties(source, props);
    4.47 +        } else if (source instanceof StaticClass) {
    4.48 +            final Class<?> clazz = ((StaticClass)source).getRepresentedClass();
    4.49 +            bindBeanProperties(targetObj, source, BeansLinker.getReadableStaticPropertyNames(clazz),
    4.50 +                    BeansLinker.getWritableStaticPropertyNames(clazz), BeansLinker.getStaticMethodNames(clazz));
    4.51 +        } else {
    4.52 +            final Class<?> clazz = source.getClass();
    4.53 +            bindBeanProperties(targetObj, source, BeansLinker.getReadableInstancePropertyNames(clazz),
    4.54 +                    BeansLinker.getWritableInstancePropertyNames(clazz), BeansLinker.getInstanceMethodNames(clazz));
    4.55          }
    4.56  
    4.57          return target;
    4.58      }
    4.59 +
    4.60 +    private static void bindBeanProperties(final ScriptObject targetObj, final Object source,
    4.61 +            final Collection<String> readablePropertyNames, final Collection<String> writablePropertyNames,
    4.62 +            final Collection<String> methodNames) {
    4.63 +        final Set<String> propertyNames = new HashSet<>(readablePropertyNames);
    4.64 +        propertyNames.addAll(writablePropertyNames);
    4.65 +
    4.66 +        final Class<?> clazz = source.getClass();
    4.67 +
    4.68 +        final MethodType getterType = MethodType.methodType(Object.class, clazz);
    4.69 +        final MethodType setterType = MethodType.methodType(Object.class, clazz, Object.class);
    4.70 +
    4.71 +        final GuardingDynamicLinker linker = BeansLinker.getLinkerForClass(clazz);
    4.72 +
    4.73 +        final List<AccessorProperty> properties = new ArrayList<>(propertyNames.size() + methodNames.size());
    4.74 +        for(final String methodName: methodNames) {
    4.75 +            properties.add(AccessorProperty.create(methodName, Property.NOT_WRITABLE,
    4.76 +                    getBoundBeanMethodGetter(source, getBeanOperation(linker, "dyn:getMethod:" + methodName, getterType, source)),
    4.77 +                    null));
    4.78 +        }
    4.79 +        for(final String propertyName: propertyNames) {
    4.80 +            final boolean isWritable = writablePropertyNames.contains(propertyName);
    4.81 +            properties.add(AccessorProperty.create(propertyName, isWritable ? 0 : Property.NOT_WRITABLE,
    4.82 +                    readablePropertyNames.contains(propertyName) ? getBeanOperation(linker, "dyn:getProp:" + propertyName, getterType, source) : Lookup.EMPTY_GETTER,
    4.83 +                    isWritable ? getBeanOperation(linker, "dyn:setProp:" + propertyName, setterType, source) : Lookup.EMPTY_SETTER));
    4.84 +        }
    4.85 +
    4.86 +        targetObj.addBoundProperties(source, properties.toArray(new AccessorProperty[properties.size()]));
    4.87 +    }
    4.88 +
    4.89 +    private static MethodHandle getBoundBeanMethodGetter(Object source, MethodHandle methodGetter) {
    4.90 +        try {
    4.91 +            // NOTE: we're relying on the fact that "dyn:getMethod:..." return value is constant for any given method
    4.92 +            // name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is
    4.93 +            // constant for any given method name and object's class.)
    4.94 +            return MethodHandles.dropArguments(MethodHandles.constant(Object.class,
    4.95 +                    Bootstrap.bindDynamicMethod(methodGetter.invoke(source), source)), 0, Object.class);
    4.96 +        } catch(RuntimeException|Error e) {
    4.97 +            throw e;
    4.98 +        } catch(Throwable t) {
    4.99 +            throw new RuntimeException(t);
   4.100 +        }
   4.101 +    }
   4.102 +
   4.103 +    private static MethodHandle getBeanOperation(final GuardingDynamicLinker linker, final String operation,
   4.104 +            final MethodType methodType, final Object source) {
   4.105 +        final GuardedInvocation inv;
   4.106 +        try {
   4.107 +            inv = linker.getGuardedInvocation(createLinkRequest(operation, methodType, source),
   4.108 +                Bootstrap.getLinkerServices());
   4.109 +            assert passesGuard(source, inv.getGuard());
   4.110 +        } catch(RuntimeException|Error e) {
   4.111 +            throw e;
   4.112 +        } catch(Throwable t) {
   4.113 +            throw new RuntimeException(t);
   4.114 +        }
   4.115 +        assert inv.getSwitchPoint() == null; // Linkers in Dynalink's beans package don't use switchpoints.
   4.116 +        // We discard the guard, as all method handles will be bound to a specific object.
   4.117 +        return inv.getInvocation();
   4.118 +    }
   4.119 +
   4.120 +    private static boolean passesGuard(final Object obj, final MethodHandle guard) throws Throwable {
   4.121 +        return guard == null || (boolean)guard.invoke(obj);
   4.122 +    }
   4.123 +
   4.124 +    private static LinkRequest createLinkRequest(String operation, MethodType methodType, Object source) {
   4.125 +        return new LinkRequestImpl(CallSiteDescriptorFactory.create(MethodHandles.publicLookup(), operation,
   4.126 +                methodType), false, source);
   4.127 +    }
   4.128  }
     5.1 --- a/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Fri Jul 12 20:06:41 2013 +0530
     5.2 +++ b/src/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Mon Jul 15 12:33:48 2013 +0200
     5.3 @@ -57,7 +57,7 @@
     5.4      static {
     5.5          final DynamicLinkerFactory factory = new DynamicLinkerFactory();
     5.6          factory.setPrioritizedLinkers(new NashornLinker(), new NashornPrimitiveLinker(), new NashornStaticClassLinker(),
     5.7 -                new JSObjectLinker(), new ReflectionCheckLinker());
     5.8 +                new BoundDynamicMethodLinker(), new JSObjectLinker(), new ReflectionCheckLinker());
     5.9          factory.setFallbackLinkers(new BeansLinker(), new NashornBottomLinker());
    5.10          factory.setSyncOnRelink(true);
    5.11          final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", -1);
    5.12 @@ -208,6 +208,16 @@
    5.13      }
    5.14  
    5.15      /**
    5.16 +     * Binds a bean dynamic method (returned by invoking {@code dyn:getMethod} on an object linked with
    5.17 +     * {@code BeansLinker} to a receiver.
    5.18 +     * @param dynamicMethod the dynamic method to bind
    5.19 +     * @param boundThis the bound "this" value.
    5.20 +     * @return a bound dynamic method.
    5.21 +     */
    5.22 +    public static Object bindDynamicMethod(Object dynamicMethod, Object boundThis) {
    5.23 +        return new BoundDynamicMethod(dynamicMethod, boundThis);
    5.24 +    }
    5.25 +    /**
    5.26       * Returns the Nashorn's internally used dynamic linker's services object. Note that in code that is processing a
    5.27       * linking request, you will normally use the {@code LinkerServices} object passed by whatever top-level linker
    5.28       * invoked the linking (if the call site is in Nashorn-generated code, you'll get this object anyway). You should
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethod.java	Mon Jul 15 12:33:48 2013 +0200
     6.3 @@ -0,0 +1,51 @@
     6.4 +/*
     6.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     6.7 + *
     6.8 + * This code is free software; you can redistribute it and/or modify it
     6.9 + * under the terms of the GNU General Public License version 2 only, as
    6.10 + * published by the Free Software Foundation.  Oracle designates this
    6.11 + * particular file as subject to the "Classpath" exception as provided
    6.12 + * by Oracle in the LICENSE file that accompanied this code.
    6.13 + *
    6.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    6.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    6.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    6.17 + * version 2 for more details (a copy is included in the LICENSE file that
    6.18 + * accompanied this code).
    6.19 + *
    6.20 + * You should have received a copy of the GNU General Public License version
    6.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    6.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    6.23 + *
    6.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    6.25 + * or visit www.oracle.com if you need additional information or have any
    6.26 + * questions.
    6.27 + */
    6.28 +
    6.29 +package jdk.nashorn.internal.runtime.linker;
    6.30 +
    6.31 +import jdk.internal.dynalink.beans.BeansLinker;
    6.32 +
    6.33 +/**
    6.34 + * Represents a Dynalink dynamic method bound to a receiver. Note that objects of this class are just the tuples of
    6.35 + * a method and a bound this, without any behavior. All the behavior is defined in the {@link BoundDynamicMethodLinker}.
    6.36 + */
    6.37 +final class BoundDynamicMethod {
    6.38 +    private final Object dynamicMethod;
    6.39 +    private final Object boundThis;
    6.40 +
    6.41 +    BoundDynamicMethod(final Object dynamicMethod, final Object boundThis) {
    6.42 +        assert BeansLinker.isDynamicMethod(dynamicMethod);
    6.43 +        this.dynamicMethod = dynamicMethod;
    6.44 +        this.boundThis = boundThis;
    6.45 +    }
    6.46 +
    6.47 +    Object getDynamicMethod() {
    6.48 +        return dynamicMethod;
    6.49 +    }
    6.50 +
    6.51 +    Object getBoundThis() {
    6.52 +        return boundThis;
    6.53 +    }
    6.54 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/jdk/nashorn/internal/runtime/linker/BoundDynamicMethodLinker.java	Mon Jul 15 12:33:48 2013 +0200
     7.3 @@ -0,0 +1,90 @@
     7.4 +/*
     7.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.7 + *
     7.8 + * This code is free software; you can redistribute it and/or modify it
     7.9 + * under the terms of the GNU General Public License version 2 only, as
    7.10 + * published by the Free Software Foundation.  Oracle designates this
    7.11 + * particular file as subject to the "Classpath" exception as provided
    7.12 + * by Oracle in the LICENSE file that accompanied this code.
    7.13 + *
    7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    7.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    7.17 + * version 2 for more details (a copy is included in the LICENSE file that
    7.18 + * accompanied this code).
    7.19 + *
    7.20 + * You should have received a copy of the GNU General Public License version
    7.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    7.23 + *
    7.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    7.25 + * or visit www.oracle.com if you need additional information or have any
    7.26 + * questions.
    7.27 + */
    7.28 +
    7.29 +package jdk.nashorn.internal.runtime.linker;
    7.30 +
    7.31 +import java.lang.invoke.MethodHandle;
    7.32 +import java.lang.invoke.MethodHandles;
    7.33 +import java.lang.invoke.MethodType;
    7.34 +import jdk.internal.dynalink.CallSiteDescriptor;
    7.35 +import jdk.internal.dynalink.beans.BeansLinker;
    7.36 +import jdk.internal.dynalink.linker.GuardedInvocation;
    7.37 +import jdk.internal.dynalink.linker.LinkRequest;
    7.38 +import jdk.internal.dynalink.linker.LinkerServices;
    7.39 +import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
    7.40 +import jdk.internal.dynalink.support.Guards;
    7.41 +
    7.42 +/**
    7.43 + * Links {@link BoundDynamicMethod} objects. Passes through to Dynalink's BeansLinker for linking a dynamic method
    7.44 + * (they only respond to "dyn:call"), and modifies the returned invocation to deal with the receiver binding.
    7.45 + */
    7.46 +final class BoundDynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
    7.47 +    @Override
    7.48 +    public boolean canLinkType(Class<?> type) {
    7.49 +        return type == BoundDynamicMethod.class;
    7.50 +    }
    7.51 +
    7.52 +    @Override
    7.53 +    public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices) throws Exception {
    7.54 +        final Object objBoundDynamicMethod = linkRequest.getReceiver();
    7.55 +        if(!(objBoundDynamicMethod instanceof BoundDynamicMethod)) {
    7.56 +            return null;
    7.57 +        }
    7.58 +
    7.59 +        final BoundDynamicMethod boundDynamicMethod = (BoundDynamicMethod)objBoundDynamicMethod;
    7.60 +        final Object dynamicMethod = boundDynamicMethod.getDynamicMethod();
    7.61 +        final Object boundThis = boundDynamicMethod.getBoundThis();
    7.62 +
    7.63 +        // Replace arguments (boundDynamicMethod, this, ...) => (dynamicMethod, boundThis, ...) when delegating to
    7.64 +        // BeansLinker
    7.65 +        final Object[] args = linkRequest.getArguments();
    7.66 +        args[0] = dynamicMethod;
    7.67 +        args[1] = boundThis;
    7.68 +
    7.69 +        // Use R(T0, T1, ...) => R(dynamicMethod.class, boundThis.class, ...) call site type when delegating to
    7.70 +        // BeansLinker.
    7.71 +        final CallSiteDescriptor descriptor = linkRequest.getCallSiteDescriptor();
    7.72 +        final MethodType type = descriptor.getMethodType();
    7.73 +        final CallSiteDescriptor newDescriptor = descriptor.changeMethodType(
    7.74 +                type.changeParameterType(0, dynamicMethod.getClass()).changeParameterType(1, boundThis.getClass()));
    7.75 +
    7.76 +        // Delegate to BeansLinker
    7.77 +        final GuardedInvocation inv = BeansLinker.getLinkerForClass(dynamicMethod.getClass()).getGuardedInvocation(
    7.78 +                linkRequest.replaceArguments(newDescriptor, args), linkerServices);
    7.79 +        if(inv == null) {
    7.80 +            return null;
    7.81 +        }
    7.82 +
    7.83 +        // Bind (dynamicMethod, boundThis) to the handle
    7.84 +        final MethodHandle boundHandle = MethodHandles.insertArguments(inv.getInvocation(), 0, dynamicMethod, boundThis);
    7.85 +        final Class<?> p0Type = type.parameterType(0);
    7.86 +        // Ignore incoming (boundDynamicMethod, this)
    7.87 +        final MethodHandle droppingHandle = MethodHandles.dropArguments(boundHandle, 0, p0Type, type.parameterType(1));
    7.88 +        // Identity guard on boundDynamicMethod object
    7.89 +        final MethodHandle newGuard = Guards.getIdentityGuard(boundDynamicMethod);
    7.90 +
    7.91 +        return inv.replaceMethods(droppingHandle, newGuard.asType(newGuard.type().changeParameterType(0, p0Type)));
    7.92 +    }
    7.93 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/test/script/basic/JDK-8020324.js	Mon Jul 15 12:33:48 2013 +0200
     8.3 @@ -0,0 +1,139 @@
     8.4 +/*
     8.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     8.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     8.7 + * 
     8.8 + * This code is free software; you can redistribute it and/or modify it
     8.9 + * under the terms of the GNU General Public License version 2 only, as
    8.10 + * published by the Free Software Foundation.
    8.11 + * 
    8.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    8.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    8.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    8.15 + * version 2 for more details (a copy is included in the LICENSE file that
    8.16 + * accompanied this code).
    8.17 + * 
    8.18 + * You should have received a copy of the GNU General Public License version
    8.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    8.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    8.21 + * 
    8.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    8.23 + * or visit www.oracle.com if you need additional information or have any
    8.24 + * questions.
    8.25 + */
    8.26 +
    8.27 +/**
    8.28 + * JDK-8020324: Implement Object.bindProperties(target, source) for beans
    8.29 + *
    8.30 + * @test
    8.31 + * @run
    8.32 + */
    8.33 +
    8.34 +var PropertyBind = Java.type("jdk.nashorn.test.models.PropertyBind")
    8.35 +var bean = new PropertyBind
    8.36 +
    8.37 +var obj1 = {}
    8.38 +Object.bindProperties(obj1, bean)
    8.39 +
    8.40 +printBanner("Two-way read-write instance field")
    8.41 +printEval("obj1.publicInt = 13")
    8.42 +printEval("bean.publicInt")
    8.43 +printEval("bean.publicInt = 15")
    8.44 +printEval("obj1.publicInt")
    8.45 +
    8.46 +printBanner("Read only public instance field")
    8.47 +printEval("obj1.publicFinalInt")
    8.48 +printEval("obj1.publicFinalInt = 16")
    8.49 +printEval("obj1.publicFinalInt")
    8.50 +printEval("bean.publicFinalInt")
    8.51 +
    8.52 +printBanner("Two-way read-write instance property")
    8.53 +printEval("obj1.readWrite = 17")
    8.54 +printEval("bean.readWrite")
    8.55 +printEval("bean.readWrite = 18")
    8.56 +printEval("obj1.readWrite")
    8.57 +printEval("obj1.getReadWrite()")
    8.58 +printEval("obj1.setReadWrite(19)")
    8.59 +printEval("obj1.readWrite")
    8.60 +printEval("bean.readWrite")
    8.61 +
    8.62 +printBanner("Read only instance property")
    8.63 +printEval("obj1.readOnly")
    8.64 +printEval("obj1.readOnly = 20")
    8.65 +printEval("obj1.readOnly")
    8.66 +printEval("obj1.getReadOnly()")
    8.67 +printEval("bean.getReadOnly()")
    8.68 +
    8.69 +printBanner("Write only instance property")
    8.70 +printEval("obj1.writeOnly = 21")
    8.71 +printEval("obj1.writeOnly")
    8.72 +printEval("bean.writeOnly")
    8.73 +printEval("bean.peekWriteOnly()")
    8.74 +
    8.75 +var obj2 = {}
    8.76 +Object.bindProperties(obj2, PropertyBind)
    8.77 +
    8.78 +printBanner("Two-way read-write public static field")
    8.79 +printEval("obj2.publicStaticInt = 22")
    8.80 +printEval("PropertyBind.publicStaticInt")
    8.81 +printEval("PropertyBind.publicStaticInt = 23")
    8.82 +printEval("obj2.publicStaticInt")
    8.83 +
    8.84 +printBanner("Read only public static field")
    8.85 +printEval("obj2.publicStaticFinalInt")
    8.86 +printEval("obj2.publicStaticFinalInt = 24")
    8.87 +printEval("obj2.publicStaticFinalInt")
    8.88 +printEval("PropertyBind.publicStaticFinalInt")
    8.89 +
    8.90 +printBanner("Two-way read-write static property")
    8.91 +printEval("obj2.staticReadWrite = 25")
    8.92 +printEval("PropertyBind.staticReadWrite")
    8.93 +printEval("PropertyBind.staticReadWrite = 26")
    8.94 +printEval("obj2.staticReadWrite")
    8.95 +printEval("obj2.getStaticReadWrite()")
    8.96 +printEval("obj2.setStaticReadWrite(27)")
    8.97 +printEval("obj2.staticReadWrite")
    8.98 +printEval("PropertyBind.staticReadWrite")
    8.99 +
   8.100 +printBanner("Read only static property")
   8.101 +printEval("obj2.staticReadOnly")
   8.102 +printEval("obj2.staticReadOnly = 28")
   8.103 +printEval("obj2.staticReadOnly")
   8.104 +printEval("obj2.getStaticReadOnly()")
   8.105 +printEval("PropertyBind.getStaticReadOnly()")
   8.106 +
   8.107 +printBanner("Write only static property")
   8.108 +printEval("obj2.staticWriteOnly = 29")
   8.109 +printEval("obj2.staticWriteOnly")
   8.110 +printEval("PropertyBind.staticWriteOnly")
   8.111 +printEval("PropertyBind.peekStaticWriteOnly()")
   8.112 +
   8.113 +printBanner("Sanity check to ensure property values remained what they were")
   8.114 +printEval("obj1.publicInt")
   8.115 +printEval("bean.publicInt")
   8.116 +printEval("obj1.publicFinalInt")
   8.117 +printEval("bean.publicFinalInt")
   8.118 +printEval("obj1.readWrite")
   8.119 +printEval("bean.readWrite")
   8.120 +printEval("obj1.readOnly")
   8.121 +printEval("bean.readOnly")
   8.122 +printEval("bean.peekWriteOnly()")
   8.123 +
   8.124 +printEval("obj2.publicStaticInt")
   8.125 +printEval("PropertyBind.publicStaticInt")
   8.126 +printEval("obj2.publicStaticFinalInt")
   8.127 +printEval("PropertyBind.publicStaticFinalInt")
   8.128 +printEval("obj2.staticReadWrite")
   8.129 +printEval("PropertyBind.staticReadWrite")
   8.130 +printEval("obj2.staticReadOnly")
   8.131 +printEval("PropertyBind.staticReadOnly")
   8.132 +printEval("PropertyBind.peekStaticWriteOnly()")
   8.133 +
   8.134 +
   8.135 +function printEval(s) {
   8.136 +    print(s + ": " + eval(s))
   8.137 +}
   8.138 +
   8.139 +function printBanner(s) {
   8.140 +    print()
   8.141 +    print("==== " + s + " ====")
   8.142 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/test/script/basic/JDK-8020324.js.EXPECTED	Mon Jul 15 12:33:48 2013 +0200
     9.3 @@ -0,0 +1,90 @@
     9.4 +
     9.5 +==== Two-way read-write instance field ====
     9.6 +obj1.publicInt = 13: 13
     9.7 +bean.publicInt: 13
     9.8 +bean.publicInt = 15: 15
     9.9 +obj1.publicInt: 15
    9.10 +
    9.11 +==== Read only public instance field ====
    9.12 +obj1.publicFinalInt: 42
    9.13 +obj1.publicFinalInt = 16: 16
    9.14 +obj1.publicFinalInt: 42
    9.15 +bean.publicFinalInt: 42
    9.16 +
    9.17 +==== Two-way read-write instance property ====
    9.18 +obj1.readWrite = 17: 17
    9.19 +bean.readWrite: 17
    9.20 +bean.readWrite = 18: 18
    9.21 +obj1.readWrite: 18
    9.22 +obj1.getReadWrite(): 18
    9.23 +obj1.setReadWrite(19): null
    9.24 +obj1.readWrite: 19
    9.25 +bean.readWrite: 19
    9.26 +
    9.27 +==== Read only instance property ====
    9.28 +obj1.readOnly: 123
    9.29 +obj1.readOnly = 20: 20
    9.30 +obj1.readOnly: 123
    9.31 +obj1.getReadOnly(): 123
    9.32 +bean.getReadOnly(): 123
    9.33 +
    9.34 +==== Write only instance property ====
    9.35 +obj1.writeOnly = 21: 21
    9.36 +obj1.writeOnly: undefined
    9.37 +bean.writeOnly: undefined
    9.38 +bean.peekWriteOnly(): 21
    9.39 +
    9.40 +==== Two-way read-write public static field ====
    9.41 +obj2.publicStaticInt = 22: 22
    9.42 +PropertyBind.publicStaticInt: 22
    9.43 +PropertyBind.publicStaticInt = 23: 23
    9.44 +obj2.publicStaticInt: 23
    9.45 +
    9.46 +==== Read only public static field ====
    9.47 +obj2.publicStaticFinalInt: 2112
    9.48 +obj2.publicStaticFinalInt = 24: 24
    9.49 +obj2.publicStaticFinalInt: 2112
    9.50 +PropertyBind.publicStaticFinalInt: 2112
    9.51 +
    9.52 +==== Two-way read-write static property ====
    9.53 +obj2.staticReadWrite = 25: 25
    9.54 +PropertyBind.staticReadWrite: 25
    9.55 +PropertyBind.staticReadWrite = 26: 26
    9.56 +obj2.staticReadWrite: 26
    9.57 +obj2.getStaticReadWrite(): 26
    9.58 +obj2.setStaticReadWrite(27): null
    9.59 +obj2.staticReadWrite: 27
    9.60 +PropertyBind.staticReadWrite: 27
    9.61 +
    9.62 +==== Read only static property ====
    9.63 +obj2.staticReadOnly: 1230
    9.64 +obj2.staticReadOnly = 28: 28
    9.65 +obj2.staticReadOnly: 1230
    9.66 +obj2.getStaticReadOnly(): 1230
    9.67 +PropertyBind.getStaticReadOnly(): 1230
    9.68 +
    9.69 +==== Write only static property ====
    9.70 +obj2.staticWriteOnly = 29: 29
    9.71 +obj2.staticWriteOnly: undefined
    9.72 +PropertyBind.staticWriteOnly: undefined
    9.73 +PropertyBind.peekStaticWriteOnly(): 29
    9.74 +
    9.75 +==== Sanity check to ensure property values remained what they were ====
    9.76 +obj1.publicInt: 15
    9.77 +bean.publicInt: 15
    9.78 +obj1.publicFinalInt: 42
    9.79 +bean.publicFinalInt: 42
    9.80 +obj1.readWrite: 19
    9.81 +bean.readWrite: 19
    9.82 +obj1.readOnly: 123
    9.83 +bean.readOnly: 123
    9.84 +bean.peekWriteOnly(): 21
    9.85 +obj2.publicStaticInt: 23
    9.86 +PropertyBind.publicStaticInt: 23
    9.87 +obj2.publicStaticFinalInt: 2112
    9.88 +PropertyBind.publicStaticFinalInt: 2112
    9.89 +obj2.staticReadWrite: 27
    9.90 +PropertyBind.staticReadWrite: 27
    9.91 +obj2.staticReadOnly: 1230
    9.92 +PropertyBind.staticReadOnly: 1230
    9.93 +PropertyBind.peekStaticWriteOnly(): 29
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/test/src/jdk/nashorn/test/models/PropertyBind.java	Mon Jul 15 12:33:48 2013 +0200
    10.3 @@ -0,0 +1,82 @@
    10.4 +/*
    10.5 + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
    10.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    10.7 + *
    10.8 + * This code is free software; you can redistribute it and/or modify it
    10.9 + * under the terms of the GNU General Public License version 2 only, as
   10.10 + * published by the Free Software Foundation.  Oracle designates this
   10.11 + * particular file as subject to the "Classpath" exception as provided
   10.12 + * by Oracle in the LICENSE file that accompanied this code.
   10.13 + *
   10.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   10.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   10.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   10.17 + * version 2 for more details (a copy is included in the LICENSE file that
   10.18 + * accompanied this code).
   10.19 + *
   10.20 + * You should have received a copy of the GNU General Public License version
   10.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   10.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   10.23 + *
   10.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   10.25 + * or visit www.oracle.com if you need additional information or have any
   10.26 + * questions.
   10.27 + */
   10.28 +
   10.29 +package jdk.nashorn.test.models;
   10.30 +
   10.31 +public class PropertyBind {
   10.32 +    public static int publicStaticInt;
   10.33 +    public static final int publicStaticFinalInt = 2112;
   10.34 +
   10.35 +    private static int staticReadWrite;
   10.36 +    private static int staticReadOnly = 1230;
   10.37 +    private static int staticWriteOnly;
   10.38 +
   10.39 +    public int publicInt;
   10.40 +    public final int publicFinalInt = 42;
   10.41 +
   10.42 +    private int readWrite;
   10.43 +    private int readOnly = 123;
   10.44 +    private int writeOnly;
   10.45 +
   10.46 +    public int getReadWrite() {
   10.47 +        return readWrite;
   10.48 +    }
   10.49 +
   10.50 +    public void setReadWrite(int readWrite) {
   10.51 +        this.readWrite = readWrite;
   10.52 +    }
   10.53 +
   10.54 +    public int getReadOnly() {
   10.55 +        return readOnly;
   10.56 +    }
   10.57 +
   10.58 +    public void setWriteOnly(int writeOnly) {
   10.59 +        this.writeOnly = writeOnly;
   10.60 +    }
   10.61 +
   10.62 +    public int peekWriteOnly() {
   10.63 +        return writeOnly;
   10.64 +    }
   10.65 +
   10.66 +    public static int getStaticReadWrite() {
   10.67 +        return staticReadWrite;
   10.68 +    }
   10.69 +
   10.70 +    public static void setStaticReadWrite(int staticReadWrite) {
   10.71 +        PropertyBind.staticReadWrite = staticReadWrite;
   10.72 +    }
   10.73 +
   10.74 +    public static int getStaticReadOnly() {
   10.75 +        return staticReadOnly;
   10.76 +    }
   10.77 +
   10.78 +    public static void setStaticWriteOnly(int staticWriteOnly) {
   10.79 +        PropertyBind.staticWriteOnly = staticWriteOnly;
   10.80 +    }
   10.81 +
   10.82 +    public static int peekStaticWriteOnly() {
   10.83 +        return PropertyBind.staticWriteOnly;
   10.84 +    }
   10.85 +}

mercurial