NativeJSAdapter.java revision 1429:b4eb53200105
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 static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
32
33import java.lang.invoke.MethodHandle;
34import java.lang.invoke.MethodHandles;
35import java.lang.invoke.MethodType;
36import java.util.ArrayList;
37import java.util.Iterator;
38import java.util.List;
39import jdk.internal.dynalink.CallSiteDescriptor;
40import jdk.internal.dynalink.linker.GuardedInvocation;
41import jdk.internal.dynalink.linker.LinkRequest;
42import jdk.nashorn.internal.lookup.Lookup;
43import jdk.nashorn.internal.objects.annotations.Constructor;
44import jdk.nashorn.internal.objects.annotations.ScriptClass;
45import jdk.nashorn.internal.runtime.FindProperty;
46import jdk.nashorn.internal.runtime.JSType;
47import jdk.nashorn.internal.runtime.PropertyMap;
48import jdk.nashorn.internal.runtime.ScriptFunction;
49import jdk.nashorn.internal.runtime.ScriptObject;
50import jdk.nashorn.internal.runtime.ScriptRuntime;
51import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
52import jdk.nashorn.internal.scripts.JO;
53
54/**
55 * This class is the implementation of the Nashorn-specific global object named {@code JSAdapter}. It can be
56 * thought of as the {@link java.lang.reflect.Proxy} equivalent for JavaScript. NativeJSAdapter calls specially named
57 * JavaScript methods on an adaptee object when property access/update/call/new/delete is attempted on it. Example:
58 *<pre>
59 *    var y = {
60 *                __get__    : function (name) { ... }
61 *                __has__    : function (name) { ... }
62 *                __put__    : function (name, value) {...}
63 *                __call__   : function (name, arg1, arg2) {...}
64 *                __new__    : function (arg1, arg2) {...}
65 *                __delete__ : function (name) { ... }
66 *                __getIds__ : function () { ... }
67 *            };
68 *
69 *    var x = new JSAdapter(y);
70 *
71 *    x.i;                        // calls y.__get__
72 *    x.foo();                    // calls y.__call__
73 *    new x();                    // calls y.__new__
74 *    i in x;                     // calls y.__has__
75 *    x.p = 10;                   // calls y.__put__
76 *    delete x.p;                 // calls y.__delete__
77 *    for (i in x) { print(i); }  // calls y.__getIds__
78 * </pre>
79 * <p>
80 * JavaScript caller of adapter object is isolated from the fact that the property access/mutation/deletion are really
81 * calls to JavaScript methods on adaptee.
82 * </p>
83 * <p>
84 * JSAdapter constructor can optionally receive an "overrides" object. Properties of overrides object is copied to
85 * JSAdapter instance. When user accessed property is one of these, then adaptee's methods like {@code __get__},
86 * {@code __put__} etc. are not called for those. This can be used to make certain "preferred" properties that can be
87 * accessed in the usual/faster way avoiding proxy mechanism. Example:
88 * </p>
89 * <pre>
90 *     var x = new JSAdapter({ foo: 444, bar: 6546 }) {
91 *          __get__: function(name) { return name; }
92 *      };
93 *
94 *     x.foo;           // 444 directly retrieved without __get__ call
95 *     x.bar = 'hello'; // "bar" directly set without __put__ call
96 *     x.prop           // calls __get__("prop") as 'prop' is not overridden
97 * </pre>
98 * It is possible to pass a specific prototype for JSAdapter instance by passing three arguments to JSAdapter
99 * constructor. So exact signature of JSAdapter constructor is as follows:
100 * <pre>
101 *     JSAdapter([proto], [overrides], adaptee);
102 * </pre>
103 * Both proto and overrides are optional - but adaptee is not. When proto is not passed {@code JSAdapter.prototype} is
104 * used.
105 */
106@ScriptClass("JSAdapter")
107public final class NativeJSAdapter extends ScriptObject {
108    /** object get operation */
109    public static final String __get__       = "__get__";
110    /** object out operation */
111    public static final String __put__       = "__put__";
112    /** object call operation */
113    public static final String __call__      = "__call__";
114    /** object new operation */
115    public static final String __new__       = "__new__";
116    /** object getIds operation */
117    public static final String __getIds__    = "__getIds__";
118    /** object getKeys operation */
119    public static final String __getKeys__   = "__getKeys__";
120    /** object getValues operation */
121    public static final String __getValues__ = "__getValues__";
122    /** object has operation */
123    public static final String __has__       = "__has__";
124    /** object delete operation */
125    public static final String __delete__    = "__delete__";
126
127    // the new extensibility, sealing and freezing operations
128
129    /** prevent extensions operation */
130    public static final String __preventExtensions__ = "__preventExtensions__";
131    /** isExtensible extensions operation */
132    public static final String __isExtensible__      = "__isExtensible__";
133    /** seal operation */
134    public static final String __seal__              = "__seal__";
135    /** isSealed extensions operation */
136    public static final String __isSealed__          = "__isSealed__";
137    /** freeze operation */
138    public static final String __freeze__            = "__freeze__";
139    /** isFrozen extensions operation */
140    public static final String __isFrozen__          = "__isFrozen__";
141
142    private final ScriptObject adaptee;
143    private final boolean overrides;
144
145    private static final MethodHandle IS_JSADAPTOR = findOwnMH("isJSAdaptor", boolean.class, Object.class, Object.class, MethodHandle.class, Object.class, ScriptFunction.class);
146
147    // initialized by nasgen
148    private static PropertyMap $nasgenmap$;
149
150    NativeJSAdapter(final Object overrides, final ScriptObject adaptee, final ScriptObject proto, final PropertyMap map) {
151        super(proto, map);
152        this.adaptee = wrapAdaptee(adaptee);
153        if (overrides instanceof ScriptObject) {
154            this.overrides = true;
155            final ScriptObject sobj = (ScriptObject)overrides;
156            this.addBoundProperties(sobj);
157        } else {
158            this.overrides = false;
159        }
160    }
161
162    private static ScriptObject wrapAdaptee(final ScriptObject adaptee) {
163        return new JO(adaptee);
164    }
165
166    @Override
167    public String getClassName() {
168        return "JSAdapter";
169    }
170
171    @Override
172    public int getInt(final Object key, final int programPoint) {
173        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
174    }
175
176    @Override
177    public int getInt(final double key, final int programPoint) {
178        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
179    }
180
181    @Override
182    public int getInt(final long key, final int programPoint) {
183        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
184    }
185
186    @Override
187    public int getInt(final int key, final int programPoint) {
188        return (overrides && super.hasOwnProperty(key)) ? super.getInt(key, programPoint) : callAdapteeInt(programPoint, __get__, key);
189    }
190
191    @Override
192    public long getLong(final Object key, final int programPoint) {
193        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
194    }
195
196    @Override
197    public long getLong(final double key, final int programPoint) {
198        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
199    }
200
201    @Override
202    public long getLong(final long key, final int programPoint) {
203        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
204    }
205
206    @Override
207    public long getLong(final int key, final int programPoint) {
208        return (overrides && super.hasOwnProperty(key)) ? super.getLong(key, programPoint) : callAdapteeLong(programPoint, __get__, key);
209    }
210
211    @Override
212    public double getDouble(final Object key, final int programPoint) {
213        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
214    }
215
216    @Override
217    public double getDouble(final double key, final int programPoint) {
218        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
219    }
220
221    @Override
222    public double getDouble(final long key, final int programPoint) {
223        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
224    }
225
226    @Override
227    public double getDouble(final int key, final int programPoint) {
228        return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key, programPoint) : callAdapteeDouble(programPoint, __get__, key);
229    }
230
231    @Override
232    public Object get(final Object key) {
233        return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
234    }
235
236    @Override
237    public Object get(final double key) {
238        return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
239    }
240
241    @Override
242    public Object get(final long key) {
243        return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
244    }
245
246    @Override
247    public Object get(final int key) {
248        return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
249    }
250
251    @Override
252    public void set(final Object key, final int value, final int flags) {
253        if (overrides && super.hasOwnProperty(key)) {
254            super.set(key, value, flags);
255        } else {
256            callAdaptee(__put__, key, value, flags);
257        }
258    }
259
260    @Override
261    public void set(final Object key, final long value, final int flags) {
262        if (overrides && super.hasOwnProperty(key)) {
263            super.set(key, value, flags);
264        } else {
265            callAdaptee(__put__, key, value, flags);
266        }
267    }
268
269    @Override
270    public void set(final Object key, final double value, final int flags) {
271        if (overrides && super.hasOwnProperty(key)) {
272            super.set(key, value, flags);
273        } else {
274            callAdaptee(__put__, key, value, flags);
275        }
276    }
277
278    @Override
279    public void set(final Object key, final Object value, final int flags) {
280        if (overrides && super.hasOwnProperty(key)) {
281            super.set(key, value, flags);
282        } else {
283            callAdaptee(__put__, key, value, flags);
284        }
285    }
286
287    @Override
288    public void set(final double key, final int value, final int flags) {
289        if (overrides && super.hasOwnProperty(key)) {
290            super.set(key, value, flags);
291        } else {
292            callAdaptee(__put__, key, value, flags);
293        }
294    }
295
296    @Override
297    public void set(final double key, final long value, final int flags) {
298        if (overrides && super.hasOwnProperty(key)) {
299            super.set(key, value, flags);
300        } else {
301            callAdaptee(__put__, key, value, flags);
302        }
303    }
304
305    @Override
306    public void set(final double key, final double value, final int flags) {
307        if (overrides && super.hasOwnProperty(key)) {
308            super.set(key, value, flags);
309        } else {
310            callAdaptee(__put__, key, value, flags);
311        }
312    }
313
314    @Override
315    public void set(final double key, final Object value, final int flags) {
316        if (overrides && super.hasOwnProperty(key)) {
317            super.set(key, value, flags);
318        } else {
319            callAdaptee(__put__, key, value, flags);
320        }
321    }
322
323    @Override
324    public void set(final long key, final int value, final int flags) {
325        if (overrides && super.hasOwnProperty(key)) {
326            super.set(key, value, flags);
327        } else {
328            callAdaptee(__put__, key, value, flags);
329        }
330    }
331
332    @Override
333    public void set(final long key, final long value, final int flags) {
334        if (overrides && super.hasOwnProperty(key)) {
335            super.set(key, value, flags);
336        } else {
337            callAdaptee(__put__, key, value, flags);
338        }
339    }
340
341    @Override
342    public void set(final long key, final double value, final int flags) {
343        if (overrides && super.hasOwnProperty(key)) {
344            super.set(key, value, flags);
345        } else {
346            callAdaptee(__put__, key, value, flags);
347        }
348    }
349
350    @Override
351    public void set(final long key, final Object value, final int flags) {
352        if (overrides && super.hasOwnProperty(key)) {
353            super.set(key, value, flags);
354        } else {
355            callAdaptee(__put__, key, value, flags);
356        }
357    }
358
359    @Override
360    public void set(final int key, final int value, final int flags) {
361        if (overrides && super.hasOwnProperty(key)) {
362            super.set(key, value, flags);
363        } else {
364            callAdaptee(__put__, key, value, flags);
365        }
366    }
367
368    @Override
369    public void set(final int key, final long value, final int flags) {
370        if (overrides && super.hasOwnProperty(key)) {
371            super.set(key, value, flags);
372        } else {
373            callAdaptee(__put__, key, value, flags);
374        }
375    }
376
377    @Override
378    public void set(final int key, final double value, final int flags) {
379        if (overrides && super.hasOwnProperty(key)) {
380            super.set(key, value, flags);
381        } else {
382            callAdaptee(__put__, key, value, flags);
383        }
384    }
385
386    @Override
387    public void set(final int key, final Object value, final int flags) {
388        if (overrides && super.hasOwnProperty(key)) {
389            super.set(key, value, flags);
390        } else {
391            callAdaptee(__put__, key, value, flags);
392        }
393    }
394
395    @Override
396    public boolean has(final Object key) {
397        if (overrides && super.hasOwnProperty(key)) {
398            return true;
399        }
400
401        return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
402    }
403
404    @Override
405    public boolean has(final int key) {
406        if (overrides && super.hasOwnProperty(key)) {
407            return true;
408        }
409
410        return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
411    }
412
413    @Override
414    public boolean has(final long key) {
415        if (overrides && super.hasOwnProperty(key)) {
416            return true;
417        }
418
419        return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
420    }
421
422    @Override
423    public boolean has(final double key) {
424        if (overrides && super.hasOwnProperty(key)) {
425            return true;
426        }
427
428        return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
429    }
430
431    @Override
432    public boolean delete(final int key, final boolean strict) {
433        if (overrides && super.hasOwnProperty(key)) {
434            return super.delete(key, strict);
435        }
436
437        return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
438    }
439
440    @Override
441    public boolean delete(final long key, final boolean strict) {
442        if (overrides && super.hasOwnProperty(key)) {
443            return super.delete(key, strict);
444        }
445
446        return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
447    }
448
449    @Override
450    public boolean delete(final double key, final boolean strict) {
451        if (overrides && super.hasOwnProperty(key)) {
452            return super.delete(key, strict);
453        }
454
455        return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
456    }
457
458    @Override
459    public boolean delete(final Object key, final boolean strict) {
460        if (overrides && super.hasOwnProperty(key)) {
461            return super.delete(key, strict);
462        }
463
464        return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
465    }
466
467    @Override
468    public Iterator<String> propertyIterator() {
469        // Try __getIds__ first, if not found then try __getKeys__
470        // In jdk6, we had added "__getIds__" so this is just for compatibility.
471        Object func = adaptee.get(__getIds__);
472        if (!(func instanceof ScriptFunction)) {
473            func = adaptee.get(__getKeys__);
474        }
475
476        Object obj;
477        if (func instanceof ScriptFunction) {
478            obj = ScriptRuntime.apply((ScriptFunction)func, adaptee);
479        } else {
480            obj = new NativeArray(0);
481        }
482
483        final List<String> array = new ArrayList<>();
484        for (final Iterator<Object> iter = ArrayLikeIterator.arrayLikeIterator(obj); iter.hasNext(); ) {
485            array.add((String)iter.next());
486        }
487
488        return array.iterator();
489    }
490
491
492    @Override
493    public Iterator<Object> valueIterator() {
494        final Object obj = callAdaptee(new NativeArray(0), __getValues__);
495        return ArrayLikeIterator.arrayLikeIterator(obj);
496    }
497
498    @Override
499    public ScriptObject preventExtensions() {
500        callAdaptee(__preventExtensions__);
501        return this;
502    }
503
504    @Override
505    public boolean isExtensible() {
506        return JSType.toBoolean(callAdaptee(Boolean.TRUE, __isExtensible__));
507    }
508
509    @Override
510    public ScriptObject seal() {
511        callAdaptee(__seal__);
512        return this;
513    }
514
515    @Override
516    public boolean isSealed() {
517        return JSType.toBoolean(callAdaptee(Boolean.FALSE, __isSealed__));
518    }
519
520    @Override
521    public ScriptObject freeze() {
522        callAdaptee(__freeze__);
523        return this;
524    }
525
526    @Override
527    public boolean isFrozen() {
528        return JSType.toBoolean(callAdaptee(Boolean.FALSE, __isFrozen__));
529    }
530
531    /**
532     * Constructor
533     *
534     * @param isNew is this NativeJSAdapter instantiated with the new operator
535     * @param self  self reference
536     * @param args  arguments ([adaptee], [overrides, adaptee] or [proto, overrides, adaptee]
537     * @return new NativeJSAdapter
538     */
539    @Constructor
540    public static NativeJSAdapter construct(final boolean isNew, final Object self, final Object... args) {
541        Object proto     = UNDEFINED;
542        Object overrides = UNDEFINED;
543        Object adaptee;
544
545        if (args == null || args.length == 0) {
546            throw typeError("not.an.object", "null");
547        }
548
549        switch (args.length) {
550        case 1:
551            adaptee = args[0];
552            break;
553
554        case 2:
555            overrides = args[0];
556            adaptee   = args[1];
557            break;
558
559        default:
560            //fallthru
561        case 3:
562            proto = args[0];
563            overrides = args[1];
564            adaptee = args[2];
565            break;
566        }
567
568        if (!(adaptee instanceof ScriptObject)) {
569            throw typeError("not.an.object", ScriptRuntime.safeToString(adaptee));
570        }
571
572        final Global global = Global.instance();
573        if (proto != null && !(proto instanceof ScriptObject)) {
574            proto = global.getJSAdapterPrototype();
575        }
576
577        return new NativeJSAdapter(overrides, (ScriptObject)adaptee, (ScriptObject)proto, $nasgenmap$);
578    }
579
580    @Override
581    protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
582        return findHook(desc, __new__, false);
583    }
584
585    @Override
586    protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
587        if (overrides && super.hasOwnProperty(desc.getNameToken(2))) {
588            try {
589                final GuardedInvocation inv = super.findCallMethodMethod(desc, request);
590                if (inv != null) {
591                    return inv;
592                }
593            } catch (final Exception e) {
594                //ignored
595            }
596        }
597
598        return findHook(desc, __call__);
599    }
600
601    @Override
602    protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operation) {
603        final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
604        if (overrides && super.hasOwnProperty(name)) {
605            try {
606                final GuardedInvocation inv = super.findGetMethod(desc, request, operation);
607                if (inv != null) {
608                    return inv;
609                }
610            } catch (final Exception e) {
611                //ignored
612            }
613        }
614
615        switch(operation) {
616        case "getProp":
617        case "getElem":
618            return findHook(desc, __get__);
619        case "getMethod":
620            final FindProperty find = adaptee.findProperty(__call__, true);
621            if (find != null) {
622                final Object value = find.getObjectValue();
623                if (value instanceof ScriptFunction) {
624                    final ScriptFunction func = (ScriptFunction)value;
625                    // TODO: It's a shame we need to produce a function bound to this and name, when we'd only need it bound
626                    // to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice.
627                    return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
628                            func.createBound(this, new Object[] { name })), 0, Object.class),
629                            testJSAdaptor(adaptee, null, null, null),
630                            adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
631                }
632            }
633            throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
634        default:
635            break;
636        }
637
638        throw new AssertionError("should not reach here");
639    }
640
641    @Override
642    protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
643        if (overrides && super.hasOwnProperty(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) {
644            try {
645                final GuardedInvocation inv = super.findSetMethod(desc, request);
646                if (inv != null) {
647                    return inv;
648                }
649            } catch (final Exception e) {
650                //ignored
651            }
652        }
653
654        return findHook(desc, __put__);
655    }
656
657    // -- Internals only below this point
658    private Object callAdaptee(final String name, final Object... args) {
659        return callAdaptee(UNDEFINED, name, args);
660    }
661
662    private double callAdapteeDouble(final int programPoint, final String name, final Object... args) {
663        return JSType.toNumberMaybeOptimistic(callAdaptee(name, args), programPoint);
664    }
665
666    private long callAdapteeLong(final int programPoint, final String name, final Object... args) {
667        return JSType.toLongMaybeOptimistic(callAdaptee(name, args), programPoint);
668    }
669
670    private int callAdapteeInt(final int programPoint, final String name, final Object... args) {
671        return JSType.toInt32MaybeOptimistic(callAdaptee(name, args), programPoint);
672    }
673
674    private Object callAdaptee(final Object retValue, final String name, final Object... args) {
675        final Object func = adaptee.get(name);
676        if (func instanceof ScriptFunction) {
677            return ScriptRuntime.apply((ScriptFunction)func, adaptee, args);
678        }
679        return retValue;
680    }
681
682    private GuardedInvocation findHook(final CallSiteDescriptor desc, final String hook) {
683        return findHook(desc, hook, true);
684    }
685
686    private GuardedInvocation findHook(final CallSiteDescriptor desc, final String hook, final boolean useName) {
687        final FindProperty findData = adaptee.findProperty(hook, true);
688        final MethodType type = desc.getMethodType();
689        if (findData != null) {
690            final String name = desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null;
691            final Object value = findData.getObjectValue();
692            if (value instanceof ScriptFunction) {
693                final ScriptFunction func = (ScriptFunction)value;
694
695                final MethodHandle methodHandle = getCallMethodHandle(findData, type,
696                    useName ? name : null);
697                if (methodHandle != null) {
698                    return new GuardedInvocation(
699                            methodHandle,
700                            testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
701                            adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null);
702                }
703             }
704        }
705
706        switch (hook) {
707        case __call__:
708            throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
709        default:
710            final MethodHandle methodHandle = hook.equals(__put__) ?
711            MH.asType(Lookup.EMPTY_SETTER, type) :
712            Lookup.emptyGetter(type.returnType());
713            return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null);
714        }
715    }
716
717    private static MethodHandle testJSAdaptor(final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
718        return MH.insertArguments(IS_JSADAPTOR, 1, adaptee, getter, where, func);
719    }
720
721    @SuppressWarnings("unused")
722    private static boolean isJSAdaptor(final Object self, final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
723        final boolean res = self instanceof NativeJSAdapter && ((NativeJSAdapter)self).getAdaptee() == adaptee;
724        if (res && getter != null) {
725            try {
726                return getter.invokeExact(where) == func;
727            } catch (final RuntimeException | Error e) {
728                throw e;
729            } catch (final Throwable t) {
730                throw new RuntimeException(t);
731            }
732        }
733
734        return res;
735    }
736
737    /**
738     * Get the adaptee
739     * @return adaptee ScriptObject
740     */
741    public ScriptObject getAdaptee() {
742        return adaptee;
743    }
744
745    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
746        return MH.findStatic(MethodHandles.lookup(), NativeJSAdapter.class, name, MH.type(rtype, types));
747    }
748}
749