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