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     NativeJSAdapter(final Object overrides, final ScriptObject adaptee, final ScriptObject proto, final PropertyMap map) {
 150         super(proto, map);
 151         this.adaptee = wrapAdaptee(adaptee);
 152         if (overrides instanceof ScriptObject) {
 153             this.overrides = true;
 154             final ScriptObject sobj = (ScriptObject)overrides;
 155             this.addBoundProperties(sobj);
 156         } else {
 157             this.overrides = false;
 158         }
 159     }
 160 
 161     private static ScriptObject wrapAdaptee(final ScriptObject adaptee) {
 162         return new JO(adaptee, JO.getInitialMap());
 163     }
 164 
 165     @Override
 166     public String getClassName() {
 167         return "JSAdapter";
 168     }
 169 
 170     @Override
 171     public int getInt(final Object key) {
 172         return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
 173     }
 174 
 175     @Override
 176     public int getInt(final double key) {
 177         return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
 178     }
 179 
 180     @Override
 181     public int getInt(final long key) {
 182         return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
 183     }
 184 
 185     @Override
 186     public int getInt(final int key) {
 187         return (overrides && super.hasOwnProperty(key)) ? super.getInt(key) : callAdapteeInt(__get__, key);
 188     }
 189 
 190     @Override
 191     public long getLong(final Object key) {
 192         return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
 193     }
 194 
 195     @Override
 196     public long getLong(final double key) {
 197         return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
 198     }
 199 
 200     @Override
 201     public long getLong(final long key) {
 202         return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
 203     }
 204 
 205     @Override
 206     public long getLong(final int key) {
 207         return (overrides && super.hasOwnProperty(key)) ? super.getLong(key) : callAdapteeLong(__get__, key);
 208     }
 209 
 210     @Override
 211     public double getDouble(final Object key) {
 212         return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
 213     }
 214 
 215     @Override
 216     public double getDouble(final double key) {
 217         return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
 218     }
 219 
 220     @Override
 221     public double getDouble(final long key) {
 222         return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
 223     }
 224 
 225     @Override
 226     public double getDouble(final int key) {
 227         return (overrides && super.hasOwnProperty(key)) ? super.getDouble(key) : callAdapteeDouble(__get__, key);
 228     }
 229 
 230     @Override
 231     public Object get(final Object key) {
 232         return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
 233     }
 234 
 235     @Override
 236     public Object get(final double key) {
 237         return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
 238     }
 239 
 240     @Override
 241     public Object get(final long key) {
 242         return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
 243     }
 244 
 245     @Override
 246     public Object get(final int key) {
 247         return (overrides && super.hasOwnProperty(key)) ? super.get(key) : callAdaptee(__get__, key);
 248     }
 249 
 250     @Override
 251     public void set(final Object key, final int value, final boolean strict) {
 252         if (overrides && super.hasOwnProperty(key)) {
 253             super.set(key, value, strict);
 254         } else {
 255             callAdaptee(__put__, key, value, strict);
 256         }
 257     }
 258 
 259     @Override
 260     public void set(final Object key, final long value, final boolean strict) {
 261         if (overrides && super.hasOwnProperty(key)) {
 262             super.set(key, value, strict);
 263         } else {
 264             callAdaptee(__put__, key, value, strict);
 265         }
 266     }
 267 
 268     @Override
 269     public void set(final Object key, final double value, final boolean strict) {
 270         if (overrides && super.hasOwnProperty(key)) {
 271             super.set(key, value, strict);
 272         } else {
 273             callAdaptee(__put__, key, value, strict);
 274         }
 275     }
 276 
 277     @Override
 278     public void set(final Object key, final Object value, final boolean strict) {
 279         if (overrides && super.hasOwnProperty(key)) {
 280             super.set(key, value, strict);
 281         } else {
 282             callAdaptee(__put__, key, value, strict);
 283         }
 284     }
 285 
 286     @Override
 287     public void set(final double key, final int value, final boolean strict) {
 288         if (overrides && super.hasOwnProperty(key)) {
 289             super.set(key, value, strict);
 290         } else {
 291             callAdaptee(__put__, key, value, strict);
 292         }
 293     }
 294 
 295     @Override
 296     public void set(final double key, final long value, final boolean strict) {
 297         if (overrides && super.hasOwnProperty(key)) {
 298             super.set(key, value, strict);
 299         } else {
 300             callAdaptee(__put__, key, value, strict);
 301         }
 302     }
 303 
 304     @Override
 305     public void set(final double key, final double value, final boolean strict) {
 306         if (overrides && super.hasOwnProperty(key)) {
 307             super.set(key, value, strict);
 308         } else {
 309             callAdaptee(__put__, key, value, strict);
 310         }
 311     }
 312 
 313     @Override
 314     public void set(final double key, final Object value, final boolean strict) {
 315         if (overrides && super.hasOwnProperty(key)) {
 316             super.set(key, value, strict);
 317         } else {
 318             callAdaptee(__put__, key, value, strict);
 319         }
 320     }
 321 
 322     @Override
 323     public void set(final long key, final int value, final boolean strict) {
 324         if (overrides && super.hasOwnProperty(key)) {
 325             super.set(key, value, strict);
 326         } else {
 327             callAdaptee(__put__, key, value, strict);
 328         }
 329     }
 330 
 331     @Override
 332     public void set(final long key, final long value, final boolean strict) {
 333         if (overrides && super.hasOwnProperty(key)) {
 334             super.set(key, value, strict);
 335         } else {
 336             callAdaptee(__put__, key, value, strict);
 337         }
 338     }
 339 
 340     @Override
 341     public void set(final long key, final double value, final boolean strict) {
 342         if (overrides && super.hasOwnProperty(key)) {
 343             super.set(key, value, strict);
 344         } else {
 345             callAdaptee(__put__, key, value, strict);
 346         }
 347     }
 348 
 349     @Override
 350     public void set(final long key, final Object value, final boolean strict) {
 351         if (overrides && super.hasOwnProperty(key)) {
 352             super.set(key, value, strict);
 353         } else {
 354             callAdaptee(__put__, key, value, strict);
 355         }
 356     }
 357 
 358     @Override
 359     public void set(final int key, final int value, final boolean strict) {
 360         if (overrides && super.hasOwnProperty(key)) {
 361             super.set(key, value, strict);
 362         } else {
 363             callAdaptee(__put__, key, value, strict);
 364         }
 365     }
 366 
 367     @Override
 368     public void set(final int key, final long value, final boolean strict) {
 369         if (overrides && super.hasOwnProperty(key)) {
 370             super.set(key, value, strict);
 371         } else {
 372             callAdaptee(__put__, key, value, strict);
 373         }
 374     }
 375 
 376     @Override
 377     public void set(final int key, final double value, final boolean strict) {
 378         if (overrides && super.hasOwnProperty(key)) {
 379             super.set(key, value, strict);
 380         } else {
 381             callAdaptee(__put__, key, value, strict);
 382         }
 383     }
 384 
 385     @Override
 386     public void set(final int key, final Object value, final boolean strict) {
 387         if (overrides && super.hasOwnProperty(key)) {
 388             super.set(key, value, strict);
 389         } else {
 390             callAdaptee(__put__, key, value, strict);
 391         }
 392     }
 393 
 394     @Override
 395     public boolean has(final Object key) {
 396         if (overrides && super.hasOwnProperty(key)) {
 397             return true;
 398         }
 399 
 400         return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
 401     }
 402 
 403     @Override
 404     public boolean has(final int key) {
 405         if (overrides && super.hasOwnProperty(key)) {
 406             return true;
 407         }
 408 
 409         return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
 410     }
 411 
 412     @Override
 413     public boolean has(final long key) {
 414         if (overrides && super.hasOwnProperty(key)) {
 415             return true;
 416         }
 417 
 418         return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
 419     }
 420 
 421     @Override
 422     public boolean has(final double key) {
 423         if (overrides && super.hasOwnProperty(key)) {
 424             return true;
 425         }
 426 
 427         return JSType.toBoolean(callAdaptee(Boolean.FALSE, __has__, key));
 428     }
 429 
 430     @Override
 431     public boolean delete(final int key, final boolean strict) {
 432         if (overrides && super.hasOwnProperty(key)) {
 433             return super.delete(key, strict);
 434         }
 435 
 436         return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
 437     }
 438 
 439     @Override
 440     public boolean delete(final long key, final boolean strict) {
 441         if (overrides && super.hasOwnProperty(key)) {
 442             return super.delete(key, strict);
 443         }
 444 
 445         return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
 446     }
 447 
 448     @Override
 449     public boolean delete(final double key, final boolean strict) {
 450         if (overrides && super.hasOwnProperty(key)) {
 451             return super.delete(key, strict);
 452         }
 453 
 454         return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
 455     }
 456 
 457     @Override
 458     public boolean delete(final Object key, final boolean strict) {
 459         if (overrides && super.hasOwnProperty(key)) {
 460             return super.delete(key, strict);
 461         }
 462 
 463         return JSType.toBoolean(callAdaptee(Boolean.TRUE, __delete__, key, strict));
 464     }
 465 
 466     @Override
 467     public Iterator<String> propertyIterator() {
 468         // Try __getIds__ first, if not found then try __getKeys__
 469         // In jdk6, we had added "__getIds__" so this is just for compatibility.
 470         Object func = adaptee.get(__getIds__);
 471         if (!(func instanceof ScriptFunction)) {
 472             func = adaptee.get(__getKeys__);
 473         }
 474 
 475         Object obj;
 476         if (func instanceof ScriptFunction) {
 477             obj = ScriptRuntime.apply((ScriptFunction)func, adaptee);
 478         } else {
 479             obj = new NativeArray(0);
 480         }
 481 
 482         final List<String> array = new ArrayList<>();
 483         for (final Iterator<Object> iter = ArrayLikeIterator.arrayLikeIterator(obj); iter.hasNext(); ) {
 484             array.add((String)iter.next());
 485         }
 486 
 487         return array.iterator();
 488     }
 489 
 490 
 491     @Override
 492     public Iterator<Object> valueIterator() {
 493         final Object obj = callAdaptee(new NativeArray(0), __getValues__);
 494         return ArrayLikeIterator.arrayLikeIterator(obj);
 495     }
 496 
 497     @Override
 498     public ScriptObject preventExtensions() {
 499         callAdaptee(__preventExtensions__);
 500         return this;
 501     }
 502 
 503     @Override
 504     public boolean isExtensible() {
 505         return JSType.toBoolean(callAdaptee(Boolean.TRUE, __isExtensible__));
 506     }
 507 
 508     @Override
 509     public ScriptObject seal() {
 510         callAdaptee(__seal__);
 511         return this;
 512     }
 513 
 514     @Override
 515     public boolean isSealed() {
 516         return JSType.toBoolean(callAdaptee(Boolean.FALSE, __isSealed__));
 517     }
 518 
 519     @Override
 520     public ScriptObject freeze() {
 521         callAdaptee(__freeze__);
 522         return this;
 523     }
 524 
 525     @Override
 526     public boolean isFrozen() {
 527         return JSType.toBoolean(callAdaptee(Boolean.FALSE, __isFrozen__));
 528     }
 529 
 530     /**
 531      * Constructor
 532      *
 533      * @param isNew is this NativeJSAdapter instantiated with the new operator
 534      * @param self  self reference
 535      * @param args  arguments ([adaptee], [overrides, adaptee] or [proto, overrides, adaptee]
 536      * @return new NativeJSAdapter
 537      */
 538     @Constructor
 539     public static Object construct(final boolean isNew, final Object self, final Object... args) {
 540         Object proto     = UNDEFINED;
 541         Object overrides = UNDEFINED;
 542         Object adaptee;
 543 
 544         if (args == null || args.length == 0) {
 545             throw typeError("not.an.object", "null");
 546         }
 547 
 548         switch (args.length) {
 549         case 1:
 550             adaptee = args[0];
 551             break;
 552 
 553         case 2:
 554             overrides = args[0];
 555             adaptee   = args[1];
 556             break;
 557 
 558         default:
 559             //fallthru
 560         case 3:
 561             proto = args[0];
 562             overrides = args[1];
 563             adaptee = args[2];
 564             break;
 565         }
 566 
 567         if (!(adaptee instanceof ScriptObject)) {
 568             throw typeError("not.an.object", ScriptRuntime.safeToString(adaptee));
 569         }
 570 
 571         final Global global = Global.instance();
 572         if (proto != null && !(proto instanceof ScriptObject)) {
 573             proto = global.getJSAdapterPrototype();
 574         }
 575 
 576         return new NativeJSAdapter(overrides, (ScriptObject)adaptee, (ScriptObject)proto, $nasgenmap$);
 577     }
 578 
 579     @Override
 580     protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc) {
 581         return findHook(desc, __new__, false);
 582     }
 583 
 584     @Override
 585     protected GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 586         if (overrides && super.hasOwnProperty(desc.getNameToken(2))) {
 587             try {
 588                 final GuardedInvocation inv = super.findCallMethodMethod(desc, request);
 589                 if (inv != null) {
 590                     return inv;
 591                 }
 592             } catch (final Exception e) {
 593                 //ignored
 594             }
 595         }
 596 
 597         return findHook(desc, __call__);
 598     }
 599 
 600     @Override
 601     protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operation) {
 602         final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
 603         if (overrides && super.hasOwnProperty(name)) {
 604             try {
 605                 final GuardedInvocation inv = super.findGetMethod(desc, request, operation);
 606                 if (inv != null) {
 607                     return inv;
 608                 }
 609             } catch (final Exception e) {
 610                 //ignored
 611             }
 612         }
 613 
 614         switch(operation) {
 615         case "getProp":
 616         case "getElem":
 617             return findHook(desc, __get__);
 618         case "getMethod":
 619             final FindProperty find = adaptee.findProperty(__call__, true);
 620             if (find != null) {
 621                 final Object value = find.getObjectValue();
 622                 if (value instanceof ScriptFunction) {
 623                     final ScriptFunctionImpl func = (ScriptFunctionImpl)value;
 624                     // TODO: It's a shame we need to produce a function bound to this and name, when we'd only need it bound
 625                     // to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice.
 626                     return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
 627                             func.makeBoundFunction(this, new Object[] { name })), 0, Object.class),
 628                             adaptee.getProtoSwitchPoint(__call__, find.getOwner()),
 629                             testJSAdaptor(adaptee, null, null, null));
 630                 }
 631             }
 632             throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
 633         default:
 634             break;
 635         }
 636 
 637         throw new AssertionError("should not reach here");
 638     }
 639 
 640     @Override
 641     protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
 642         if (overrides && super.hasOwnProperty(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND))) {
 643             try {
 644                 final GuardedInvocation inv = super.findSetMethod(desc, request);
 645                 if (inv != null) {
 646                     return inv;
 647                 }
 648             } catch (final Exception e) {
 649                 //ignored
 650             }
 651         }
 652 
 653         return findHook(desc, __put__);
 654     }
 655 
 656     // -- Internals only below this point
 657     private Object callAdaptee(final String name, final Object... args) {
 658         return callAdaptee(UNDEFINED, name, args);
 659     }
 660 
 661     private double callAdapteeDouble(final String name, final Object... args) {
 662         return JSType.toNumber(callAdaptee(name, args));
 663     }
 664 
 665     private long callAdapteeLong(final String name, final Object... args) {
 666         return JSType.toLong(callAdaptee(name, args));
 667     }
 668 
 669     private int callAdapteeInt(final String name, final Object... args) {
 670         return JSType.toInt32(callAdaptee(name, args));
 671     }
 672 
 673     private Object callAdaptee(final Object retValue, final String name, final Object... args) {
 674         final Object func = adaptee.get(name);
 675         if (func instanceof ScriptFunction) {
 676             return ScriptRuntime.apply((ScriptFunction)func, adaptee, args);
 677         }
 678         return retValue;
 679     }
 680 
 681     private GuardedInvocation findHook(final CallSiteDescriptor desc, final String hook) {
 682         return findHook(desc, hook, true);
 683     }
 684 
 685     private GuardedInvocation findHook(final CallSiteDescriptor desc, final String hook, final boolean useName) {
 686         final FindProperty findData = adaptee.findProperty(hook, true);
 687         final MethodType type = desc.getMethodType();
 688         if (findData != null) {
 689             final String name = desc.getNameTokenCount() > 2 ? desc.getNameToken(2) : null;
 690             final Object value = findData.getObjectValue();
 691             if (value instanceof ScriptFunction) {
 692                 final ScriptFunction func = (ScriptFunction)value;
 693 
 694                 final MethodHandle methodHandle = getCallMethodHandle(findData, type,
 695                     useName ? name : null);
 696                 if (methodHandle != null) {
 697                     return new GuardedInvocation(
 698                             methodHandle,
 699                             adaptee.getProtoSwitchPoint(hook, findData.getOwner()),
 700                             testJSAdaptor(adaptee, findData.getGetter(Object.class), findData.getOwner(), func));
 701                 }
 702              }
 703         }
 704 
 705         switch (hook) {
 706         case __call__:
 707             throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
 708         default:
 709             final MethodHandle methodHandle = hook.equals(__put__) ?
 710             MH.asType(Lookup.EMPTY_SETTER, type) :
 711             Lookup.emptyGetter(type.returnType());
 712             return new GuardedInvocation(methodHandle, adaptee.getProtoSwitchPoint(hook, null), testJSAdaptor(adaptee, null, null, null));
 713         }
 714     }
 715 
 716     private static MethodHandle testJSAdaptor(final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
 717         return MH.insertArguments(IS_JSADAPTOR, 1, adaptee, getter, where, func);
 718     }
 719 
 720     @SuppressWarnings("unused")
 721     private static boolean isJSAdaptor(final Object self, final Object adaptee, final MethodHandle getter, final Object where, final ScriptFunction func) {
 722         final boolean res = self instanceof NativeJSAdapter && ((NativeJSAdapter)self).getAdaptee() == adaptee;
 723         if (res && getter != null) {
 724             try {
 725                 return getter.invokeExact(where) == func;
 726             } catch (final RuntimeException | Error e) {
 727                 throw e;
 728             } catch (final Throwable t) {
 729                 throw new RuntimeException(t);
 730             }
 731         }
 732 
 733         return res;
 734     }
 735 
 736     /**
 737      * Get the adaptee
 738      * @return adaptee ScriptObject
 739      */
 740     public ScriptObject getAdaptee() {
 741         return adaptee;
 742     }
 743 
 744     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 745         return MH.findStatic(MethodHandles.lookup(), NativeJSAdapter.class, name, MH.type(rtype, types));
 746     }
 747 }