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.referenceError;
  31 import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
  32 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
  33 import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
  34 
  35 import java.lang.invoke.MethodHandle;
  36 import java.lang.invoke.MethodHandles;
  37 import java.lang.reflect.Array;
  38 import java.util.Collections;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 import java.util.Locale;
  42 import java.util.Map;
  43 import java.util.NoSuchElementException;
  44 import java.util.Objects;
  45 import jdk.internal.dynalink.beans.StaticClass;
  46 import jdk.nashorn.api.scripting.ScriptObjectMirror;
  47 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
  48 import jdk.nashorn.internal.ir.debug.JSONWriter;
  49 import jdk.nashorn.internal.parser.Lexer;
  50 import jdk.nashorn.internal.runtime.linker.Bootstrap;
  51 
  52 
  53 /**
  54  * Utilities to be called by JavaScript runtime API and generated classes.
  55  */
  56 
  57 public final class ScriptRuntime {
  58     private ScriptRuntime() {
  59     }
  60 
  61     /** Singleton representing the empty array object '[]' */
  62     public static final Object[] EMPTY_ARRAY = new Object[0];
  63 
  64     /** Unique instance of undefined. */
  65     public static final Undefined UNDEFINED = Undefined.getUndefined();
  66 
  67     /**
  68      * Unique instance of undefined used to mark empty array slots.
  69      * Can't escape the array.
  70      */
  71     public static final Undefined EMPTY = Undefined.getEmpty();
  72 
  73     /** Method handle to generic + operator, operating on objects */
  74     public static final Call ADD = staticCallNoLookup(ScriptRuntime.class, "ADD", Object.class, Object.class, Object.class);
  75 
  76     /** Method handle to generic === operator, operating on objects */
  77     public static final Call EQ_STRICT = staticCallNoLookup(ScriptRuntime.class, "EQ_STRICT", boolean.class, Object.class, Object.class);
  78 
  79     /** Method handle used to enter a {@code with} scope at runtime. */
  80     public static final Call OPEN_WITH = staticCallNoLookup(ScriptRuntime.class, "openWith", ScriptObject.class, ScriptObject.class, Object.class);
  81 
  82     /** Method handle used to exit a {@code with} scope at runtime. */
  83     public static final Call CLOSE_WITH = staticCallNoLookup(ScriptRuntime.class, "closeWith", ScriptObject.class, ScriptObject.class);
  84 
  85     /**
  86      * Method used to place a scope's variable into the Global scope, which has to be done for the
  87      * properties declared at outermost script level.
  88      */
  89     public static final Call MERGE_SCOPE = staticCallNoLookup(ScriptRuntime.class, "mergeScope", ScriptObject.class, ScriptObject.class);
  90 
  91     /**
  92      * Return an appropriate iterator for the elements in a for-in construct
  93      */
  94     public static final Call TO_PROPERTY_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toPropertyIterator", Iterator.class, Object.class);
  95 
  96     /**
  97      * Return an appropriate iterator for the elements in a for-each construct
  98      */
  99     public static final Call TO_VALUE_ITERATOR = staticCallNoLookup(ScriptRuntime.class, "toValueIterator", Iterator.class, Object.class);
 100 
 101     /**
 102       * Method handle for apply. Used from {@link ScriptFunction} for looking up calls to
 103       * call sites that are known to be megamorphic. Using an invoke dynamic here would
 104       * lead to the JVM deoptimizing itself to death
 105       */
 106     public static final Call APPLY = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "apply", Object.class, ScriptFunction.class, Object.class, Object[].class);
 107 
 108     /**
 109      * Converts a switch tag value to a simple integer. deflt value if it can't.
 110      *
 111      * @param tag   Switch statement tag value.
 112      * @param deflt default to use if not convertible.
 113      * @return int tag value (or deflt.)
 114      */
 115     public static int switchTagAsInt(final Object tag, final int deflt) {
 116         if (tag instanceof Number) {
 117             final double d = ((Number)tag).doubleValue();
 118             if (isRepresentableAsInt(d)) {
 119                 return (int)d;
 120             }
 121         }
 122 
 123         return deflt;
 124     }
 125 
 126     /**
 127      * Converts a switch tag value to a simple integer. deflt value if it can't.
 128      *
 129      * @param tag   Switch statement tag value.
 130      * @param deflt default to use if not convertible.
 131      * @return int tag value (or deflt.)
 132      */
 133     public static int switchTagAsInt(final boolean tag, final int deflt) {
 134         return deflt;
 135     }
 136 
 137     /**
 138      * Converts a switch tag value to a simple integer. deflt value if it can't.
 139      *
 140      * @param tag   Switch statement tag value.
 141      * @param deflt default to use if not convertible.
 142      * @return int tag value (or deflt.)
 143      */
 144     public static int switchTagAsInt(final long tag, final int deflt) {
 145         return isRepresentableAsInt(tag) ? (int)tag : deflt;
 146     }
 147 
 148     /**
 149      * Converts a switch tag value to a simple integer. deflt value if it can't.
 150      *
 151      * @param tag   Switch statement tag value.
 152      * @param deflt default to use if not convertible.
 153      * @return int tag value (or deflt.)
 154      */
 155     public static int switchTagAsInt(final double tag, final int deflt) {
 156         return isRepresentableAsInt(tag) ? (int)tag : deflt;
 157     }
 158 
 159     /**
 160      * This is the builtin implementation of {@code Object.prototype.toString}
 161      * @param self reference
 162      * @return string representation as object
 163      */
 164     public static String builtinObjectToString(final Object self) {
 165         String className;
 166         // Spec tells us to convert primitives by ToObject..
 167         // But we don't need to -- all we need is the right class name
 168         // of the corresponding primitive wrapper type.
 169 
 170         final JSType type = JSType.of(self);
 171 
 172         switch (type) {
 173         case BOOLEAN:
 174             className = "Boolean";
 175             break;
 176         case NUMBER:
 177             className = "Number";
 178             break;
 179         case STRING:
 180             className = "String";
 181             break;
 182         // special case of null and undefined
 183         case NULL:
 184             className = "Null";
 185             break;
 186         case UNDEFINED:
 187             className = "Undefined";
 188             break;
 189         case OBJECT:
 190         case FUNCTION:
 191             if (self instanceof ScriptObject) {
 192                 className = ((ScriptObject)self).getClassName();
 193             } else if (self instanceof ScriptObjectMirror) {
 194                 className = ((ScriptObjectMirror)self).getClassName();
 195             } else {
 196                 className = self.getClass().getName();
 197             }
 198             break;
 199         default:
 200             // Nashorn extension: use Java class name
 201             className = self.getClass().getName();
 202             break;
 203         }
 204 
 205         final StringBuilder sb = new StringBuilder();
 206         sb.append("[object ");
 207         sb.append(className);
 208         sb.append(']');
 209 
 210         return sb.toString();
 211     }
 212 
 213     /**
 214      * This is called whenever runtime wants to throw an error and wants to provide
 215      * meaningful information about an object. We don't want to call toString which
 216      * ends up calling "toString" from script world which may itself throw error.
 217      * When we want to throw an error, we don't additional error from script land
 218      * -- which may sometimes lead to infinite recursion.
 219      *
 220      * @param obj Object to converted to String safely (without calling user script)
 221      * @return safe String representation of the given object
 222      */
 223     public static String safeToString(final Object obj) {
 224         return JSType.toStringImpl(obj, true);
 225     }
 226 
 227     /**
 228      * Returns an iterator over property identifiers used in the {@code for...in} statement. Note that the ECMAScript
 229      * 5.1 specification, chapter 12.6.4. uses the terminology "property names", which seems to imply that the property
 230      * identifiers are expected to be strings, but this is not actually spelled out anywhere, and Nashorn will in some
 231      * cases deviate from this. Namely, we guarantee to always return an iterator over {@link String} values for any
 232      * built-in JavaScript object. We will however return an iterator over {@link Integer} objects for native Java
 233      * arrays and {@link List} objects, as well as arbitrary objects representing keys of a {@link Map}. Therefore, the
 234      * expression {@code typeof i} within a {@code for(i in obj)} statement can return something other than
 235      * {@code string} when iterating over native Java arrays, {@code List}, and {@code Map} objects.
 236      * @param obj object to iterate on.
 237      * @return iterator over the object's property names.
 238      */
 239     public static Iterator<?> toPropertyIterator(final Object obj) {
 240         if (obj instanceof ScriptObject) {
 241             return ((ScriptObject)obj).propertyIterator();
 242         }
 243 
 244         if (obj != null && obj.getClass().isArray()) {
 245             return new RangeIterator(Array.getLength(obj));
 246         }
 247 
 248         if (obj instanceof ScriptObjectMirror) {
 249             return ((ScriptObjectMirror)obj).keySet().iterator();
 250         }
 251 
 252         if (obj instanceof List) {
 253             return new RangeIterator(((List<?>)obj).size());
 254         }
 255 
 256         if (obj instanceof Map) {
 257             return ((Map<?,?>)obj).keySet().iterator();
 258         }
 259 
 260         return Collections.emptyIterator();
 261     }
 262 
 263     private static final class RangeIterator implements Iterator<Integer> {
 264         private final int length;
 265         private int index;
 266 
 267         RangeIterator(int length) {
 268             this.length = length;
 269         }
 270 
 271         @Override
 272         public boolean hasNext() {
 273             return index < length;
 274         }
 275 
 276         @Override
 277         public Integer next() {
 278             return index++;
 279         }
 280 
 281         @Override
 282         public void remove() {
 283             throw new UnsupportedOperationException();
 284         }
 285     }
 286 
 287     /**
 288      * Returns an iterator over property values used in the {@code for each...in} statement. Aside from built-in JS
 289      * objects, it also operates on Java arrays, any {@link Iterable}, as well as on {@link Map} objects, iterating over
 290      * map values.
 291      * @param obj object to iterate on.
 292      * @return iterator over the object's property values.
 293      */
 294     public static Iterator<?> toValueIterator(final Object obj) {
 295         if (obj instanceof ScriptObject) {
 296             return ((ScriptObject)obj).valueIterator();
 297         }
 298 
 299         if (obj != null && obj.getClass().isArray()) {
 300             final Object array  = obj;
 301             final int    length = Array.getLength(obj);
 302 
 303             return new Iterator<Object>() {
 304                 private int index = 0;
 305 
 306                 @Override
 307                 public boolean hasNext() {
 308                     return index < length;
 309                 }
 310 
 311                 @Override
 312                 public Object next() {
 313                     if (index >= length) {
 314                         throw new NoSuchElementException();
 315                     }
 316                     return Array.get(array, index++);
 317                 }
 318 
 319                 @Override
 320                 public void remove() {
 321                     throw new UnsupportedOperationException();
 322                 }
 323             };
 324         }
 325 
 326         if (obj instanceof ScriptObjectMirror) {
 327             return ((ScriptObjectMirror)obj).values().iterator();
 328         }
 329 
 330         if (obj instanceof Map) {
 331             return ((Map<?,?>)obj).values().iterator();
 332         }
 333 
 334         if (obj instanceof Iterable) {
 335             return ((Iterable<?>)obj).iterator();
 336         }
 337 
 338         return Collections.emptyIterator();
 339     }
 340 
 341     /**
 342      * Merge a scope into its prototype's map.
 343      * Merge a scope into its prototype.
 344      *
 345      * @param scope Scope to merge.
 346      * @return prototype object after merge
 347      */
 348     public static ScriptObject mergeScope(final ScriptObject scope) {
 349         final ScriptObject global = scope.getProto();
 350         global.addBoundProperties(scope);
 351         return global;
 352     }
 353 
 354     /**
 355      * Check that the target function is associated with current Context. And also make sure that 'self', if
 356      * ScriptObject, is from current context.
 357      *
 358      * Call a function given self and args. If the number of the arguments is known in advance, you can likely achieve
 359      * better performance by {@link Bootstrap#createDynamicInvoker(String, Class, Class...) creating a dynamic invoker}
 360      * for operation {@code "dyn:call"}, then using its {@link MethodHandle#invokeExact(Object...)} method instead.
 361      *
 362      * @param target ScriptFunction object.
 363      * @param self   Receiver in call.
 364      * @param args   Call arguments.
 365      * @return Call result.
 366      */
 367     public static Object checkAndApply(final ScriptFunction target, final Object self, final Object... args) {
 368         final ScriptObject global = Context.getGlobalTrusted();
 369         assert (global instanceof GlobalObject): "No current global set";
 370 
 371         if (target.getContext() != global.getContext()) {
 372             throw new IllegalArgumentException("'target' function is not from current Context");
 373         }
 374 
 375         if (self instanceof ScriptObject && ((ScriptObject)self).getContext() != global.getContext()) {
 376             throw new IllegalArgumentException("'self' object is not from current Context");
 377         }
 378 
 379         // all in order - call real 'apply'
 380         return apply(target, self, args);
 381     }
 382 
 383     /**
 384      * Call a function given self and args. If the number of the arguments is known in advance, you can likely achieve
 385      * better performance by {@link Bootstrap#createDynamicInvoker(String, Class, Class...) creating a dynamic invoker}
 386      * for operation {@code "dyn:call"}, then using its {@link MethodHandle#invokeExact(Object...)} method instead.
 387      *
 388      * @param target ScriptFunction object.
 389      * @param self   Receiver in call.
 390      * @param args   Call arguments.
 391      * @return Call result.
 392      */
 393     public static Object apply(final ScriptFunction target, final Object self, final Object... args) {
 394         try {
 395             return target.invoke(self, args);
 396         } catch (final RuntimeException | Error e) {
 397             throw e;
 398         } catch (final Throwable t) {
 399             throw new RuntimeException(t);
 400         }
 401     }
 402 
 403     /**
 404      * Check that the target function is associated with current Context.
 405      * And also make sure that 'self', if ScriptObject, is from current context.
 406      *
 407      * Call a function as a constructor given args.
 408      *
 409      * @param target ScriptFunction object.
 410      * @param args   Call arguments.
 411      * @return Constructor call result.
 412      */
 413     public static Object checkAndConstruct(final ScriptFunction target, final Object... args) {
 414         final ScriptObject global = Context.getGlobalTrusted();
 415         assert (global instanceof GlobalObject): "No current global set";
 416 
 417         if (target.getContext() != global.getContext()) {
 418             throw new IllegalArgumentException("'target' function is not from current Context");
 419         }
 420 
 421         // all in order - call real 'construct'
 422         return construct(target, args);
 423     }
 424 
 425     /**
 426      * Call a script function as a constructor with given args.
 427      *
 428      * @param target ScriptFunction object.
 429      * @param args   Call arguments.
 430      * @return Constructor call result.
 431      */
 432     public static Object construct(final ScriptFunction target, final Object... args) {
 433         try {
 434             return target.construct(args);
 435         } catch (final RuntimeException | Error e) {
 436             throw e;
 437         } catch (final Throwable t) {
 438             throw new RuntimeException(t);
 439         }
 440     }
 441 
 442     /**
 443      * Generic implementation of ECMA 9.12 - SameValue algorithm
 444      *
 445      * @param x first value to compare
 446      * @param y second value to compare
 447      *
 448      * @return true if both objects have the same value
 449      */
 450     public static boolean sameValue(final Object x, final Object y) {
 451         final JSType xType = JSType.of(x);
 452         final JSType yType = JSType.of(y);
 453 
 454         if (xType != yType) {
 455             return false;
 456         }
 457 
 458         if (xType == JSType.UNDEFINED || xType == JSType.NULL) {
 459             return true;
 460         }
 461 
 462         if (xType == JSType.NUMBER) {
 463             final double xVal = ((Number)x).doubleValue();
 464             final double yVal = ((Number)y).doubleValue();
 465 
 466             if (Double.isNaN(xVal) && Double.isNaN(yVal)) {
 467                 return true;
 468             }
 469 
 470             // checking for xVal == -0.0 and yVal == +0.0 or vice versa
 471             if (xVal == 0.0 && (Double.doubleToLongBits(xVal) != Double.doubleToLongBits(yVal))) {
 472                 return false;
 473             }
 474 
 475             return xVal == yVal;
 476         }
 477 
 478         if (xType == JSType.STRING || yType == JSType.BOOLEAN) {
 479             return x.equals(y);
 480         }
 481 
 482         return (x == y);
 483     }
 484 
 485     /**
 486      * Returns AST as JSON compatible string. This is used to
 487      * implement "parse" function in resources/parse.js script.
 488      *
 489      * @param code code to be parsed
 490      * @param name name of the code source (used for location)
 491      * @param includeLoc tells whether to include location information for nodes or not
 492      * @return JSON string representation of AST of the supplied code
 493      */
 494     public static String parse(final String code, final String name, final boolean includeLoc) {
 495         return JSONWriter.parse(Context.getContextTrusted().getEnv(), code, name, includeLoc);
 496     }
 497 
 498     /**
 499      * Test whether a char is valid JavaScript whitespace
 500      * @param ch a char
 501      * @return true if valid JavaScript whitespace
 502      */
 503     public static boolean isJSWhitespace(final char ch) {
 504         return Lexer.isJSWhitespace(ch);
 505     }
 506 
 507     /**
 508      * Entering a {@code with} node requires new scope. This is the implementation
 509      *
 510      * @param scope      existing scope
 511      * @param expression expression in with
 512      *
 513      * @return {@link WithObject} that is the new scope
 514      */
 515     public static ScriptObject openWith(final ScriptObject scope, final Object expression) {
 516         final ScriptObject global = Context.getGlobalTrusted();
 517         if (expression == UNDEFINED) {
 518             throw typeError(global, "cant.apply.with.to.undefined");
 519         } else if (expression == null) {
 520             throw typeError(global, "cant.apply.with.to.null");
 521         }
 522 
 523         final ScriptObject withObject = new WithObject(scope, JSType.toScriptObject(global, expression));



 524 
 525         return withObject;
 526     }
 527 
 528     /**
 529      * Exiting a {@code with} node requires restoring scope. This is the implementation
 530      *
 531      * @param scope existing scope
 532      *
 533      * @return restored scope
 534      */
 535     public static ScriptObject closeWith(final ScriptObject scope) {
 536         if (scope instanceof WithObject) {
 537             return scope.getProto();
 538         }
 539         return scope;
 540     }
 541 
 542     /**
 543      * ECMA 11.6.1 - The addition operator (+) - generic implementation
 544      * Compiler specializes using {@link jdk.nashorn.internal.codegen.RuntimeCallSite}
 545      * if any type information is available for any of the operands
 546      *
 547      * @param x  first term
 548      * @param y  second term
 549      *
 550      * @return result of addition
 551      */
 552     public static Object ADD(final Object x, final Object y) {
 553         // This prefix code to handle Number special is for optimization.
 554         final boolean xIsNumber = x instanceof Number;
 555         final boolean yIsNumber = y instanceof Number;
 556 
 557         if (xIsNumber && yIsNumber) {
 558              return ((Number)x).doubleValue() + ((Number)y).doubleValue();
 559         }
 560 
 561         final boolean xIsUndefined = x == UNDEFINED;
 562         final boolean yIsUndefined = y == UNDEFINED;
 563 
 564         if ((xIsNumber && yIsUndefined) || (xIsUndefined && yIsNumber) || (xIsUndefined && yIsUndefined)) {
 565             return Double.NaN;
 566         }
 567 
 568         // code below is as per the spec.
 569         final Object xPrim = JSType.toPrimitive(x);
 570         final Object yPrim = JSType.toPrimitive(y);
 571 
 572         if (xPrim instanceof String || yPrim instanceof String
 573                 || xPrim instanceof ConsString || yPrim instanceof ConsString) {
 574             return new ConsString(JSType.toCharSequence(xPrim), JSType.toCharSequence(yPrim));
 575         }
 576 
 577         return JSType.toNumber(xPrim) + JSType.toNumber(yPrim);
 578     }
 579 
 580     /**
 581      * Debugger hook.
 582      * TODO: currently unimplemented
 583      *
 584      * @return undefined
 585      */
 586     public static Object DEBUGGER() {
 587         return UNDEFINED;
 588     }
 589 
 590     /**
 591      * New hook
 592      *
 593      * @param clazz type for the clss
 594      * @param args  constructor arguments
 595      *
 596      * @return undefined
 597      */
 598     public static Object NEW(final Object clazz, final Object... args) {
 599         return UNDEFINED;
 600     }
 601 
 602     /**
 603      * ECMA 11.4.3 The typeof Operator - generic implementation
 604      *
 605      * @param object   the object from which to retrieve property to type check
 606      * @param property property in object to check
 607      *
 608      * @return type name
 609      */
 610     public static Object TYPEOF(final Object object, final Object property) {
 611         Object obj = object;
 612 
 613         if (property != null) {
 614             if (obj instanceof ScriptObject) {
 615                 obj = ((ScriptObject)obj).get(property);
 616             } else if (object instanceof Undefined) {
 617                 obj = ((Undefined)obj).get(property);
 618             } else if (object == null) {
 619                 throw typeError("cant.get.property", safeToString(property), "null");
 620             } else if (JSType.isPrimitive(obj)) {
 621                 obj = ((ScriptObject)JSType.toScriptObject(obj)).get(property);
 622             } else if (obj instanceof ScriptObjectMirror) {
 623                 obj = ((ScriptObjectMirror)obj).getMember(property.toString());
 624             } else {
 625                 obj = UNDEFINED;
 626             }
 627         }
 628 
 629         return JSType.of(obj).typeName();
 630     }
 631 
 632     /**
 633      * Throw ReferenceError when LHS of assignment or increment/decrement
 634      * operator is not an assignable node (say a literal)
 635      *
 636      * @param lhs Evaluated LHS
 637      * @param rhs Evaluated RHS
 638      * @param msg Additional LHS info for error message
 639      * @return undefined
 640      */
 641     public static Object REFERENCE_ERROR(final Object lhs, final Object rhs, final Object msg) {
 642         throw referenceError("cant.be.used.as.lhs", Objects.toString(msg));
 643     }
 644 
 645     /**
 646      * ECMA 11.4.1 - delete operation, generic implementation
 647      *
 648      * @param obj       object with property to delete
 649      * @param property  property to delete
 650      * @param strict    are we in strict mode
 651      *
 652      * @return true if property was successfully found and deleted
 653      */
 654     public static boolean DELETE(final Object obj, final Object property, final Object strict) {
 655         if (obj instanceof ScriptObject) {
 656             return ((ScriptObject)obj).delete(property, Boolean.TRUE.equals(strict));
 657         }
 658 
 659         if (obj instanceof Undefined) {
 660             return ((Undefined)obj).delete(property, false);
 661         }
 662 
 663         if (obj == null) {
 664             throw typeError("cant.delete.property", safeToString(property), "null");
 665         }
 666 
 667         if (obj instanceof ScriptObjectMirror) {
 668             return ((ScriptObjectMirror)obj).delete(property);
 669         }
 670 
 671         if (JSType.isPrimitive(obj)) {
 672             return ((ScriptObject) JSType.toScriptObject(obj)).delete(property, Boolean.TRUE.equals(strict));
 673         }
 674 
 675         // if object is not reference type, vacuously delete is successful.
 676         return true;
 677     }
 678 
 679     /**
 680      * ECMA 11.4.1 - delete operator, special case
 681      *
 682      * This is 'delete' that always fails. We have to check strict mode and throw error.
 683      * That is why this is a runtime function. Or else we could have inlined 'false'.
 684      *
 685      * @param property  property to delete
 686      * @param strict    are we in strict mode
 687      *
 688      * @return false always
 689      */
 690     public static boolean FAIL_DELETE(final Object property, final Object strict) {
 691         if (Boolean.TRUE.equals(strict)) {
 692             throw syntaxError("strict.cant.delete", safeToString(property));
 693         }
 694         return false;
 695     }
 696 
 697     /**
 698      * ECMA 11.9.1 - The equals operator (==) - generic implementation
 699      *
 700      * @param x first object to compare
 701      * @param y second object to compare
 702      *
 703      * @return true if type coerced versions of objects are equal
 704      */
 705     public static boolean EQ(final Object x, final Object y) {
 706         return equals(x, y);
 707     }
 708 
 709     /**
 710      * ECMA 11.9.2 - The does-not-equal operator (==) - generic implementation
 711      *
 712      * @param x first object to compare
 713      * @param y second object to compare
 714      *
 715      * @return true if type coerced versions of objects are not equal
 716      */
 717     public static boolean NE(final Object x, final Object y) {
 718         return !EQ(x, y);
 719     }
 720 
 721     /** ECMA 11.9.3 The Abstract Equality Comparison Algorithm */
 722     private static boolean equals(final Object x, final Object y) {
 723         final JSType xType = JSType.of(x);
 724         final JSType yType = JSType.of(y);
 725 
 726         if (xType == yType) {
 727 
 728             if (xType == JSType.UNDEFINED || xType == JSType.NULL) {
 729                 return true;
 730             }
 731 
 732             if (xType == JSType.NUMBER) {
 733                 final double xVal = ((Number)x).doubleValue();
 734                 final double yVal = ((Number)y).doubleValue();
 735                 if (Double.isNaN(xVal) || Double.isNaN(yVal)) {
 736                     return false;
 737                 }
 738 
 739                 return xVal == yVal;
 740             }
 741 
 742             if (xType == JSType.STRING) {
 743                 // String may be represented by ConsString
 744                 return x.toString().equals(y.toString());
 745             }
 746 
 747             if (xType == JSType.BOOLEAN) {
 748                 // Boolean comparison
 749                 return x.equals(y);
 750             }
 751 
 752             return x == y;
 753         }
 754 
 755         if ((xType == JSType.UNDEFINED && yType == JSType.NULL) ||
 756             (xType == JSType.NULL && yType == JSType.UNDEFINED)) {
 757             return true;
 758         }
 759 
 760         if (xType == JSType.NUMBER && yType == JSType.STRING) {
 761             return EQ(x, JSType.toNumber(y));
 762         }
 763 
 764         if (xType == JSType.STRING && yType == JSType.NUMBER) {
 765             return EQ(JSType.toNumber(x), y);
 766         }
 767 
 768         if (xType == JSType.BOOLEAN) {
 769             return EQ(JSType.toNumber(x), y);
 770         }
 771 
 772         if (yType == JSType.BOOLEAN) {
 773             return EQ(x, JSType.toNumber(y));
 774         }
 775 
 776         if ((xType == JSType.STRING || xType == JSType.NUMBER) &&
 777              (y instanceof ScriptObject))  {
 778             return EQ(x, JSType.toPrimitive(y));
 779         }
 780 
 781         if ((x instanceof ScriptObject) &&
 782             (yType == JSType.STRING || yType == JSType.NUMBER)) {
 783             return EQ(JSType.toPrimitive(x), y);
 784         }
 785 
 786         return false;
 787     }
 788 
 789     /**
 790      * ECMA 11.9.4 - The strict equal operator (===) - generic implementation
 791      *
 792      * @param x first object to compare
 793      * @param y second object to compare
 794      *
 795      * @return true if objects are equal
 796      */
 797     public static boolean EQ_STRICT(final Object x, final Object y) {
 798         return strictEquals(x, y);
 799     }
 800 
 801     /**
 802      * ECMA 11.9.5 - The strict non equal operator (!==) - generic implementation
 803      *
 804      * @param x first object to compare
 805      * @param y second object to compare
 806      *
 807      * @return true if objects are not equal
 808      */
 809     public static boolean NE_STRICT(final Object x, final Object y) {
 810         return !EQ_STRICT(x, y);
 811     }
 812 
 813     /** ECMA 11.9.6 The Strict Equality Comparison Algorithm */
 814     private static boolean strictEquals(final Object x, final Object y) {
 815         final JSType xType = JSType.of(x);
 816         final JSType yType = JSType.of(y);
 817 
 818         if (xType != yType) {
 819             return false;
 820         }
 821 
 822         if (xType == JSType.UNDEFINED || xType == JSType.NULL) {
 823             return true;
 824         }
 825 
 826         if (xType == JSType.NUMBER) {
 827             final double xVal = ((Number)x).doubleValue();
 828             final double yVal = ((Number)y).doubleValue();
 829 
 830             if (Double.isNaN(xVal) || Double.isNaN(yVal)) {
 831                 return false;
 832             }
 833 
 834             return xVal == yVal;
 835         }
 836 
 837         if (xType == JSType.STRING) {
 838             // String may be represented by ConsString
 839             return x.toString().equals(y.toString());
 840         }
 841 
 842         if (xType == JSType.BOOLEAN) {
 843             return x.equals(y);
 844         }
 845 
 846         // finally, the object identity comparison
 847         return x == y;
 848     }
 849 
 850     /**
 851      * ECMA 11.8.6 - The in operator - generic implementation
 852      *
 853      * @param property property to check for
 854      * @param obj object in which to check for property
 855      *
 856      * @return true if objects are equal
 857      */
 858     public static boolean IN(final Object property, final Object obj) {
 859         final JSType rvalType = JSType.of(obj);
 860 
 861         if (rvalType == JSType.OBJECT || rvalType == JSType.FUNCTION) {
 862             if (obj instanceof ScriptObject) {
 863                 return ((ScriptObject)obj).has(property);
 864             }
 865 
 866             return false;
 867         }
 868 
 869         throw typeError("in.with.non.object", rvalType.toString().toLowerCase(Locale.ENGLISH));
 870     }
 871 
 872     /**
 873      * ECMA 11.8.6 - The strict instanceof operator - generic implementation
 874      *
 875      * @param obj first object to compare
 876      * @param clazz type to check against
 877      *
 878      * @return true if {@code obj} is an instanceof {@code clazz}
 879      */
 880     public static boolean INSTANCEOF(final Object obj, final Object clazz) {
 881         if (clazz instanceof ScriptFunction) {
 882             if (obj instanceof ScriptObject) {
 883                 return ((ScriptObject)clazz).isInstance((ScriptObject)obj);
 884             }
 885             return false;
 886         }
 887 
 888         if (clazz instanceof StaticClass) {
 889             return ((StaticClass)clazz).getRepresentedClass().isInstance(obj);
 890         }
 891 
 892         if (clazz instanceof ScriptObjectMirror) {
 893             if (obj instanceof ScriptObjectMirror) {
 894                 return ((ScriptObjectMirror)clazz).isInstance((ScriptObjectMirror)obj);
 895             }
 896             return false;
 897         }
 898 
 899         throw typeError("instanceof.on.non.object");
 900     }
 901 
 902     /**
 903      * ECMA 11.8.1 - The less than operator ({@literal <}) - generic implementation
 904      *
 905      * @param x first object to compare
 906      * @param y second object to compare
 907      *
 908      * @return true if x is less than y
 909      */
 910     public static boolean LT(final Object x, final Object y) {
 911         final Object value = lessThan(x, y, true);
 912         return (value == UNDEFINED) ? false : (Boolean)value;
 913     }
 914 
 915     /**
 916      * ECMA 11.8.2 - The greater than operator ({@literal >}) - generic implementation
 917      *
 918      * @param x first object to compare
 919      * @param y second object to compare
 920      *
 921      * @return true if x is greater than y
 922      */
 923     public static boolean GT(final Object x, final Object y) {
 924         final Object value = lessThan(y, x, false);
 925         return (value == UNDEFINED) ? false : (Boolean)value;
 926     }
 927 
 928     /**
 929      * ECMA 11.8.3 - The less than or equal operator ({@literal <=}) - generic implementation
 930      *
 931      * @param x first object to compare
 932      * @param y second object to compare
 933      *
 934      * @return true if x is less than or equal to y
 935      */
 936     public static boolean LE(final Object x, final Object y) {
 937         final Object value = lessThan(y, x, false);
 938         return (!(Boolean.TRUE.equals(value) || value == UNDEFINED));
 939     }
 940 
 941     /**
 942      * ECMA 11.8.4 - The greater than or equal operator ({@literal >=}) - generic implementation
 943      *
 944      * @param x first object to compare
 945      * @param y second object to compare
 946      *
 947      * @return true if x is greater than or equal to y
 948      */
 949     public static boolean GE(final Object x, final Object y) {
 950         final Object value = lessThan(x, y, true);
 951         return (!(Boolean.TRUE.equals(value) || value == UNDEFINED));
 952     }
 953 
 954     /** ECMA 11.8.5 The Abstract Relational Comparison Algorithm */
 955     private static Object lessThan(final Object x, final Object y, final boolean leftFirst) {
 956         Object px, py;
 957 
 958         //support e.g. x < y should throw exception correctly if x or y are not numeric
 959         if (leftFirst) {
 960             px = JSType.toPrimitive(x, Number.class);
 961             py = JSType.toPrimitive(y, Number.class);
 962         } else {
 963             py = JSType.toPrimitive(y, Number.class);
 964             px = JSType.toPrimitive(x, Number.class);
 965         }
 966 
 967         if (JSType.of(px) == JSType.STRING && JSType.of(py) == JSType.STRING) {
 968             // May be String or ConsString
 969             return (px.toString()).compareTo(py.toString()) < 0;
 970         }
 971 
 972         final double nx = JSType.toNumber(px);
 973         final double ny = JSType.toNumber(py);
 974 
 975         if (Double.isNaN(nx) || Double.isNaN(ny)) {
 976             return UNDEFINED;
 977         }
 978 
 979         if (nx == ny) {
 980             return false;
 981         }
 982 
 983         if (nx > 0 && ny > 0 && Double.isInfinite(nx) && Double.isInfinite(ny)) {
 984             return false;
 985         }
 986 
 987         if (nx < 0 && ny < 0 && Double.isInfinite(nx) && Double.isInfinite(ny)) {
 988             return false;
 989         }
 990 
 991         return nx < ny;
 992     }
 993 
 994 }
--- EOF ---