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