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