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