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