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