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