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 
  26 package jdk.nashorn.internal.objects;
  27 
  28 import static jdk.nashorn.internal.lookup.Lookup.MH;
  29 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  30 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  31 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
  32 
  33 import java.lang.invoke.MethodHandle;
  34 import java.lang.invoke.MethodHandles;
  35 import java.lang.invoke.MethodType;
  36 import java.util.ArrayList;
  37 import java.util.Iterator;
  38 import java.util.List;
  39 import jdk.internal.dynalink.CallSiteDescriptor;
  40 import jdk.internal.dynalink.linker.GuardedInvocation;
  41 import jdk.internal.dynalink.linker.LinkRequest;
  42 import jdk.nashorn.internal.lookup.Lookup;
  43 import jdk.nashorn.internal.objects.annotations.Constructor;
  44 import jdk.nashorn.internal.objects.annotations.ScriptClass;
  45 import jdk.nashorn.internal.runtime.FindProperty;
  46 import jdk.nashorn.internal.runtime.JSType;
  47 import jdk.nashorn.internal.runtime.PropertyMap;
  48 import jdk.nashorn.internal.runtime.ScriptFunction;
  49 import jdk.nashorn.internal.runtime.ScriptObject;
  50 import jdk.nashorn.internal.runtime.ScriptRuntime;
  51 import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
  52 import 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")
 107 public 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 }