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