1 /*
   2  * Copyright (c) 2010, 2015, 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.runtime;
  27 
  28 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
  29 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
  30 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
  31 import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
  32 import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
  33 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  34 import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
  35 import static jdk.nashorn.internal.runtime.JSType.isString;
  36 
  37 import java.lang.invoke.MethodHandle;
  38 import java.lang.invoke.MethodHandles;
  39 import java.lang.invoke.SwitchPoint;
  40 import java.lang.reflect.Array;
  41 import java.util.Collections;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.Locale;
  45 import java.util.Map;
  46 import java.util.NoSuchElementException;
  47 import java.util.Objects;
  48 import jdk.dynalink.beans.StaticClass;
  49 import jdk.nashorn.api.scripting.JSObject;
  50 import jdk.nashorn.api.scripting.ScriptObjectMirror;
  51 import jdk.nashorn.internal.codegen.ApplySpecialization;
  52 import jdk.nashorn.internal.codegen.CompilerConstants;
  53 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  54 import jdk.nashorn.internal.ir.debug.JSONWriter;
  55 import jdk.nashorn.internal.objects.AbstractIterator;
  56 import jdk.nashorn.internal.objects.Global;
  57 import jdk.nashorn.internal.objects.NativeObject;
  58 import jdk.nashorn.internal.parser.Lexer;
  59 import jdk.nashorn.internal.runtime.linker.Bootstrap;
  60 import jdk.nashorn.internal.runtime.linker.InvokeByName;
  61 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
  62 
  63 /**
  64  * Utilities to be called by JavaScript runtime API and generated classes.
  65  */
  66 
  67 public final class ScriptRuntime {
  68     private ScriptRuntime() {
  69     }
  70 
  71     /** Singleton representing the empty array object '[]' */
  72     public static final Object[] EMPTY_ARRAY = new Object[0];
  73 
  74     /** Unique instance of undefined. */
  75     public static final Undefined UNDEFINED = Undefined.getUndefined();
  76 
  77     /**
  78      * Unique instance of undefined used to mark empty array slots.
  79      * Can't escape the array.
  80      */
  81     public static final Undefined EMPTY = Undefined.getEmpty();
  82 
  83     /** Method handle to generic + operator, operating on objects */
  84     public static final Call ADD = staticCallNoLookup(ScriptRuntime.class, "ADD", Object.class, Object.class, Object.class);
  85 
  86     /** Method handle to generic === operator, operating on objects */
  87     public static final Call EQ_STRICT = staticCallNoLookup(ScriptRuntime.class, "EQ_STRICT", boolean.class, Object.class, Object.class);
  88 
  89     /** Method handle used to enter a {@code with} scope at runtime. */
  90     public static final Call OPEN_WITH = staticCallNoLookup(ScriptRuntime.class, "openWith", ScriptObject.class, ScriptObject.class, Object.class);
  91 
  92     /**
  93      * Method used to place a scope's variable into the Global scope, which has to be done for the
  94      * properties declared at outermost script level.
  95      */
  96     public static final Call MERGE_SCOPE = staticCallNoLookup(ScriptRuntime.class, "mergeScope", ScriptObject.class, ScriptObject.class);
  97 
  98     /**
  99      * Return an appropriate iterator for the elements in a for-in construct
 100      */
 101     public static final Call TO_PROPERTY_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toPropertyIterator", Iterator.class, Object.class);
 102 
 103     /**
 104      * Return an appropriate iterator for the elements in a for-each construct
 105      */
 106     public static final Call TO_VALUE_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toValueIterator", Iterator.class, Object.class);
 107 
 108     /**
 109      * Return an appropriate iterator for the elements in a ES6 for-of loop
 110      */
 111     public static final Call TO_ES6_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toES6Iterator", Iterator.class, Object.class);
 112 
 113     /**
 114       * Method handle for apply. Used from {@link ScriptFunction} for looking up calls to
 115       * call sites that are known to be megamorphic. Using an invoke dynamic here would
 116       * lead to the JVM deoptimizing itself to death
 117       */
 118     public static final Call APPLY = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "apply", Object.class, ScriptFunction.class, Object.class, Object[].class);
 119 
 120     /**
 121      * Throws a reference error for an undefined variable.
 122      */
 123     public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class);
 124 
 125     /**
 126      * Throws a reference error for an undefined variable.
 127      */
 128     public static final Call THROW_CONST_TYPE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwConstTypeError", void.class, String.class);
 129 
 130     /**
 131      * Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself.
 132      */
 133     public static final Call INVALIDATE_RESERVED_BUILTIN_NAME = staticCallNoLookup(ScriptRuntime.class, "invalidateReservedBuiltinName", void.class, String.class);
 134 
 135     /**
 136      * Converts a switch tag value to a simple integer. deflt value if it can't.
 137      *
 138      * @param tag   Switch statement tag value.
 139      * @param deflt default to use if not convertible.
 140      * @return int tag value (or deflt.)
 141      */
 142     public static int switchTagAsInt(final Object tag, final int deflt) {
 143         if (tag instanceof Number) {
 144             final double d = ((Number)tag).doubleValue();
 145             if (isRepresentableAsInt(d)) {
 146                 return (int)d;
 147             }
 148         }
 149         return deflt;
 150     }
 151 
 152     /**
 153      * Converts a switch tag value to a simple integer. deflt value if it can't.
 154      *
 155      * @param tag   Switch statement tag value.
 156      * @param deflt default to use if not convertible.
 157      * @return int tag value (or deflt.)
 158      */
 159     public static int switchTagAsInt(final boolean tag, final int deflt) {
 160         return deflt;
 161     }
 162 
 163     /**
 164      * Converts a switch tag value to a simple integer. deflt value if it can't.
 165      *
 166      * @param tag   Switch statement tag value.
 167      * @param deflt default to use if not convertible.
 168      * @return int tag value (or deflt.)
 169      */
 170     public static int switchTagAsInt(final long tag, final int deflt) {
 171         return isRepresentableAsInt(tag) ? (int)tag : deflt;
 172     }
 173 
 174     /**
 175      * Converts a switch tag value to a simple integer. deflt value if it can't.
 176      *
 177      * @param tag   Switch statement tag value.
 178      * @param deflt default to use if not convertible.
 179      * @return int tag value (or deflt.)
 180      */
 181     public static int switchTagAsInt(final double tag, final int deflt) {
 182         return isRepresentableAsInt(tag) ? (int)tag : deflt;
 183     }
 184 
 185     /**
 186      * This is the builtin implementation of {@code Object.prototype.toString}
 187      * @param self reference
 188      * @return string representation as object
 189      */
 190     public static String builtinObjectToString(final Object self) {
 191         String className;
 192         // Spec tells us to convert primitives by ToObject..
 193         // But we don't need to -- all we need is the right class name
 194         // of the corresponding primitive wrapper type.
 195 
 196         final JSType type = JSType.ofNoFunction(self);
 197 
 198         switch (type) {
 199         case BOOLEAN:
 200             className = "Boolean";
 201             break;
 202         case NUMBER:
 203             className = "Number";
 204             break;
 205         case STRING:
 206             className = "String";
 207             break;
 208         // special case of null and undefined
 209         case NULL:
 210             className = "Null";
 211             break;
 212         case UNDEFINED:
 213             className = "Undefined";
 214             break;
 215         case OBJECT:
 216             if (self instanceof ScriptObject) {
 217                 className = ((ScriptObject)self).getClassName();
 218             } else if (self instanceof JSObject) {
 219                 className = ((JSObject)self).getClassName();
 220             } else {
 221                 className = self.getClass().getName();
 222             }
 223             break;
 224         default:
 225             // Nashorn extension: use Java class name
 226             className = self.getClass().getName();
 227             break;
 228         }
 229 
 230         final StringBuilder sb = new StringBuilder();
 231         sb.append("[object ");
 232         sb.append(className);
 233         sb.append(']');
 234 
 235         return sb.toString();
 236     }
 237 
 238     /**
 239      * This is called whenever runtime wants to throw an error and wants to provide
 240      * meaningful information about an object. We don't want to call toString which
 241      * ends up calling "toString" from script world which may itself throw error.
 242      * When we want to throw an error, we don't additional error from script land
 243      * -- which may sometimes lead to infinite recursion.
 244      *
 245      * @param obj Object to converted to String safely (without calling user script)
 246      * @return safe String representation of the given object
 247      */
 248     public static String safeToString(final Object obj) {
 249         return JSType.toStringImpl(obj, true);
 250     }
 251 
 252     /**
 253      * Returns an iterator over property identifiers used in the {@code for...in} statement. Note that the ECMAScript
 254      * 5.1 specification, chapter 12.6.4. uses the terminology "property names", which seems to imply that the property
 255      * identifiers are expected to be strings, but this is not actually spelled out anywhere, and Nashorn will in some
 256      * cases deviate from this. Namely, we guarantee to always return an iterator over {@link String} values for any
 257      * built-in JavaScript object. We will however return an iterator over {@link Integer} objects for native Java
 258      * arrays and {@link List} objects, as well as arbitrary objects representing keys of a {@link Map}. Therefore, the
 259      * expression {@code typeof i} within a {@code for(i in obj)} statement can return something other than
 260      * {@code string} when iterating over native Java arrays, {@code List}, and {@code Map} objects.
 261      * @param obj object to iterate on.
 262      * @return iterator over the object's property names.
 263      */
 264     public static Iterator<?> toPropertyIterator(final Object obj) {
 265         if (obj instanceof ScriptObject) {
 266             return ((ScriptObject)obj).propertyIterator();
 267         }
 268 
 269         if (obj != null && obj.getClass().isArray()) {
 270             return new RangeIterator(Array.getLength(obj));
 271         }
 272 
 273         if (obj instanceof JSObject) {
 274             return ((JSObject)obj).keySet().iterator();
 275         }
 276 
 277         if (obj instanceof List) {
 278             return new RangeIterator(((List<?>)obj).size());
 279         }
 280 
 281         if (obj instanceof Map) {
 282             return ((Map<?,?>)obj).keySet().iterator();
 283         }
 284 
 285         final Object wrapped = Global.instance().wrapAsObject(obj);
 286         if (wrapped instanceof ScriptObject) {
 287             return ((ScriptObject)wrapped).propertyIterator();
 288         }
 289 
 290         return Collections.emptyIterator();
 291     }
 292 
 293     private static final class RangeIterator implements Iterator<Integer> {
 294         private final int length;
 295         private int index;
 296 
 297         RangeIterator(final int length) {
 298             this.length = length;
 299         }
 300 
 301         @Override
 302         public boolean hasNext() {
 303             return index < length;
 304         }
 305 
 306         @Override
 307         public Integer next() {
 308             return index++;
 309         }
 310 
 311         @Override
 312         public void remove() {
 313             throw new UnsupportedOperationException("remove");
 314         }
 315     }
 316 
 317     /**
 318      * Returns an iterator over property values used in the {@code for each...in} statement. Aside from built-in JS
 319      * objects, it also operates on Java arrays, any {@link Iterable}, as well as on {@link Map} objects, iterating over
 320      * map values.
 321      * @param obj object to iterate on.
 322      * @return iterator over the object's property values.
 323      */
 324     public static Iterator<?> toValueIterator(final Object obj) {
 325         if (obj instanceof ScriptObject) {
 326             return ((ScriptObject)obj).valueIterator();
 327         }
 328 
 329         if (obj != null && obj.getClass().isArray()) {
 330             final Object array  = obj;
 331             final int    length = Array.getLength(obj);
 332 
 333             return new Iterator<Object>() {
 334                 private int index = 0;
 335 
 336                 @Override
 337                 public boolean hasNext() {
 338                     return index < length;
 339                 }
 340 
 341                 @Override
 342                 public Object next() {
 343                     if (index >= length) {
 344                         throw new NoSuchElementException();
 345                     }
 346                     return Array.get(array, index++);
 347                 }
 348 
 349                 @Override
 350                 public void remove() {
 351                     throw new UnsupportedOperationException("remove");
 352                 }
 353             };
 354         }
 355 
 356         if (obj instanceof JSObject) {
 357             return ((JSObject)obj).values().iterator();
 358         }
 359 
 360         if (obj instanceof Map) {
 361             return ((Map<?,?>)obj).values().iterator();
 362         }
 363 
 364         if (obj instanceof Iterable) {
 365             return ((Iterable<?>)obj).iterator();
 366         }
 367 
 368         final Object wrapped = Global.instance().wrapAsObject(obj);
 369         if (wrapped instanceof ScriptObject) {
 370             return ((ScriptObject)wrapped).valueIterator();
 371         }
 372 
 373         return Collections.emptyIterator();
 374     }
 375 
 376     /**
 377      * Returns an iterator over property values used in the {@code for ... of} statement. The iterator uses the
 378      * Iterator interface defined in version 6 of the ECMAScript specification.
 379      *
 380      * @param obj object to iterate on.
 381      * @return iterator based on the ECMA 6 Iterator interface.
 382      */
 383     public static Iterator<?> toES6Iterator(final Object obj) {
 384         final Global global = Global.instance();
 385         final Object iterator = AbstractIterator.getIterator(Global.toObject(obj), global);
 386 
 387         final InvokeByName nextInvoker = AbstractIterator.getNextInvoker(global);
 388         final MethodHandle doneInvoker = AbstractIterator.getDoneInvoker(global);
 389         final MethodHandle valueInvoker = AbstractIterator.getValueInvoker(global);
 390 
 391         return new Iterator<Object>() {
 392 
 393             private Object nextResult = nextResult();
 394 
 395             private Object nextResult() {
 396                 try {
 397                     final Object next = nextInvoker.getGetter().invokeExact(iterator);
 398                     if (Bootstrap.isCallable(next)) {
 399                         return nextInvoker.getInvoker().invokeExact(next, iterator, (Object) null);
 400                     }
 401                 } catch (final RuntimeException|Error r) {
 402                     throw r;
 403                 } catch (final Throwable t) {
 404                     throw new RuntimeException(t);
 405                 }
 406                 return null;
 407             }
 408 
 409             @Override
 410             public boolean hasNext() {
 411                 if (nextResult == null) {
 412                     return false;
 413                 }
 414                 try {
 415                     final Object done = doneInvoker.invokeExact(nextResult);
 416                     return !JSType.toBoolean(done);
 417                 } catch (final RuntimeException|Error r) {
 418                     throw r;
 419                 } catch (final Throwable t) {
 420                     throw new RuntimeException(t);
 421                 }
 422             }
 423 
 424             @Override
 425             public Object next() {
 426                 if (nextResult == null) {
 427                     return Undefined.getUndefined();
 428                 }
 429                 try {
 430                     final Object result = nextResult;
 431                     nextResult = nextResult();
 432                     return valueInvoker.invokeExact(result);
 433                 } catch (final RuntimeException|Error r) {
 434                     throw r;
 435                 } catch (final Throwable t) {
 436                     throw new RuntimeException(t);
 437                 }
 438             }
 439 
 440             @Override
 441             public void remove() {
 442                 throw new UnsupportedOperationException("remove");
 443             }
 444         };
 445     }
 446 
 447     /**
 448      * Merge a scope into its prototype's map.
 449      * Merge a scope into its prototype.
 450      *
 451      * @param scope Scope to merge.
 452      * @return prototype object after merge
 453      */
 454     public static ScriptObject mergeScope(final ScriptObject scope) {
 455         final ScriptObject parentScope = scope.getProto();
 456         parentScope.addBoundProperties(scope);
 457         return parentScope;
 458     }
 459 
 460     /**
 461      * Call a function given self and args. If the number of the arguments is known in advance, you can likely achieve
 462      * better performance by creating a dynamic invoker using {@link Bootstrap#createDynamicCallInvoker(Class, Class...)}
 463      * then using its {@link MethodHandle#invokeExact(Object...)} method instead.
 464      *
 465      * @param target ScriptFunction object.
 466      * @param self   Receiver in call.
 467      * @param args   Call arguments.
 468      * @return Call result.
 469      */
 470     public static Object apply(final ScriptFunction target, final Object self, final Object... args) {
 471         try {
 472             return target.invoke(self, args);
 473         } catch (final RuntimeException | Error e) {
 474             throw e;
 475         } catch (final Throwable t) {
 476             throw new RuntimeException(t);
 477         }
 478     }
 479 
 480     /**
 481      * Throws a reference error for an undefined variable.
 482      *
 483      * @param name the variable name
 484      */
 485     public static void throwReferenceError(final String name) {
 486         throw referenceError("not.defined", name);
 487     }
 488 
 489     /**
 490      * Throws a type error for an assignment to a const.
 491      *
 492      * @param name the const name
 493      */
 494     public static void throwConstTypeError(final String name) {
 495         throw typeError("assign.constant", name);
 496     }
 497 
 498     /**
 499      * Call a script function as a constructor with given args.
 500      *
 501      * @param target ScriptFunction object.
 502      * @param args   Call arguments.
 503      * @return Constructor call result.
 504      */
 505     public static Object construct(final ScriptFunction target, final Object... args) {
 506         try {
 507             return target.construct(args);
 508         } catch (final RuntimeException | Error e) {
 509             throw e;
 510         } catch (final Throwable t) {
 511             throw new RuntimeException(t);
 512         }
 513     }
 514 
 515     /**
 516      * Generic implementation of ECMA 9.12 - SameValue algorithm
 517      *
 518      * @param x first value to compare
 519      * @param y second value to compare
 520      *
 521      * @return true if both objects have the same value
 522      */
 523     public static boolean sameValue(final Object x, final Object y) {
 524         final JSType xType = JSType.ofNoFunction(x);
 525         final JSType yType = JSType.ofNoFunction(y);
 526 
 527         if (xType != yType) {
 528             return false;
 529         }
 530 
 531         if (xType == JSType.UNDEFINED || xType == JSType.NULL) {
 532             return true;
 533         }
 534 
 535         if (xType == JSType.NUMBER) {
 536             final double xVal = ((Number)x).doubleValue();
 537             final double yVal = ((Number)y).doubleValue();
 538 
 539             if (Double.isNaN(xVal) && Double.isNaN(yVal)) {
 540                 return true;
 541             }
 542 
 543             // checking for xVal == -0.0 and yVal == +0.0 or vice versa
 544             if (xVal == 0.0 && Double.doubleToLongBits(xVal) != Double.doubleToLongBits(yVal)) {
 545                 return false;
 546             }
 547 
 548             return xVal == yVal;
 549         }
 550 
 551         if (xType == JSType.STRING || yType == JSType.BOOLEAN) {
 552             return x.equals(y);
 553         }
 554 
 555         return x == y;
 556     }
 557 
 558     /**
 559      * Returns AST as JSON compatible string. This is used to
 560      * implement "parse" function in resources/parse.js script.
 561      *
 562      * @param code code to be parsed
 563      * @param name name of the code source (used for location)
 564      * @param includeLoc tells whether to include location information for nodes or not
 565      * @return JSON string representation of AST of the supplied code
 566      */
 567     public static String parse(final String code, final String name, final boolean includeLoc) {
 568         return JSONWriter.parse(Context.getContextTrusted(), code, name, includeLoc);
 569     }
 570 
 571     /**
 572      * Test whether a char is valid JavaScript whitespace
 573      * @param ch a char
 574      * @return true if valid JavaScript whitespace
 575      */
 576     public static boolean isJSWhitespace(final char ch) {
 577         return Lexer.isJSWhitespace(ch);
 578     }
 579 
 580     /**
 581      * Entering a {@code with} node requires new scope. This is the implementation. When exiting the with statement,
 582      * use {@link ScriptObject#getProto()} on the scope.
 583      *
 584      * @param scope      existing scope
 585      * @param expression expression in with
 586      *
 587      * @return {@link WithObject} that is the new scope
 588      */
 589     public static ScriptObject openWith(final ScriptObject scope, final Object expression) {
 590         final Global global = Context.getGlobal();
 591         if (expression == UNDEFINED) {
 592             throw typeError(global, "cant.apply.with.to.undefined");
 593         } else if (expression == null) {
 594             throw typeError(global, "cant.apply.with.to.null");
 595         }
 596 
 597         if (expression instanceof ScriptObjectMirror) {
 598             final Object unwrapped = ScriptObjectMirror.unwrap(expression, global);
 599             if (unwrapped instanceof ScriptObject) {
 600                 return new WithObject(scope, (ScriptObject)unwrapped);
 601             }
 602             // foreign ScriptObjectMirror
 603             final ScriptObject exprObj = global.newObject();
 604             NativeObject.bindAllProperties(exprObj, (ScriptObjectMirror)expression);
 605             return new WithObject(scope, exprObj);
 606         }
 607 
 608         final Object wrappedExpr = JSType.toScriptObject(global, expression);
 609         if (wrappedExpr instanceof ScriptObject) {
 610             return new WithObject(scope, (ScriptObject)wrappedExpr);
 611         }
 612 
 613         throw typeError(global, "cant.apply.with.to.non.scriptobject");
 614     }
 615 
 616     /**
 617      * ECMA 11.6.1 - The addition operator (+) - generic implementation
 618      *
 619      * @param x  first term
 620      * @param y  second term
 621      *
 622      * @return result of addition
 623      */
 624     public static Object ADD(final Object x, final Object y) {
 625         // This prefix code to handle Number special is for optimization.
 626         final boolean xIsNumber = x instanceof Number;
 627         final boolean yIsNumber = y instanceof Number;
 628 
 629         if (xIsNumber && yIsNumber) {
 630              return ((Number)x).doubleValue() + ((Number)y).doubleValue();
 631         }
 632 
 633         final boolean xIsUndefined = x == UNDEFINED;
 634         final boolean yIsUndefined = y == UNDEFINED;
 635 
 636         if (xIsNumber && yIsUndefined || xIsUndefined && yIsNumber || xIsUndefined && yIsUndefined) {
 637             return Double.NaN;
 638         }
 639 
 640         // code below is as per the spec.
 641         final Object xPrim = JSType.toPrimitive(x);
 642         final Object yPrim = JSType.toPrimitive(y);
 643 
 644         if (isString(xPrim) || isString(yPrim)) {
 645             try {
 646                 return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
 647             } catch (final IllegalArgumentException iae) {
 648                 throw rangeError(iae, "concat.string.too.big");
 649             }
 650         }
 651 
 652         return JSType.toNumber(xPrim) + JSType.toNumber(yPrim);
 653     }
 654 
 655     /**
 656      * Debugger hook.
 657      * TODO: currently unimplemented
 658      *
 659      * @return undefined
 660      */
 661     public static Object DEBUGGER() {
 662         return UNDEFINED;
 663     }
 664 
 665     /**
 666      * New hook
 667      *
 668      * @param clazz type for the clss
 669      * @param args  constructor arguments
 670      *
 671      * @return undefined
 672      */
 673     public static Object NEW(final Object clazz, final Object... args) {
 674         return UNDEFINED;
 675     }
 676 
 677     /**
 678      * ECMA 11.4.3 The typeof Operator - generic implementation
 679      *
 680      * @param object   the object from which to retrieve property to type check
 681      * @param property property in object to check
 682      *
 683      * @return type name
 684      */
 685     public static Object TYPEOF(final Object object, final Object property) {
 686         Object obj = object;
 687 
 688         if (property != null) {
 689             if (obj instanceof ScriptObject) {
 690                 obj = ((ScriptObject)obj).get(property);
 691                 if(Global.isLocationPropertyPlaceholder(obj)) {
 692                     if(CompilerConstants.__LINE__.name().equals(property)) {
 693                         obj = 0;
 694                     } else {
 695                         obj = "";
 696                     }
 697                 }
 698             } else if (object instanceof Undefined) {
 699                 obj = ((Undefined)obj).get(property);
 700             } else if (object == null) {
 701                 throw typeError("cant.get.property", safeToString(property), "null");
 702             } else if (JSType.isPrimitive(obj)) {
 703                 obj = ((ScriptObject)JSType.toScriptObject(obj)).get(property);
 704             } else if (obj instanceof JSObject) {
 705                 obj = ((JSObject)obj).getMember(property.toString());
 706             } else {
 707                 obj = UNDEFINED;
 708             }
 709         }
 710 
 711         return JSType.of(obj).typeName();
 712     }
 713 
 714     /**
 715      * Throw ReferenceError when LHS of assignment or increment/decrement
 716      * operator is not an assignable node (say a literal)
 717      *
 718      * @param lhs Evaluated LHS
 719      * @param rhs Evaluated RHS
 720      * @param msg Additional LHS info for error message
 721      * @return undefined
 722      */
 723     public static Object REFERENCE_ERROR(final Object lhs, final Object rhs, final Object msg) {
 724         throw referenceError("cant.be.used.as.lhs", Objects.toString(msg));
 725     }
 726 
 727     /**
 728      * ECMA 11.4.1 - delete operation, generic implementation
 729      *
 730      * @param obj       object with property to delete
 731      * @param property  property to delete
 732      * @param strict    are we in strict mode
 733      *
 734      * @return true if property was successfully found and deleted
 735      */
 736     public static boolean DELETE(final Object obj, final Object property, final Object strict) {
 737         if (obj instanceof ScriptObject) {
 738             return ((ScriptObject)obj).delete(property, Boolean.TRUE.equals(strict));
 739         }
 740 
 741         if (obj instanceof Undefined) {
 742             return ((Undefined)obj).delete(property, false);
 743         }
 744 
 745         if (obj == null) {
 746             throw typeError("cant.delete.property", safeToString(property), "null");
 747         }
 748 
 749         if (obj instanceof ScriptObjectMirror) {
 750             return ((ScriptObjectMirror)obj).delete(property);
 751         }
 752 
 753         if (JSType.isPrimitive(obj)) {
 754             return ((ScriptObject) JSType.toScriptObject(obj)).delete(property, Boolean.TRUE.equals(strict));
 755         }
 756 
 757         if (obj instanceof JSObject) {
 758             ((JSObject)obj).removeMember(Objects.toString(property));
 759             return true;
 760         }
 761 
 762         // if object is not reference type, vacuously delete is successful.
 763         return true;
 764     }
 765 
 766     /**
 767      * ECMA 11.4.1 - delete operator, implementation for slow scopes
 768      *
 769      * This implementation of 'delete' walks the scope chain to find the scope that contains the
 770      * property to be deleted, then invokes delete on it.
 771      *
 772      * @param obj       top scope object
 773      * @param property  property to delete
 774      * @param strict    are we in strict mode
 775      *
 776      * @return true if property was successfully found and deleted
 777      */
 778     public static boolean SLOW_DELETE(final Object obj, final Object property, final Object strict) {
 779         if (obj instanceof ScriptObject) {
 780             ScriptObject sobj = (ScriptObject) obj;
 781             final String key = property.toString();
 782             while (sobj != null && sobj.isScope()) {
 783                 final FindProperty find = sobj.findProperty(key, false);
 784                 if (find != null) {
 785                     return sobj.delete(key, Boolean.TRUE.equals(strict));
 786                 }
 787                 sobj = sobj.getProto();
 788             }
 789         }
 790         return DELETE(obj, property, strict);
 791     }
 792 
 793     /**
 794      * ECMA 11.4.1 - delete operator, special case
 795      *
 796      * This is 'delete' that always fails. We have to check strict mode and throw error.
 797      * That is why this is a runtime function. Or else we could have inlined 'false'.
 798      *
 799      * @param property  property to delete
 800      * @param strict    are we in strict mode
 801      *
 802      * @return false always
 803      */
 804     public static boolean FAIL_DELETE(final Object property, final Object strict) {
 805         if (Boolean.TRUE.equals(strict)) {
 806             throw syntaxError("strict.cant.delete", safeToString(property));
 807         }
 808         return false;
 809     }
 810 
 811     /**
 812      * ECMA 11.9.1 - The equals operator (==) - generic implementation
 813      *
 814      * @param x first object to compare
 815      * @param y second object to compare
 816      *
 817      * @return true if type coerced versions of objects are equal
 818      */
 819     public static boolean EQ(final Object x, final Object y) {
 820         return equals(x, y);
 821     }
 822 
 823     /**
 824      * ECMA 11.9.2 - The does-not-equal operator (==) - generic implementation
 825      *
 826      * @param x first object to compare
 827      * @param y second object to compare
 828      *
 829      * @return true if type coerced versions of objects are not equal
 830      */
 831     public static boolean NE(final Object x, final Object y) {
 832         return !EQ(x, y);
 833     }
 834 
 835     /** ECMA 11.9.3 The Abstract Equality Comparison Algorithm */
 836     private static boolean equals(final Object x, final Object y) {
 837         // We want to keep this method small so we skip reference equality check for numbers
 838         // as NaN should return false when compared to itself (JDK-8043608).
 839         if (x == y && !(x instanceof Number)) {
 840             return true;
 841         }
 842         if (x instanceof ScriptObject && y instanceof ScriptObject) {
 843             return false; // x != y
 844         }
 845         if (x instanceof ScriptObjectMirror || y instanceof ScriptObjectMirror) {
 846             return ScriptObjectMirror.identical(x, y);
 847         }
 848         return equalValues(x, y);
 849     }
 850 
 851     /**
 852      * Extracted portion of {@code equals()} that compares objects by value (or by reference, if no known value
 853      * comparison applies).
 854      * @param x one value
 855      * @param y another value
 856      * @return true if they're equal according to 11.9.3
 857      */
 858     private static boolean equalValues(final Object x, final Object y) {
 859         final JSType xType = JSType.ofNoFunction(x);
 860         final JSType yType = JSType.ofNoFunction(y);
 861 
 862         if (xType == yType) {
 863             return equalSameTypeValues(x, y, xType);
 864         }
 865 
 866         return equalDifferentTypeValues(x, y, xType, yType);
 867     }
 868 
 869     /**
 870      * Extracted portion of {@link #equals(Object, Object)} and {@link #strictEquals(Object, Object)} that compares
 871      * values belonging to the same JSType.
 872      * @param x one value
 873      * @param y another value
 874      * @param type the common type for the values
 875      * @return true if they're equal
 876      */
 877     private static boolean equalSameTypeValues(final Object x, final Object y, final JSType type) {
 878         if (type == JSType.UNDEFINED || type == JSType.NULL) {
 879             return true;
 880         }
 881 
 882         if (type == JSType.NUMBER) {
 883             return ((Number)x).doubleValue() == ((Number)y).doubleValue();
 884         }
 885 
 886         if (type == JSType.STRING) {
 887             // String may be represented by ConsString
 888             return x.toString().equals(y.toString());
 889         }
 890 
 891         if (type == JSType.BOOLEAN) {
 892             return ((Boolean)x).booleanValue() == ((Boolean)y).booleanValue();
 893         }
 894 
 895         return x == y;
 896     }
 897 
 898     /**
 899      * Extracted portion of {@link #equals(Object, Object)} that compares values belonging to different JSTypes.
 900      * @param x one value
 901      * @param y another value
 902      * @param xType the type for the value x
 903      * @param yType the type for the value y
 904      * @return true if they're equal
 905      */
 906     private static boolean equalDifferentTypeValues(final Object x, final Object y, final JSType xType, final JSType yType) {
 907         if (isUndefinedAndNull(xType, yType) || isUndefinedAndNull(yType, xType)) {
 908             return true;
 909         } else if (isNumberAndString(xType, yType)) {
 910             return equalNumberToString(x, y);
 911         } else if (isNumberAndString(yType, xType)) {
 912             // Can reverse order as both are primitives
 913             return equalNumberToString(y, x);
 914         } else if (xType == JSType.BOOLEAN) {
 915             return equalBooleanToAny(x, y);
 916         } else if (yType == JSType.BOOLEAN) {
 917             // Can reverse order as y is primitive
 918             return equalBooleanToAny(y, x);
 919         } else if (isPrimitiveAndObject(xType, yType)) {
 920             return equalWrappedPrimitiveToObject(x, y);
 921         } else if (isPrimitiveAndObject(yType, xType)) {
 922             // Can reverse order as y is primitive
 923             return equalWrappedPrimitiveToObject(y, x);
 924         }
 925 
 926         return false;
 927     }
 928 
 929     private static boolean isUndefinedAndNull(final JSType xType, final JSType yType) {
 930         return xType == JSType.UNDEFINED && yType == JSType.NULL;
 931     }
 932 
 933     private static boolean isNumberAndString(final JSType xType, final JSType yType) {
 934         return xType == JSType.NUMBER && yType == JSType.STRING;
 935     }
 936 
 937     private static boolean isPrimitiveAndObject(final JSType xType, final JSType yType) {
 938         return (xType == JSType.NUMBER || xType == JSType.STRING || xType == JSType.SYMBOL) && yType == JSType.OBJECT;
 939     }
 940 
 941     private static boolean equalNumberToString(final Object num, final Object str) {
 942         // Specification says comparing a number to string should be done as "equals(num, JSType.toNumber(str))". We
 943         // can short circuit it to this as we know that "num" is a number, so it'll end up being a number-number
 944         // comparison.
 945         return ((Number)num).doubleValue() == JSType.toNumber(str.toString());
 946     }
 947 
 948     private static boolean equalBooleanToAny(final Object bool, final Object any) {
 949         return equals(JSType.toNumber((Boolean)bool), any);
 950     }
 951 
 952     private static boolean equalWrappedPrimitiveToObject(final Object numOrStr, final Object any) {
 953         return equals(numOrStr, JSType.toPrimitive(any));
 954     }
 955 
 956     /**
 957      * ECMA 11.9.4 - The strict equal operator (===) - generic implementation
 958      *
 959      * @param x first object to compare
 960      * @param y second object to compare
 961      *
 962      * @return true if objects are equal
 963      */
 964     public static boolean EQ_STRICT(final Object x, final Object y) {
 965         return strictEquals(x, y);
 966     }
 967 
 968     /**
 969      * ECMA 11.9.5 - The strict non equal operator (!==) - generic implementation
 970      *
 971      * @param x first object to compare
 972      * @param y second object to compare
 973      *
 974      * @return true if objects are not equal
 975      */
 976     public static boolean NE_STRICT(final Object x, final Object y) {
 977         return !EQ_STRICT(x, y);
 978     }
 979 
 980     /** ECMA 11.9.6 The Strict Equality Comparison Algorithm */
 981     private static boolean strictEquals(final Object x, final Object y) {
 982         // NOTE: you might be tempted to do a quick x == y comparison. Remember, though, that any Double object having
 983         // NaN value is not equal to itself by value even though it is referentially.
 984 
 985         final JSType xType = JSType.ofNoFunction(x);
 986         final JSType yType = JSType.ofNoFunction(y);
 987 
 988         if (xType != yType) {
 989             return false;
 990         }
 991 
 992         return equalSameTypeValues(x, y, xType);
 993     }
 994 
 995     /**
 996      * ECMA 11.8.6 - The in operator - generic implementation
 997      *
 998      * @param property property to check for
 999      * @param obj object in which to check for property
1000      *
1001      * @return true if objects are equal
1002      */
1003     public static boolean IN(final Object property, final Object obj) {
1004         final JSType rvalType = JSType.ofNoFunction(obj);
1005 
1006         if (rvalType == JSType.OBJECT) {
1007             if (obj instanceof ScriptObject) {
1008                 return ((ScriptObject)obj).has(property);
1009             }
1010 
1011             if (obj instanceof JSObject) {
1012                 return ((JSObject)obj).hasMember(Objects.toString(property));
1013             }
1014 
1015             return false;
1016         }
1017 
1018         throw typeError("in.with.non.object", rvalType.toString().toLowerCase(Locale.ENGLISH));
1019     }
1020 
1021     /**
1022      * ECMA 11.8.6 - The strict instanceof operator - generic implementation
1023      *
1024      * @param obj first object to compare
1025      * @param clazz type to check against
1026      *
1027      * @return true if {@code obj} is an instanceof {@code clazz}
1028      */
1029     public static boolean INSTANCEOF(final Object obj, final Object clazz) {
1030         if (clazz instanceof ScriptFunction) {
1031             if (obj instanceof ScriptObject) {
1032                 return ((ScriptObject)clazz).isInstance((ScriptObject)obj);
1033             }
1034             return false;
1035         }
1036 
1037         if (clazz instanceof StaticClass) {
1038             return ((StaticClass)clazz).getRepresentedClass().isInstance(obj);
1039         }
1040 
1041         if (clazz instanceof JSObject) {
1042             return ((JSObject)clazz).isInstance(obj);
1043         }
1044 
1045         // provide for reverse hook
1046         if (obj instanceof JSObject) {
1047             return ((JSObject)obj).isInstanceOf(clazz);
1048         }
1049 
1050         throw typeError("instanceof.on.non.object");
1051     }
1052 
1053     /**
1054      * ECMA 11.8.1 - The less than operator ({@literal <}) - generic implementation
1055      *
1056      * @param x first object to compare
1057      * @param y second object to compare
1058      *
1059      * @return true if x is less than y
1060      */
1061     public static boolean LT(final Object x, final Object y) {
1062         final Object px = JSType.toPrimitive(x, Number.class);
1063         final Object py = JSType.toPrimitive(y, Number.class);
1064 
1065         return areBothString(px, py) ? px.toString().compareTo(py.toString()) < 0 :
1066             JSType.toNumber(px) < JSType.toNumber(py);
1067     }
1068 
1069     private static boolean areBothString(final Object x, final Object y) {
1070         return isString(x) && isString(y);
1071     }
1072 
1073     /**
1074      * ECMA 11.8.2 - The greater than operator ({@literal >}) - generic implementation
1075      *
1076      * @param x first object to compare
1077      * @param y second object to compare
1078      *
1079      * @return true if x is greater than y
1080      */
1081     public static boolean GT(final Object x, final Object y) {
1082         final Object px = JSType.toPrimitive(x, Number.class);
1083         final Object py = JSType.toPrimitive(y, Number.class);
1084 
1085         return areBothString(px, py) ? px.toString().compareTo(py.toString()) > 0 :
1086             JSType.toNumber(px) > JSType.toNumber(py);
1087     }
1088 
1089     /**
1090      * ECMA 11.8.3 - The less than or equal operator ({@literal <=}) - generic implementation
1091      *
1092      * @param x first object to compare
1093      * @param y second object to compare
1094      *
1095      * @return true if x is less than or equal to y
1096      */
1097     public static boolean LE(final Object x, final Object y) {
1098         final Object px = JSType.toPrimitive(x, Number.class);
1099         final Object py = JSType.toPrimitive(y, Number.class);
1100 
1101         return areBothString(px, py) ? px.toString().compareTo(py.toString()) <= 0 :
1102             JSType.toNumber(px) <= JSType.toNumber(py);
1103     }
1104 
1105     /**
1106      * ECMA 11.8.4 - The greater than or equal operator ({@literal >=}) - generic implementation
1107      *
1108      * @param x first object to compare
1109      * @param y second object to compare
1110      *
1111      * @return true if x is greater than or equal to y
1112      */
1113     public static boolean GE(final Object x, final Object y) {
1114         final Object px = JSType.toPrimitive(x, Number.class);
1115         final Object py = JSType.toPrimitive(y, Number.class);
1116 
1117         return areBothString(px, py) ? px.toString().compareTo(py.toString()) >= 0 :
1118             JSType.toNumber(px) >= JSType.toNumber(py);
1119     }
1120 
1121     /**
1122      * Tag a reserved name as invalidated - used when someone writes
1123      * to a property with this name - overly conservative, but link time
1124      * is too late to apply e.g. apply-&gt;call specialization
1125      * @param name property name
1126      */
1127     public static void invalidateReservedBuiltinName(final String name) {
1128         final Context context = Context.getContextTrusted();
1129         final SwitchPoint sp = context.getBuiltinSwitchPoint(name);
1130         assert sp != null;
1131         context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
1132         SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
1133     }
1134 
1135     /**
1136      * ES6 12.2.9.3 Runtime Semantics: GetTemplateObject(templateLiteral).
1137      *
1138      * @param rawStrings array of template raw values
1139      * @param cookedStrings array of template values
1140      * @return template object
1141      */
1142     public static ScriptObject GET_TEMPLATE_OBJECT(final Object rawStrings, final Object cookedStrings) {
1143         final ScriptObject template = (ScriptObject)cookedStrings;
1144         final ScriptObject rawObj = (ScriptObject)rawStrings;
1145         assert rawObj.getArray().length() == template.getArray().length();
1146         template.addOwnProperty("raw", Property.NOT_WRITABLE | Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, rawObj.freeze());
1147         template.freeze();
1148         return template;
1149     }
1150 }