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