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