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