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