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