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.api.scripting;
  27 
  28 import java.nio.ByteBuffer;
  29 import java.security.AccessControlContext;
  30 import java.security.AccessController;
  31 import java.security.Permissions;
  32 import java.security.PrivilegedAction;
  33 import java.security.ProtectionDomain;
  34 import java.util.AbstractMap;
  35 import java.util.ArrayList;
  36 import java.util.Collection;
  37 import java.util.Collections;
  38 import java.util.Iterator;
  39 import java.util.LinkedHashSet;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Set;
  43 import java.util.concurrent.Callable;
  44 import javax.script.Bindings;
  45 import jdk.nashorn.internal.objects.Global;
  46 import jdk.nashorn.internal.runtime.ConsString;
  47 import jdk.nashorn.internal.runtime.Context;
  48 import jdk.nashorn.internal.runtime.JSType;
  49 import jdk.nashorn.internal.runtime.ScriptFunction;
  50 import jdk.nashorn.internal.runtime.ScriptObject;
  51 import jdk.nashorn.internal.runtime.ScriptRuntime;
  52 import jdk.nashorn.internal.runtime.arrays.ArrayData;
  53 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  54 
  55 /**
  56  * Mirror object that wraps a given Nashorn Script object.
  57  *
  58  * @since 1.8u40
  59  */
  60 @jdk.Exported
  61 public final class ScriptObjectMirror extends AbstractJSObject implements Bindings {
  62     private static AccessControlContext getContextAccCtxt() {
  63         final Permissions perms = new Permissions();
  64         perms.add(new RuntimePermission(Context.NASHORN_GET_CONTEXT));
  65         return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
  66     }
  67 
  68     private static final AccessControlContext GET_CONTEXT_ACC_CTXT = getContextAccCtxt();
  69 
  70     private final ScriptObject sobj;
  71     private final Global  global;
  72     private final boolean strict;
  73 
  74     @Override
  75     public boolean equals(final Object other) {
  76         if (other instanceof ScriptObjectMirror) {
  77             return sobj.equals(((ScriptObjectMirror)other).sobj);
  78         }
  79 
  80         return false;
  81     }
  82 
  83     @Override
  84     public int hashCode() {
  85         return sobj.hashCode();
  86     }
  87 
  88     @Override
  89     public String toString() {
  90         return inGlobal(new Callable<String>() {
  91             @Override
  92             public String call() {
  93                 return ScriptRuntime.safeToString(sobj);
  94             }
  95         });
  96     }
  97 
  98     // JSObject methods
  99 
 100     @Override
 101     public Object call(final Object thiz, final Object... args) {
 102         final Global oldGlobal = Context.getGlobal();
 103         final boolean globalChanged = (oldGlobal != global);
 104 
 105         try {
 106             if (globalChanged) {
 107                 Context.setGlobal(global);
 108             }
 109 
 110             if (sobj instanceof ScriptFunction) {
 111                 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
 112                 final Object self = globalChanged? wrap(thiz, oldGlobal) : thiz;
 113                 return wrap(ScriptRuntime.apply((ScriptFunction)sobj, unwrap(self, global), unwrapArray(modArgs, global)), global);
 114             }
 115 
 116             throw new RuntimeException("not a function: " + toString());
 117         } catch (final NashornException ne) {
 118             throw ne.initEcmaError(global);
 119         } catch (final RuntimeException | Error e) {
 120             throw e;
 121         } catch (final Throwable t) {
 122             throw new RuntimeException(t);
 123         } finally {
 124             if (globalChanged) {
 125                 Context.setGlobal(oldGlobal);
 126             }
 127         }
 128     }
 129 
 130     @Override
 131     public Object newObject(final Object... args) {
 132         final Global oldGlobal = Context.getGlobal();
 133         final boolean globalChanged = (oldGlobal != global);
 134 
 135         try {
 136             if (globalChanged) {
 137                 Context.setGlobal(global);
 138             }
 139 
 140             if (sobj instanceof ScriptFunction) {
 141                 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
 142                 return wrap(ScriptRuntime.construct((ScriptFunction)sobj, unwrapArray(modArgs, global)), global);
 143             }
 144 
 145             throw new RuntimeException("not a constructor: " + toString());
 146         } catch (final NashornException ne) {
 147             throw ne.initEcmaError(global);
 148         } catch (final RuntimeException | Error e) {
 149             throw e;
 150         } catch (final Throwable t) {
 151             throw new RuntimeException(t);
 152         } finally {
 153             if (globalChanged) {
 154                 Context.setGlobal(oldGlobal);
 155             }
 156         }
 157     }
 158 
 159     @Override
 160     public Object eval(final String s) {
 161         return inGlobal(new Callable<Object>() {
 162             @Override
 163             public Object call() {
 164                 final Context context = AccessController.doPrivileged(
 165                         new PrivilegedAction<Context>() {
 166                             @Override
 167                             public Context run() {
 168                                 return Context.getContext();
 169                             }
 170                         }, GET_CONTEXT_ACC_CTXT);
 171                 return wrap(context.eval(global, s, sobj, null, false), global);
 172             }
 173         });
 174     }
 175 
 176     /**
 177      * Call member function
 178      * @param functionName function name
 179      * @param args         arguments
 180      * @return return value of function
 181      */
 182     public Object callMember(final String functionName, final Object... args) {
 183         functionName.getClass(); // null check
 184         final Global oldGlobal = Context.getGlobal();
 185         final boolean globalChanged = (oldGlobal != global);
 186 
 187         try {
 188             if (globalChanged) {
 189                 Context.setGlobal(global);
 190             }
 191 
 192             final Object val = sobj.get(functionName);
 193             if (val instanceof ScriptFunction) {
 194                 final Object[] modArgs = globalChanged? wrapArray(args, oldGlobal) : args;
 195                 return wrap(ScriptRuntime.apply((ScriptFunction)val, sobj, unwrapArray(modArgs, global)), global);
 196             } else if (val instanceof JSObject && ((JSObject)val).isFunction()) {
 197                 return ((JSObject)val).call(sobj, args);
 198             }
 199 
 200             throw new NoSuchMethodException("No such function " + functionName);
 201         } catch (final NashornException ne) {
 202             throw ne.initEcmaError(global);
 203         } catch (final RuntimeException | Error e) {
 204             throw e;
 205         } catch (final Throwable t) {
 206             throw new RuntimeException(t);
 207         } finally {
 208             if (globalChanged) {
 209                 Context.setGlobal(oldGlobal);
 210             }
 211         }
 212     }
 213 
 214     @Override
 215     public Object getMember(final String name) {
 216         name.getClass();
 217         return inGlobal(new Callable<Object>() {
 218             @Override public Object call() {
 219                 return wrap(sobj.get(name), global);
 220             }
 221         });
 222     }
 223 
 224     @Override
 225     public Object getSlot(final int index) {
 226         return inGlobal(new Callable<Object>() {
 227             @Override public Object call() {
 228                 return wrap(sobj.get(index), global);
 229             }
 230         });
 231     }
 232 
 233     @Override
 234     public boolean hasMember(final String name) {
 235         name.getClass();
 236         return inGlobal(new Callable<Boolean>() {
 237             @Override public Boolean call() {
 238                 return sobj.has(name);
 239             }
 240         });
 241     }
 242 
 243     @Override
 244     public boolean hasSlot(final int slot) {
 245         return inGlobal(new Callable<Boolean>() {
 246             @Override public Boolean call() {
 247                 return sobj.has(slot);
 248             }
 249         });
 250     }
 251 
 252     @Override
 253     public void removeMember(final String name) {
 254         name.getClass();
 255         remove(name);
 256     }
 257 
 258     @Override
 259     public void setMember(final String name, final Object value) {
 260         name.getClass();
 261         put(name, value);
 262     }
 263 
 264     @Override
 265     public void setSlot(final int index, final Object value) {
 266         inGlobal(new Callable<Void>() {
 267             @Override public Void call() {
 268                 sobj.set(index, unwrap(value, global), getCallSiteFlags());
 269                 return null;
 270             }
 271         });
 272     }
 273 
 274     /**
 275      * Nashorn extension: setIndexedPropertiesToExternalArrayData.
 276      * set indexed properties be exposed from a given nio ByteBuffer.
 277      *
 278      * @param buf external buffer - should be a nio ByteBuffer
 279      */
 280     public void setIndexedPropertiesToExternalArrayData(final ByteBuffer buf) {
 281         inGlobal(new Callable<Void>() {
 282             @Override public Void call() {
 283                 sobj.setArray(ArrayData.allocate(buf));
 284                 return null;
 285             }
 286         });
 287     }
 288 
 289 
 290     @Override
 291     public boolean isInstance(final Object obj) {
 292         if (! (obj instanceof ScriptObjectMirror)) {
 293             return false;
 294         }
 295 
 296         final ScriptObjectMirror instance = (ScriptObjectMirror)obj;
 297         // if not belongs to my global scope, return false
 298         if (global != instance.global) {
 299             return false;
 300         }
 301 
 302         return inGlobal(new Callable<Boolean>() {
 303             @Override public Boolean call() {
 304                 return sobj.isInstance(instance.sobj);
 305             }
 306         });
 307     }
 308 
 309     @Override
 310     public String getClassName() {
 311         return sobj.getClassName();
 312     }
 313 
 314     @Override
 315     public boolean isFunction() {
 316         return sobj instanceof ScriptFunction;
 317     }
 318 
 319     @Override
 320     public boolean isStrictFunction() {
 321         return isFunction() && ((ScriptFunction)sobj).isStrict();
 322     }
 323 
 324     @Override
 325     public boolean isArray() {
 326         return sobj.isArray();
 327     }
 328 
 329     // javax.script.Bindings methods
 330 
 331     @Override
 332     public void clear() {
 333         inGlobal(new Callable<Object>() {
 334             @Override public Object call() {
 335                 sobj.clear(strict);
 336                 return null;
 337             }
 338         });
 339     }
 340 
 341     @Override
 342     public boolean containsKey(final Object key) {
 343         return inGlobal(new Callable<Boolean>() {
 344             @Override public Boolean call() {
 345                 return sobj.containsKey(unwrap(key, global));
 346             }
 347         });
 348     }
 349 
 350     @Override
 351     public boolean containsValue(final Object value) {
 352         return inGlobal(new Callable<Boolean>() {
 353             @Override public Boolean call() {
 354                 return sobj.containsValue(unwrap(value, global));
 355             }
 356         });
 357     }
 358 
 359     @Override
 360     public Set<Map.Entry<String, Object>> entrySet() {
 361         return inGlobal(new Callable<Set<Map.Entry<String, Object>>>() {
 362             @Override public Set<Map.Entry<String, Object>> call() {
 363                 final Iterator<String>               iter    = sobj.propertyIterator();
 364                 final Set<Map.Entry<String, Object>> entries = new LinkedHashSet<>();
 365 
 366                 while (iter.hasNext()) {
 367                     final String key   = iter.next();
 368                     final Object value = translateUndefined(wrap(sobj.get(key), global));
 369                     entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value));
 370                 }
 371 
 372                 return Collections.unmodifiableSet(entries);
 373             }
 374         });
 375     }
 376 
 377     @Override
 378     public Object get(final Object key) {
 379         return inGlobal(new Callable<Object>() {
 380             @Override public Object call() {
 381                 return translateUndefined(wrap(sobj.get(key), global));
 382             }
 383         });
 384     }
 385 
 386     @Override
 387     public boolean isEmpty() {
 388         return inGlobal(new Callable<Boolean>() {
 389             @Override public Boolean call() {
 390                 return sobj.isEmpty();
 391             }
 392         });
 393     }
 394 
 395     @Override
 396     public Set<String> keySet() {
 397         return inGlobal(new Callable<Set<String>>() {
 398             @Override public Set<String> call() {
 399                 final Iterator<String> iter   = sobj.propertyIterator();
 400                 final Set<String>      keySet = new LinkedHashSet<>();
 401 
 402                 while (iter.hasNext()) {
 403                     keySet.add(iter.next());
 404                 }
 405 
 406                 return Collections.unmodifiableSet(keySet);
 407             }
 408         });
 409     }
 410 
 411     @Override
 412     public Object put(final String key, final Object value) {
 413         final ScriptObject oldGlobal = Context.getGlobal();
 414         final boolean globalChanged = (oldGlobal != global);
 415         return inGlobal(new Callable<Object>() {
 416             @Override public Object call() {
 417                 final Object modValue = globalChanged? wrap(value, oldGlobal) : value;
 418                 return translateUndefined(wrap(sobj.put(key, unwrap(modValue, global), strict), global));
 419             }
 420         });
 421     }
 422 
 423     @Override
 424     public void putAll(final Map<? extends String, ? extends Object> map) {
 425         final ScriptObject oldGlobal = Context.getGlobal();
 426         final boolean globalChanged = (oldGlobal != global);
 427         inGlobal(new Callable<Object>() {
 428             @Override public Object call() {
 429                 for (final Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
 430                     final Object value = entry.getValue();
 431                     final Object modValue = globalChanged? wrap(value, oldGlobal) : value;
 432                     sobj.set(entry.getKey(), unwrap(modValue, global), getCallSiteFlags());
 433                 }
 434                 return null;
 435             }
 436         });
 437     }
 438 
 439     @Override
 440     public Object remove(final Object key) {
 441         return inGlobal(new Callable<Object>() {
 442             @Override public Object call() {
 443                 return wrap(sobj.remove(unwrap(key, global), strict), global);
 444             }
 445         });
 446     }
 447 
 448     /**
 449      * Delete a property from this object.
 450      *
 451      * @param key the property to be deleted
 452      *
 453      * @return if the delete was successful or not
 454      */
 455     public boolean delete(final Object key) {
 456         return inGlobal(new Callable<Boolean>() {
 457             @Override public Boolean call() {
 458                 return sobj.delete(unwrap(key, global), strict);
 459             }
 460         });
 461     }
 462 
 463     @Override
 464     public int size() {
 465         return inGlobal(new Callable<Integer>() {
 466             @Override public Integer call() {
 467                 return sobj.size();
 468             }
 469         });
 470     }
 471 
 472     @Override
 473     public Collection<Object> values() {
 474         return inGlobal(new Callable<Collection<Object>>() {
 475             @Override public Collection<Object> call() {
 476                 final List<Object>     values = new ArrayList<>(size());
 477                 final Iterator<Object> iter   = sobj.valueIterator();
 478 
 479                 while (iter.hasNext()) {
 480                     values.add(translateUndefined(wrap(iter.next(), global)));
 481                 }
 482 
 483                 return Collections.unmodifiableList(values);
 484             }
 485         });
 486     }
 487 
 488     // Support for ECMAScript Object API on mirrors
 489 
 490     /**
 491      * Return the __proto__ of this object.
 492      * @return __proto__ object.
 493      */
 494     public Object getProto() {
 495         return inGlobal(new Callable<Object>() {
 496             @Override public Object call() {
 497                 return wrap(sobj.getProto(), global);
 498             }
 499         });
 500     }
 501 
 502     /**
 503      * Set the __proto__ of this object.
 504      * @param proto new proto for this object
 505      */
 506     public void setProto(final Object proto) {
 507         inGlobal(new Callable<Void>() {
 508             @Override public Void call() {
 509                 sobj.setPrototypeOf(unwrap(proto, global));
 510                 return null;
 511             }
 512         });
 513     }
 514 
 515     /**
 516      * ECMA 8.12.1 [[GetOwnProperty]] (P)
 517      *
 518      * @param key property key
 519      *
 520      * @return Returns the Property Descriptor of the named own property of this
 521      * object, or undefined if absent.
 522      */
 523     public Object getOwnPropertyDescriptor(final String key) {
 524         return inGlobal(new Callable<Object>() {
 525             @Override public Object call() {
 526                 return wrap(sobj.getOwnPropertyDescriptor(key), global);
 527             }
 528         });
 529     }
 530 
 531     /**
 532      * return an array of own property keys associated with the object.
 533      *
 534      * @param all True if to include non-enumerable keys.
 535      * @return Array of keys.
 536      */
 537     public String[] getOwnKeys(final boolean all) {
 538         return inGlobal(new Callable<String[]>() {
 539             @Override public String[] call() {
 540                 return sobj.getOwnKeys(all);
 541             }
 542         });
 543     }
 544 
 545     /**
 546      * Flag this script object as non extensible
 547      *
 548      * @return the object after being made non extensible
 549      */
 550     public ScriptObjectMirror preventExtensions() {
 551         return inGlobal(new Callable<ScriptObjectMirror>() {
 552             @Override public ScriptObjectMirror call() {
 553                 sobj.preventExtensions();
 554                 return ScriptObjectMirror.this;
 555             }
 556         });
 557     }
 558 
 559     /**
 560      * Check if this script object is extensible
 561      * @return true if extensible
 562      */
 563     public boolean isExtensible() {
 564         return inGlobal(new Callable<Boolean>() {
 565             @Override public Boolean call() {
 566                 return sobj.isExtensible();
 567             }
 568         });
 569     }
 570 
 571     /**
 572      * ECMAScript 15.2.3.8 - seal implementation
 573      * @return the sealed script object
 574      */
 575     public ScriptObjectMirror seal() {
 576         return inGlobal(new Callable<ScriptObjectMirror>() {
 577             @Override public ScriptObjectMirror call() {
 578                 sobj.seal();
 579                 return ScriptObjectMirror.this;
 580             }
 581         });
 582     }
 583 
 584     /**
 585      * Check whether this script object is sealed
 586      * @return true if sealed
 587      */
 588     public boolean isSealed() {
 589         return inGlobal(new Callable<Boolean>() {
 590             @Override public Boolean call() {
 591                 return sobj.isSealed();
 592             }
 593         });
 594     }
 595 
 596     /**
 597      * ECMA 15.2.39 - freeze implementation. Freeze this script object
 598      * @return the frozen script object
 599      */
 600     public ScriptObjectMirror freeze() {
 601         return inGlobal(new Callable<ScriptObjectMirror>() {
 602             @Override public ScriptObjectMirror call() {
 603                 sobj.freeze();
 604                 return ScriptObjectMirror.this;
 605             }
 606         });
 607     }
 608 
 609     /**
 610      * Check whether this script object is frozen
 611      * @return true if frozen
 612      */
 613     public boolean isFrozen() {
 614         return inGlobal(new Callable<Boolean>() {
 615             @Override public Boolean call() {
 616                 return sobj.isFrozen();
 617             }
 618         });
 619     }
 620 
 621     /**
 622      * Utility to check if given object is ECMAScript undefined value
 623      *
 624      * @param obj object to check
 625      * @return true if 'obj' is ECMAScript undefined value
 626      */
 627     public static boolean isUndefined(final Object obj) {
 628         return obj == ScriptRuntime.UNDEFINED;
 629     }
 630 
 631     /**
 632      * Utilitity to convert this script object to the given type.
 633      *
 634      * @param <T> destination type to convert to
 635      * @param type destination type to convert to
 636      * @return converted object
 637      */
 638     public <T> T to(final Class<T> type) {
 639         return inGlobal(new Callable<T>() {
 640             @Override
 641             public T call() {
 642                 return type.cast(ScriptUtils.convert(sobj, type));
 643             }
 644         });
 645     }
 646 
 647     /**
 648      * Make a script object mirror on given object if needed. Also converts ConsString instances to Strings.
 649      *
 650      * @param obj object to be wrapped/converted
 651      * @param homeGlobal global to which this object belongs. Not used for ConsStrings.
 652      * @return wrapped/converted object
 653      */
 654     public static Object wrap(final Object obj, final Object homeGlobal) {
 655         if(obj instanceof ScriptObject) {
 656             return homeGlobal instanceof Global ? new ScriptObjectMirror((ScriptObject)obj, (Global)homeGlobal) : obj;
 657         }
 658         if(obj instanceof ConsString) {
 659             return obj.toString();
 660         }
 661         return obj;
 662     }
 663 
 664     /**
 665      * Unwrap a script object mirror if needed.
 666      *
 667      * @param obj object to be unwrapped
 668      * @param homeGlobal global to which this object belongs
 669      * @return unwrapped object
 670      */
 671     public static Object unwrap(final Object obj, final Object homeGlobal) {
 672         if (obj instanceof ScriptObjectMirror) {
 673             final ScriptObjectMirror mirror = (ScriptObjectMirror)obj;
 674             return (mirror.global == homeGlobal)? mirror.sobj : obj;
 675         }
 676 
 677         return obj;
 678     }
 679 
 680     /**
 681      * Wrap an array of object to script object mirrors if needed.
 682      *
 683      * @param args array to be unwrapped
 684      * @param homeGlobal global to which this object belongs
 685      * @return wrapped array
 686      */
 687     public static Object[] wrapArray(final Object[] args, final Object homeGlobal) {
 688         if (args == null || args.length == 0) {
 689             return args;
 690         }
 691 
 692         final Object[] newArgs = new Object[args.length];
 693         int index = 0;
 694         for (final Object obj : args) {
 695             newArgs[index] = wrap(obj, homeGlobal);
 696             index++;
 697         }
 698         return newArgs;
 699     }
 700 
 701     /**
 702      * Unwrap an array of script object mirrors if needed.
 703      *
 704      * @param args array to be unwrapped
 705      * @param homeGlobal global to which this object belongs
 706      * @return unwrapped array
 707      */
 708     public static Object[] unwrapArray(final Object[] args, final Object homeGlobal) {
 709         if (args == null || args.length == 0) {
 710             return args;
 711         }
 712 
 713         final Object[] newArgs = new Object[args.length];
 714         int index = 0;
 715         for (final Object obj : args) {
 716             newArgs[index] = unwrap(obj, homeGlobal);
 717             index++;
 718         }
 719         return newArgs;
 720     }
 721 
 722     /**
 723      * Are the given objects mirrors to same underlying object?
 724      *
 725      * @param obj1 first object
 726      * @param obj2 second object
 727      * @return true if obj1 and obj2 are identical script objects or mirrors of it.
 728      */
 729     public static boolean identical(final Object obj1, final Object obj2) {
 730         final Object o1 = (obj1 instanceof ScriptObjectMirror)?
 731             ((ScriptObjectMirror)obj1).sobj : obj1;
 732 
 733         final Object o2 = (obj2 instanceof ScriptObjectMirror)?
 734             ((ScriptObjectMirror)obj2).sobj : obj2;
 735 
 736         return o1 == o2;
 737     }
 738 
 739     // package-privates below this.
 740 
 741     ScriptObjectMirror(final ScriptObject sobj, final Global global) {
 742         assert sobj != null : "ScriptObjectMirror on null!";
 743         assert global != null : "home Global is null";
 744 
 745         this.sobj = sobj;
 746         this.global = global;
 747         this.strict = global.isStrictContext();
 748     }
 749 
 750     // accessors for script engine
 751     ScriptObject getScriptObject() {
 752         return sobj;
 753     }
 754 
 755     Global getHomeGlobal() {
 756         return global;
 757     }
 758 
 759     static Object translateUndefined(final Object obj) {
 760         return (obj == ScriptRuntime.UNDEFINED)? null : obj;
 761     }
 762 
 763     private int getCallSiteFlags() {
 764         return strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
 765     }
 766 
 767     // internals only below this.
 768     private <V> V inGlobal(final Callable<V> callable) {
 769         final Global oldGlobal = Context.getGlobal();
 770         final boolean globalChanged = (oldGlobal != global);
 771         if (globalChanged) {
 772             Context.setGlobal(global);
 773         }
 774         try {
 775             return callable.call();
 776         } catch (final NashornException ne) {
 777             throw ne.initEcmaError(global);
 778         } catch (final RuntimeException e) {
 779             throw e;
 780         } catch (final Exception e) {
 781             throw new AssertionError("Cannot happen", e);
 782         } finally {
 783             if (globalChanged) {
 784                 Context.setGlobal(oldGlobal);
 785             }
 786         }
 787     }
 788 
 789     @Override
 790     public double toNumber() {
 791         return inGlobal(new Callable<Double>() {
 792             @Override public Double call() {
 793                 return JSType.toNumber(sobj);
 794             }
 795         });
 796     }
 797 }