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