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