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