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