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