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