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