Undefined.java revision 1368:ed56500172f4
138494Sobrien/*
2174294Sobrien * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
338494Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
438494Sobrien *
538494Sobrien * This code is free software; you can redistribute it and/or modify it
638494Sobrien * under the terms of the GNU General Public License version 2 only, as
738494Sobrien * published by the Free Software Foundation.  Oracle designates this
838494Sobrien * particular file as subject to the "Classpath" exception as provided
938494Sobrien * by Oracle in the LICENSE file that accompanied this code.
1038494Sobrien *
1138494Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT
1238494Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1338494Sobrien * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1438494Sobrien * version 2 for more details (a copy is included in the LICENSE file that
1538494Sobrien * accompanied this code).
1638494Sobrien *
1738494Sobrien * You should have received a copy of the GNU General Public License version
1838494Sobrien * 2 along with this work; if not, write to the Free Software Foundation,
1938494Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2042629Sobrien *
2138494Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2238494Sobrien * or visit www.oracle.com if you need additional information or have any
2338494Sobrien * questions.
2438494Sobrien */
2538494Sobrien
2638494Sobrienpackage jdk.nashorn.internal.runtime;
2738494Sobrien
2838494Sobrienimport static jdk.nashorn.internal.lookup.Lookup.MH;
2938494Sobrienimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
3038494Sobrien
3138494Sobrienimport java.lang.invoke.MethodHandle;
3238494Sobrienimport java.lang.invoke.MethodHandles;
3338494Sobrienimport jdk.internal.dynalink.CallSiteDescriptor;
3438494Sobrienimport jdk.internal.dynalink.linker.GuardedInvocation;
3538494Sobrienimport jdk.internal.dynalink.support.CallSiteDescriptorFactory;
3638494Sobrienimport jdk.internal.dynalink.support.Guards;
3738494Sobrienimport jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
3838494Sobrien
3938494Sobrien/**
40174294Sobrien * Unique instance of this class is used to represent JavaScript undefined.
4138494Sobrien */
4238494Sobrienpublic final class Undefined extends DefaultPropertyAccess {
4338494Sobrien
4438494Sobrien    private Undefined() {
4538494Sobrien    }
4638494Sobrien
4738494Sobrien    private static final Undefined UNDEFINED = new Undefined();
4838494Sobrien    private static final Undefined EMPTY     = new Undefined();
4938494Sobrien
5038494Sobrien    // Guard used for indexed property access/set on the Undefined instance
5138494Sobrien    private static final MethodHandle UNDEFINED_GUARD = Guards.getIdentityGuard(UNDEFINED);
5238494Sobrien
5338494Sobrien    /**
5438494Sobrien     * Get the value of {@code undefined}, this is represented as a global singleton
5538494Sobrien     * instance of this class. It can always be reference compared
5638494Sobrien     *
57174294Sobrien     * @return the undefined object
5838494Sobrien     */
5938494Sobrien    public static Undefined getUndefined() {
6038494Sobrien        return UNDEFINED;
6138494Sobrien    }
6238494Sobrien
6382794Sobrien    /**
6438494Sobrien     * Get the value of {@code empty}. This is represented as a global singleton
6538494Sobrien     * instanceof this class. It can always be reference compared.
6638494Sobrien     * <p>
6738494Sobrien     * We need empty to differentiate behavior in things like array iterators
6838494Sobrien     * <p>
6938494Sobrien     * @return the empty object
7038494Sobrien     */
7138494Sobrien    public static Undefined getEmpty() {
7238494Sobrien        return EMPTY;
7338494Sobrien    }
7438494Sobrien
7538494Sobrien    /**
7638494Sobrien     * Get the class name of Undefined
7738494Sobrien     * @return "Undefined"
7838494Sobrien     */
7938494Sobrien    @SuppressWarnings("static-method")
8038494Sobrien    public String getClassName() {
8138494Sobrien        return "Undefined";
8238494Sobrien    }
8338494Sobrien
8438494Sobrien    @Override
8538494Sobrien    public String toString() {
8638494Sobrien        return "undefined";
8738494Sobrien    }
88174294Sobrien
8938494Sobrien    /**
9038494Sobrien     * Lookup the appropriate method for an invoke dynamic call.
9138494Sobrien     * @param desc The invoke dynamic callsite descriptor.
9238494Sobrien     * @return GuardedInvocation to be invoked at call site.
9338494Sobrien     */
9438494Sobrien    public static GuardedInvocation lookup(final CallSiteDescriptor desc) {
9538494Sobrien        final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
9638494Sobrien
9738494Sobrien        switch (operator) {
98174294Sobrien        case "new":
99174294Sobrien        case "call": {
100174294Sobrien            final String name = desc.getNameTokenCount() > 2? desc.getNameToken(2) : null;
101174294Sobrien            final String msg = name != null? "cant.call.undefined.arg" : "cant.call.undefined";
102174294Sobrien            throw typeError(msg, name);
103174294Sobrien        }
10438494Sobrien
10538494Sobrien        case "callMethod":
10638494Sobrien            throw lookupTypeError("cant.read.property.of.undefined", desc);
10738494Sobrien        // NOTE: we support getElem and setItem as JavaScript doesn't distinguish items from properties. Nashorn itself
10838494Sobrien        // emits "dyn:getProp:identifier" for "<expr>.<identifier>" and "dyn:getElem" for "<expr>[<expr>]", but we are
10938494Sobrien        // more flexible here and dispatch not on operation name (getProp vs. getElem), but rather on whether the
11038494Sobrien        // operation has an associated name or not.
11138494Sobrien        case "getProp":
11238494Sobrien        case "getElem":
11338494Sobrien        case "getMethod":
11438494Sobrien            if (desc.getNameTokenCount() < 3) {
11538494Sobrien                return findGetIndexMethod(desc);
11638494Sobrien            }
11738494Sobrien            return findGetMethod(desc);
11838494Sobrien        case "setProp":
11938494Sobrien        case "setElem":
12038494Sobrien            if (desc.getNameTokenCount() < 3) {
12138494Sobrien                return findSetIndexMethod(desc);
12238494Sobrien            }
12338494Sobrien            return findSetMethod(desc);
12438494Sobrien        default:
12538494Sobrien            break;
12638494Sobrien        }
12738494Sobrien
12838494Sobrien        return null;
12938494Sobrien    }
13038494Sobrien
13138494Sobrien    private static ECMAException lookupTypeError(final String msg, final CallSiteDescriptor desc) {
13238494Sobrien        final String name = desc.getNameToken(2);
13338494Sobrien        return typeError(msg, name != null && !name.isEmpty()? name : null);
13438494Sobrien    }
13538494Sobrien
13638494Sobrien    private static final MethodHandle GET_METHOD = findOwnMH("get", Object.class, Object.class);
13738494Sobrien    private static final MethodHandle SET_METHOD = MH.insertArguments(findOwnMH("set", void.class, Object.class, Object.class, int.class), 3, NashornCallSiteDescriptor.CALLSITE_STRICT);
13838494Sobrien
13938494Sobrien    private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) {
14038494Sobrien        return new GuardedInvocation(MH.insertArguments(GET_METHOD, 1, desc.getNameToken(2)), UNDEFINED_GUARD).asType(desc);
14138494Sobrien    }
14238494Sobrien
14338494Sobrien    private static GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc) {
14438494Sobrien        return new GuardedInvocation(GET_METHOD, UNDEFINED_GUARD).asType(desc);
14538494Sobrien    }
14638494Sobrien
14738494Sobrien    private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) {
14838494Sobrien        return new GuardedInvocation(MH.insertArguments(SET_METHOD, 1, desc.getNameToken(2)), UNDEFINED_GUARD).asType(desc);
14938494Sobrien    }
15038494Sobrien
15138494Sobrien    private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc) {
15238494Sobrien        return new GuardedInvocation(SET_METHOD, UNDEFINED_GUARD).asType(desc);
15338494Sobrien    }
15438494Sobrien
15538494Sobrien    @Override
15638494Sobrien    public Object get(final Object key) {
15738494Sobrien        throw typeError("cant.read.property.of.undefined", ScriptRuntime.safeToString(key));
15838494Sobrien    }
15938494Sobrien
16038494Sobrien    @Override
16138494Sobrien    public void set(final Object key, final Object value, final int flags) {
16238494Sobrien        throw typeError("cant.set.property.of.undefined", ScriptRuntime.safeToString(key));
16338494Sobrien    }
16438494Sobrien
16538494Sobrien    @Override
16638494Sobrien    public boolean delete(final Object key, final boolean strict) {
16738494Sobrien        throw typeError("cant.delete.property.of.undefined", ScriptRuntime.safeToString(key));
16838494Sobrien    }
16938494Sobrien
17038494Sobrien    @Override
17138494Sobrien    public boolean has(final Object key) {
17238494Sobrien        return false;
17338494Sobrien    }
17438494Sobrien
17538494Sobrien    @Override
17638494Sobrien    public boolean hasOwnProperty(final Object key) {
17738494Sobrien        return false;
17838494Sobrien    }
17938494Sobrien
18038494Sobrien    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
18138494Sobrien        return MH.findVirtual(MethodHandles.lookup(), Undefined.class, name, MH.type(rtype, types));
18238494Sobrien    }
18338494Sobrien}
18438494Sobrien