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