src/jdk/nashorn/internal/objects/NativeObject.java

Tue, 27 Aug 2013 13:17:00 +0200

author
attila
date
Tue, 27 Aug 2013 13:17:00 +0200
changeset 531
47f0a4c4b729
parent 513
b7c04b3b01a7
child 663
98bab0cdd7bf
permissions
-rw-r--r--

8023780: Gracefully handle @CS methods while binding bean properties
Reviewed-by: jlaskey, lagergren, sundar

     1 /*
     2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package jdk.nashorn.internal.objects;
    28 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
    29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
    31 import java.lang.invoke.MethodHandle;
    32 import java.lang.invoke.MethodHandles;
    33 import java.lang.invoke.MethodType;
    34 import java.util.ArrayList;
    35 import java.util.Collection;
    36 import java.util.HashSet;
    37 import java.util.List;
    38 import java.util.Set;
    39 import java.util.concurrent.Callable;
    40 import jdk.internal.dynalink.beans.BeansLinker;
    41 import jdk.internal.dynalink.beans.StaticClass;
    42 import jdk.internal.dynalink.linker.GuardedInvocation;
    43 import jdk.internal.dynalink.linker.GuardingDynamicLinker;
    44 import jdk.internal.dynalink.linker.LinkRequest;
    45 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
    46 import jdk.internal.dynalink.support.LinkRequestImpl;
    47 import jdk.nashorn.api.scripting.ScriptObjectMirror;
    48 import jdk.nashorn.internal.lookup.Lookup;
    49 import jdk.nashorn.internal.objects.annotations.Attribute;
    50 import jdk.nashorn.internal.objects.annotations.Constructor;
    51 import jdk.nashorn.internal.objects.annotations.Function;
    52 import jdk.nashorn.internal.objects.annotations.ScriptClass;
    53 import jdk.nashorn.internal.objects.annotations.Where;
    54 import jdk.nashorn.internal.runtime.AccessorProperty;
    55 import jdk.nashorn.internal.runtime.ECMAException;
    56 import jdk.nashorn.internal.runtime.JSType;
    57 import jdk.nashorn.internal.runtime.Property;
    58 import jdk.nashorn.internal.runtime.PropertyMap;
    59 import jdk.nashorn.internal.runtime.ScriptObject;
    60 import jdk.nashorn.internal.runtime.ScriptRuntime;
    61 import jdk.nashorn.internal.runtime.linker.Bootstrap;
    62 import jdk.nashorn.internal.runtime.linker.InvokeByName;
    64 /**
    65  * ECMA 15.2 Object objects
    66  *
    67  * JavaScript Object constructor/prototype. Note: instances of this class are
    68  * never created. This class is not even a subclass of ScriptObject. But, we use
    69  * this class to generate prototype and constructor for "Object".
    70  *
    71  */
    72 @ScriptClass("Object")
    73 public final class NativeObject {
    74     private static final Object TO_STRING = new Object();
    76     private static InvokeByName getTO_STRING() {
    77         return Global.instance().getInvokeByName(TO_STRING,
    78                 new Callable<InvokeByName>() {
    79                     @Override
    80                     public InvokeByName call() {
    81                         return new InvokeByName("toString", ScriptObject.class);
    82                     }
    83                 });
    84     }
    86     private static final MethodType MIRROR_GETTER_TYPE = MethodType.methodType(Object.class, ScriptObjectMirror.class);
    87     private static final MethodType MIRROR_SETTER_TYPE = MethodType.methodType(Object.class, ScriptObjectMirror.class, Object.class);
    89     // initialized by nasgen
    90     @SuppressWarnings("unused")
    91     private static PropertyMap $nasgenmap$;
    93     private NativeObject() {
    94         // don't create me!
    95         throw new UnsupportedOperationException();
    96     }
    98     private static ECMAException notAnObject(final Object obj) {
    99         return typeError("not.an.object", ScriptRuntime.safeToString(obj));
   100     }
   102     /**
   103      * ECMA 15.2.3.2 Object.getPrototypeOf ( O )
   104      *
   105      * @param  self self reference
   106      * @param  obj object to get prototype from
   107      * @return the prototype of an object
   108      */
   109     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   110     public static Object getPrototypeOf(final Object self, final Object obj) {
   111         if (obj instanceof ScriptObject) {
   112             return ((ScriptObject)obj).getProto();
   113         } else if (obj instanceof ScriptObjectMirror) {
   114             return ((ScriptObjectMirror)obj).getProto();
   115         } else {
   116             final JSType type = JSType.of(obj);
   117             if (type == JSType.OBJECT) {
   118                 // host (Java) objects have null __proto__
   119                 return null;
   120             }
   122             // must be some JS primitive
   123             throw notAnObject(obj);
   124         }
   125     }
   127     /**
   128      * Nashorn extension: Object.setPrototypeOf ( O, proto )
   129      * Also found in ES6 draft specification.
   130      *
   131      * @param  self self reference
   132      * @param  obj object to set prototype for
   133      * @param  proto prototype object to be used
   134      * @return object whose prototype is set
   135      */
   136     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   137     public static Object setPrototypeOf(final Object self, final Object obj, final Object proto) {
   138         if (obj instanceof ScriptObject) {
   139             ((ScriptObject)obj).setProtoCheck(proto);
   140             return obj;
   141         } else if (obj instanceof ScriptObjectMirror) {
   142             ((ScriptObjectMirror)obj).setProto(proto);
   143             return obj;
   144         }
   146         throw notAnObject(obj);
   147     }
   149     /**
   150      * ECMA 15.2.3.3 Object.getOwnPropertyDescriptor ( O, P )
   151      *
   152      * @param self  self reference
   153      * @param obj   object from which to get property descriptor for {@code ToString(prop)}
   154      * @param prop  property descriptor
   155      * @return property descriptor
   156      */
   157     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   158     public static Object getOwnPropertyDescriptor(final Object self, final Object obj, final Object prop) {
   159         if (obj instanceof ScriptObject) {
   160             final String       key  = JSType.toString(prop);
   161             final ScriptObject sobj = (ScriptObject)obj;
   163             return sobj.getOwnPropertyDescriptor(key);
   164         } else if (obj instanceof ScriptObjectMirror) {
   165             final String       key  = JSType.toString(prop);
   166             final ScriptObjectMirror sobjMirror = (ScriptObjectMirror)obj;
   168             return sobjMirror.getOwnPropertyDescriptor(key);
   169         } else {
   170             throw notAnObject(obj);
   171         }
   172     }
   174     /**
   175      * ECMA 15.2.3.4 Object.getOwnPropertyNames ( O )
   176      *
   177      * @param self self reference
   178      * @param obj  object to query for property names
   179      * @return array of property names
   180      */
   181     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   182     public static Object getOwnPropertyNames(final Object self, final Object obj) {
   183         if (obj instanceof ScriptObject) {
   184             return new NativeArray(((ScriptObject)obj).getOwnKeys(true));
   185         } else if (obj instanceof ScriptObjectMirror) {
   186             return new NativeArray(((ScriptObjectMirror)obj).getOwnKeys(true));
   187         } else {
   188             throw notAnObject(obj);
   189         }
   190     }
   192     /**
   193      * ECMA 15.2.3.5 Object.create ( O [, Properties] )
   194      *
   195      * @param self  self reference
   196      * @param proto prototype object
   197      * @param props properties to define
   198      * @return object created
   199      */
   200     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   201     public static Object create(final Object self, final Object proto, final Object props) {
   202         if (proto != null) {
   203             Global.checkObject(proto);
   204         }
   206         // FIXME: should we create a proper object with correct number of
   207         // properties?
   208         final ScriptObject newObj = Global.newEmptyInstance();
   209         newObj.setProto((ScriptObject)proto);
   210         if (props != UNDEFINED) {
   211             NativeObject.defineProperties(self, newObj, props);
   212         }
   214         return newObj;
   215     }
   217     /**
   218      * ECMA 15.2.3.6 Object.defineProperty ( O, P, Attributes )
   219      *
   220      * @param self self reference
   221      * @param obj  object in which to define a property
   222      * @param prop property to define
   223      * @param attr attributes for property descriptor
   224      * @return object
   225      */
   226     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   227     public static Object defineProperty(final Object self, final Object obj, final Object prop, final Object attr) {
   228         Global.checkObject(obj);
   229         ((ScriptObject)obj).defineOwnProperty(JSType.toString(prop), attr, true);
   230         return obj;
   231     }
   233     /**
   234      * ECMA 5.2.3.7 Object.defineProperties ( O, Properties )
   235      *
   236      * @param self  self reference
   237      * @param obj   object in which to define properties
   238      * @param props properties
   239      * @return object
   240      */
   241     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   242     public static Object defineProperties(final Object self, final Object obj, final Object props) {
   243         Global.checkObject(obj);
   245         final ScriptObject sobj     = (ScriptObject)obj;
   246         final Object       propsObj = Global.toObject(props);
   248         if (propsObj instanceof ScriptObject) {
   249             final Object[] keys = ((ScriptObject)propsObj).getOwnKeys(false);
   250             for (final Object key : keys) {
   251                 final String prop = JSType.toString(key);
   252                 sobj.defineOwnProperty(prop, ((ScriptObject)propsObj).get(prop), true);
   253             }
   254         }
   255         return sobj;
   256     }
   258     /**
   259      * ECMA 15.2.3.8 Object.seal ( O )
   260      *
   261      * @param self self reference
   262      * @param obj  object to seal
   263      * @return sealed object
   264      */
   265     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   266     public static Object seal(final Object self, final Object obj) {
   267         if (obj instanceof ScriptObject) {
   268             return ((ScriptObject)obj).seal();
   269         } else if (obj instanceof ScriptObjectMirror) {
   270             return ((ScriptObjectMirror)obj).seal();
   271         } else {
   272             throw notAnObject(obj);
   273         }
   274     }
   277     /**
   278      * ECMA 15.2.3.9 Object.freeze ( O )
   279      *
   280      * @param self self reference
   281      * @param obj object to freeze
   282      * @return frozen object
   283      */
   284     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   285     public static Object freeze(final Object self, final Object obj) {
   286         if (obj instanceof ScriptObject) {
   287             return ((ScriptObject)obj).freeze();
   288         } else if (obj instanceof ScriptObjectMirror) {
   289             return ((ScriptObjectMirror)obj).freeze();
   290         } else {
   291             throw notAnObject(obj);
   292         }
   293     }
   295     /**
   296      * ECMA 15.2.3.10 Object.preventExtensions ( O )
   297      *
   298      * @param self self reference
   299      * @param obj  object, for which to set the internal extensible property to false
   300      * @return object
   301      */
   302     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   303     public static Object preventExtensions(final Object self, final Object obj) {
   304         if (obj instanceof ScriptObject) {
   305             return ((ScriptObject)obj).preventExtensions();
   306         } else if (obj instanceof ScriptObjectMirror) {
   307             return ((ScriptObjectMirror)obj).preventExtensions();
   308         } else {
   309             throw notAnObject(obj);
   310         }
   311     }
   313     /**
   314      * ECMA 15.2.3.11 Object.isSealed ( O )
   315      *
   316      * @param self self reference
   317      * @param obj check whether an object is sealed
   318      * @return true if sealed, false otherwise
   319      */
   320     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   321     public static Object isSealed(final Object self, final Object obj) {
   322         if (obj instanceof ScriptObject) {
   323             return ((ScriptObject)obj).isSealed();
   324         } else if (obj instanceof ScriptObjectMirror) {
   325             return ((ScriptObjectMirror)obj).isSealed();
   326         } else {
   327             throw notAnObject(obj);
   328         }
   329     }
   331     /**
   332      * ECMA 15.2.3.12 Object.isFrozen ( O )
   333      *
   334      * @param self self reference
   335      * @param obj check whether an object
   336      * @return true if object is frozen, false otherwise
   337      */
   338     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   339     public static Object isFrozen(final Object self, final Object obj) {
   340         if (obj instanceof ScriptObject) {
   341             return ((ScriptObject)obj).isFrozen();
   342         } else if (obj instanceof ScriptObjectMirror) {
   343             return ((ScriptObjectMirror)obj).isFrozen();
   344         } else {
   345             throw notAnObject(obj);
   346         }
   347     }
   349     /**
   350      * ECMA 15.2.3.13 Object.isExtensible ( O )
   351      *
   352      * @param self self reference
   353      * @param obj check whether an object is extensible
   354      * @return true if object is extensible, false otherwise
   355      */
   356     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   357     public static Object isExtensible(final Object self, final Object obj) {
   358         if (obj instanceof ScriptObject) {
   359             return ((ScriptObject)obj).isExtensible();
   360         } else if (obj instanceof ScriptObjectMirror) {
   361             return ((ScriptObjectMirror)obj).isExtensible();
   362         } else {
   363             throw notAnObject(obj);
   364         }
   365     }
   367     /**
   368      * ECMA 15.2.3.14 Object.keys ( O )
   369      *
   370      * @param self self reference
   371      * @param obj  object from which to extract keys
   372      * @return array of keys in object
   373      */
   374     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   375     public static Object keys(final Object self, final Object obj) {
   376         if (obj instanceof ScriptObject) {
   377             final ScriptObject sobj = (ScriptObject)obj;
   378             return new NativeArray(sobj.getOwnKeys(false));
   379         } else if (obj instanceof ScriptObjectMirror) {
   380             final ScriptObjectMirror sobjMirror = (ScriptObjectMirror)obj;
   381             return new NativeArray(sobjMirror.getOwnKeys(false));
   382         } else {
   383             throw notAnObject(obj);
   384         }
   385     }
   387     /**
   388      * ECMA 15.2.2.1 , 15.2.1.1 new Object([value]) and Object([value])
   389      *
   390      * Constructor
   391      *
   392      * @param newObj is the new object instantiated with the new operator
   393      * @param self   self reference
   394      * @param value  value of object to be instantiated
   395      * @return the new NativeObject
   396      */
   397     @Constructor
   398     public static Object construct(final boolean newObj, final Object self, final Object value) {
   399         final JSType type = JSType.of(value);
   401         // Object(null), Object(undefined), Object() are same as "new Object()"
   403         if (newObj || (type == JSType.NULL || type == JSType.UNDEFINED)) {
   404             switch (type) {
   405             case BOOLEAN:
   406             case NUMBER:
   407             case STRING:
   408                 return Global.toObject(value);
   409             case OBJECT:
   410             case FUNCTION:
   411                 return value;
   412             case NULL:
   413             case UNDEFINED:
   414                 // fall through..
   415             default:
   416                 break;
   417             }
   419             return Global.newEmptyInstance();
   420         }
   422         return Global.toObject(value);
   423     }
   425     /**
   426      * ECMA 15.2.4.2 Object.prototype.toString ( )
   427      *
   428      * @param self self reference
   429      * @return ToString of object
   430      */
   431     @Function(attributes = Attribute.NOT_ENUMERABLE)
   432     public static Object toString(final Object self) {
   433         return ScriptRuntime.builtinObjectToString(self);
   434     }
   436     /**
   437      * ECMA 15.2.4.3 Object.prototype.toLocaleString ( )
   438      *
   439      * @param self self reference
   440      * @return localized ToString
   441      */
   442     @Function(attributes = Attribute.NOT_ENUMERABLE)
   443     public static Object toLocaleString(final Object self) {
   444         final Object obj = JSType.toScriptObject(self);
   445         if (obj instanceof ScriptObject) {
   446             final InvokeByName toStringInvoker = getTO_STRING();
   447             final ScriptObject sobj = (ScriptObject)self;
   448             try {
   449                 final Object toString = toStringInvoker.getGetter().invokeExact(sobj);
   451                 if (Bootstrap.isCallable(toString)) {
   452                     return toStringInvoker.getInvoker().invokeExact(toString, sobj);
   453                 }
   454             } catch (final RuntimeException | Error e) {
   455                 throw e;
   456             } catch (final Throwable t) {
   457                 throw new RuntimeException(t);
   458             }
   460             throw typeError("not.a.function", "toString");
   461         }
   463         return ScriptRuntime.builtinObjectToString(self);
   464     }
   466     /**
   467      * ECMA 15.2.4.4 Object.prototype.valueOf ( )
   468      *
   469      * @param self self reference
   470      * @return value of object
   471      */
   472     @Function(attributes = Attribute.NOT_ENUMERABLE)
   473     public static Object valueOf(final Object self) {
   474         return Global.toObject(self);
   475     }
   477     /**
   478      * ECMA 15.2.4.5 Object.prototype.hasOwnProperty (V)
   479      *
   480      * @param self self reference
   481      * @param v property to check for
   482      * @return true if property exists in object
   483      */
   484     @Function(attributes = Attribute.NOT_ENUMERABLE)
   485     public static Object hasOwnProperty(final Object self, final Object v) {
   486         final String str = JSType.toString(v);
   487         final Object obj = Global.toObject(self);
   489         return (obj instanceof ScriptObject) && ((ScriptObject)obj).hasOwnProperty(str);
   490     }
   492     /**
   493      * ECMA 15.2.4.6 Object.prototype.isPrototypeOf (V)
   494      *
   495      * @param self self reference
   496      * @param v v prototype object to check against
   497      * @return true if object is prototype of v
   498      */
   499     @Function(attributes = Attribute.NOT_ENUMERABLE)
   500     public static Object isPrototypeOf(final Object self, final Object v) {
   501         if (!(v instanceof ScriptObject)) {
   502             return false;
   503         }
   505         final Object obj   = Global.toObject(self);
   506         ScriptObject proto = (ScriptObject)v;
   508         do {
   509             proto = proto.getProto();
   510             if (proto == obj) {
   511                 return true;
   512             }
   513         } while (proto != null);
   515         return false;
   516     }
   518     /**
   519      * ECMA 15.2.4.7 Object.prototype.propertyIsEnumerable (V)
   520      *
   521      * @param self self reference
   522      * @param v property to check if enumerable
   523      * @return true if property is enumerable
   524      */
   525     @Function(attributes = Attribute.NOT_ENUMERABLE)
   526     public static Object propertyIsEnumerable(final Object self, final Object v) {
   527         final String str = JSType.toString(v);
   528         final Object obj = Global.toObject(self);
   530         if (obj instanceof ScriptObject) {
   531             final jdk.nashorn.internal.runtime.Property property = ((ScriptObject)obj).getMap().findProperty(str);
   532             return property != null && property.isEnumerable();
   533         }
   535         return false;
   536     }
   538     /**
   539      * Nashorn extension: Object.bindProperties
   540      *
   541      * Binds the source object's properties to the target object. Binding
   542      * properties allows two-way read/write for the properties of the source object.
   543      *
   544      * Example:
   545      * <pre>
   546      * var obj = { x: 34, y: 100 };
   547      * var foo = {}
   548      *
   549      * // bind properties of "obj" to "foo" object
   550      * Object.bindProperties(foo, obj);
   551      *
   552      * // now, we can access/write on 'foo' properties
   553      * print(foo.x); // prints obj.x which is 34
   554      *
   555      * // update obj.x via foo.x
   556      * foo.x = "hello";
   557      * print(obj.x); // prints "hello" now
   558      *
   559      * obj.x = 42;   // foo.x also becomes 42
   560      * print(foo.x); // prints 42
   561      * </pre>
   562      * <p>
   563      * The source object bound can be a ScriptObject or a ScriptOjectMirror.
   564      * null or undefined source object results in TypeError being thrown.
   565      * </p>
   566      * Example:
   567      * <pre>
   568      * var obj = loadWithNewGlobal({
   569      *    name: "test",
   570      *    script: "obj = { x: 33, y: 'hello' }"
   571      * });
   572      *
   573      * // bind 'obj's properties to global scope 'this'
   574      * Object.bindProperties(this, obj);
   575      * print(x);         // prints 33
   576      * print(y);         // prints "hello"
   577      * x = Math.PI;      // changes obj.x to Math.PI
   578      * print(obj.x);     // prints Math.PI
   579      * </pre>
   580      *
   581      * Limitations of property binding:
   582      * <ul>
   583      * <li> Only enumerable, immediate (not proto inherited) properties of the source object are bound.
   584      * <li> If the target object already contains a property called "foo", the source's "foo" is skipped (not bound).
   585      * <li> Properties added to the source object after binding to the target are not bound.
   586      * <li> Property configuration changes on the source object (or on the target) is not propagated.
   587      * <li> Delete of property on the target (or the source) is not propagated -
   588      * only the property value is set to 'undefined' if the property happens to be a data property.
   589      * </ul>
   590      * <p>
   591      * It is recommended that the bound properties be treated as non-configurable
   592      * properties to avoid surprises.
   593      * </p>
   594      *
   595      * @param self self reference
   596      * @param target the target object to which the source object's properties are bound
   597      * @param source the source object whose properties are bound to the target
   598      * @return the target object after property binding
   599      */
   600     @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
   601     public static Object bindProperties(final Object self, final Object target, final Object source) {
   602         // target object has to be a ScriptObject
   603         Global.checkObject(target);
   604         // check null or undefined source object
   605         Global.checkObjectCoercible(source);
   607         final ScriptObject targetObj = (ScriptObject)target;
   609         if (source instanceof ScriptObject) {
   610             final ScriptObject sourceObj = (ScriptObject)source;
   611             final Property[] properties = sourceObj.getMap().getProperties();
   613             // filter non-enumerable properties
   614             final ArrayList<Property> propList = new ArrayList<>();
   615             for (Property prop : properties) {
   616                 if (prop.isEnumerable()) {
   617                     propList.add(prop);
   618                 }
   619             }
   621             if (! propList.isEmpty()) {
   622                 targetObj.addBoundProperties(sourceObj, propList.toArray(new Property[propList.size()]));
   623             }
   624         } else if (source instanceof ScriptObjectMirror) {
   625             // get enumerable, immediate properties of mirror
   626             final ScriptObjectMirror mirror = (ScriptObjectMirror)source;
   627             final String[] keys = mirror.getOwnKeys(false);
   628             if (keys.length == 0) {
   629                 // nothing to bind
   630                 return target;
   631             }
   633             // make accessor properties using dynamic invoker getters and setters
   634             final AccessorProperty[] props = new AccessorProperty[keys.length];
   635             for (int idx = 0; idx < keys.length; idx++) {
   636                 final String name = keys[idx];
   637                 final MethodHandle getter = Bootstrap.createDynamicInvoker("dyn:getMethod|getProp|getElem:" + name, MIRROR_GETTER_TYPE);
   638                 final MethodHandle setter = Bootstrap.createDynamicInvoker("dyn:setProp|setElem:" + name, MIRROR_SETTER_TYPE);
   639                 props[idx] = (AccessorProperty.create(name, 0, getter, setter));
   640             }
   642             targetObj.addBoundProperties(source, props);
   643         } else if (source instanceof StaticClass) {
   644             final Class<?> clazz = ((StaticClass)source).getRepresentedClass();
   645             bindBeanProperties(targetObj, source, BeansLinker.getReadableStaticPropertyNames(clazz),
   646                     BeansLinker.getWritableStaticPropertyNames(clazz), BeansLinker.getStaticMethodNames(clazz));
   647         } else {
   648             final Class<?> clazz = source.getClass();
   649             bindBeanProperties(targetObj, source, BeansLinker.getReadableInstancePropertyNames(clazz),
   650                     BeansLinker.getWritableInstancePropertyNames(clazz), BeansLinker.getInstanceMethodNames(clazz));
   651         }
   653         return target;
   654     }
   656     private static void bindBeanProperties(final ScriptObject targetObj, final Object source,
   657             final Collection<String> readablePropertyNames, final Collection<String> writablePropertyNames,
   658             final Collection<String> methodNames) {
   659         final Set<String> propertyNames = new HashSet<>(readablePropertyNames);
   660         propertyNames.addAll(writablePropertyNames);
   662         final Class<?> clazz = source.getClass();
   663         Bootstrap.checkReflectionAccess(clazz);
   665         final MethodType getterType = MethodType.methodType(Object.class, clazz);
   666         final MethodType setterType = MethodType.methodType(Object.class, clazz, Object.class);
   668         final GuardingDynamicLinker linker = BeansLinker.getLinkerForClass(clazz);
   670         final List<AccessorProperty> properties = new ArrayList<>(propertyNames.size() + methodNames.size());
   671         for(final String methodName: methodNames) {
   672             final MethodHandle method;
   673             try {
   674                 method = getBeanOperation(linker, "dyn:getMethod:" + methodName, getterType, source);
   675             } catch(final IllegalAccessError e) {
   676                 // Presumably, this was a caller sensitive method. Ignore it and carry on.
   677                 continue;
   678             }
   679             properties.add(AccessorProperty.create(methodName, Property.NOT_WRITABLE, getBoundBeanMethodGetter(source,
   680                     method), null));
   681         }
   682         for(final String propertyName: propertyNames) {
   683             MethodHandle getter;
   684             if(readablePropertyNames.contains(propertyName)) {
   685                 try {
   686                     getter = getBeanOperation(linker, "dyn:getProp:" + propertyName, getterType, source);
   687                 } catch(final IllegalAccessError e) {
   688                     // Presumably, this was a caller sensitive method. Ignore it and carry on.
   689                     getter = Lookup.EMPTY_GETTER;
   690                 }
   691             } else {
   692                 getter = Lookup.EMPTY_GETTER;
   693             }
   694             final boolean isWritable = writablePropertyNames.contains(propertyName);
   695             MethodHandle setter;
   696             if(isWritable) {
   697                 try {
   698                     setter = getBeanOperation(linker, "dyn:setProp:" + propertyName, setterType, source);
   699                 } catch(final IllegalAccessError e) {
   700                     // Presumably, this was a caller sensitive method. Ignore it and carry on.
   701                     setter = Lookup.EMPTY_SETTER;
   702                 }
   703             } else {
   704                 setter = Lookup.EMPTY_SETTER;
   705             }
   706             if(getter != Lookup.EMPTY_GETTER || setter != Lookup.EMPTY_SETTER) {
   707                 properties.add(AccessorProperty.create(propertyName, isWritable ? 0 : Property.NOT_WRITABLE, getter, setter));
   708             }
   709         }
   711         targetObj.addBoundProperties(source, properties.toArray(new AccessorProperty[properties.size()]));
   712     }
   714     private static MethodHandle getBoundBeanMethodGetter(Object source, MethodHandle methodGetter) {
   715         try {
   716             // NOTE: we're relying on the fact that "dyn:getMethod:..." return value is constant for any given method
   717             // name and object linked with BeansLinker. (Actually, an even stronger assumption is true: return value is
   718             // constant for any given method name and object's class.)
   719             return MethodHandles.dropArguments(MethodHandles.constant(Object.class,
   720                     Bootstrap.bindDynamicMethod(methodGetter.invoke(source), source)), 0, Object.class);
   721         } catch(RuntimeException|Error e) {
   722             throw e;
   723         } catch(Throwable t) {
   724             throw new RuntimeException(t);
   725         }
   726     }
   728     private static MethodHandle getBeanOperation(final GuardingDynamicLinker linker, final String operation,
   729             final MethodType methodType, final Object source) {
   730         final GuardedInvocation inv;
   731         try {
   732             inv = linker.getGuardedInvocation(createLinkRequest(operation, methodType, source),
   733                 Bootstrap.getLinkerServices());
   734             assert passesGuard(source, inv.getGuard());
   735         } catch(RuntimeException|Error e) {
   736             throw e;
   737         } catch(Throwable t) {
   738             throw new RuntimeException(t);
   739         }
   740         assert inv.getSwitchPoint() == null; // Linkers in Dynalink's beans package don't use switchpoints.
   741         // We discard the guard, as all method handles will be bound to a specific object.
   742         return inv.getInvocation();
   743     }
   745     private static boolean passesGuard(final Object obj, final MethodHandle guard) throws Throwable {
   746         return guard == null || (boolean)guard.invoke(obj);
   747     }
   749     private static LinkRequest createLinkRequest(String operation, MethodType methodType, Object source) {
   750         return new LinkRequestImpl(CallSiteDescriptorFactory.create(MethodHandles.publicLookup(), operation,
   751                 methodType), false, source);
   752     }
   753 }

mercurial