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