src/jdk/nashorn/internal/runtime/ScriptRuntime.java

Wed, 05 Nov 2014 12:34:06 +0100

author
lagergren
date
Wed, 05 Nov 2014 12:34:06 +0100
changeset 1086
d0b26e6f602c
parent 1028
d79265f2fa92
child 1205
4112748288bb
child 1243
2b51c0b3f463
permissions
-rw-r--r--

8057825: Bug in apply specialization - if an apply specialization that is available doesn't fit, a new one wouldn't be installed, if the new code generated as a specialization didn't manage to do the apply specialization. Basically changing a conditional to an unconditional.
Reviewed-by: attila, hannesw

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

mercurial