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