All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gargoylesoftware.htmlunit.javascript.host.Symbol Maven / Gradle / Ivy

/*
 * Copyright (c) 2002-2016 Gargoyle Software Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gargoylesoftware.htmlunit.javascript.host;

import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.CHROME;
import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.EDGE;
import static com.gargoylesoftware.htmlunit.javascript.configuration.BrowserName.FF;

import java.util.HashMap;
import java.util.Map.Entry;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
import com.gargoylesoftware.htmlunit.javascript.configuration.AbstractJavaScriptConfiguration;
import com.gargoylesoftware.htmlunit.javascript.configuration.ClassConfiguration;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxStaticFunction;
import com.gargoylesoftware.htmlunit.javascript.configuration.JsxStaticGetter;
import com.gargoylesoftware.htmlunit.javascript.configuration.WebBrowser;

import net.sourceforge.htmlunit.corejs.javascript.Context;
import net.sourceforge.htmlunit.corejs.javascript.Function;
import net.sourceforge.htmlunit.corejs.javascript.ScriptRuntime;
import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
import net.sourceforge.htmlunit.corejs.javascript.Undefined;

/**
 * A JavaScript object for {@code Symbol}.
 *
 * @author Ahmed Ashour
 */
@JsxClass(browsers = { @WebBrowser(CHROME), @WebBrowser(value = FF, minVersion = 38), @WebBrowser(EDGE) })
public class Symbol extends SimpleScriptable {

    static final String ITERATOR_STRING = "Symbol(Symbol.iterator)";
    private static java.util.Map> SYMBOL_MAP_ = new HashMap<>();

    private Object name_;

    /**
     * Default constructor.
     */
    public Symbol() {
    }

    /**
     * Creates an instance.
     * @param name the name
     */
    @JsxConstructor
    public Symbol(final Object name) {
        name_ = name;
        for (final StackTraceElement stackElements : new Throwable().getStackTrace()) {
            if (stackElements.getClassName().contains("BaseFunction")) {
                throw ScriptRuntime.typeError("Symbol is not a constructor");
            }
        }
    }

    /**
     * Returns the {@code iterator} static property.
     * @param thisObj the scriptable
     * @return the {@code iterator} static property
     */
    @JsxStaticGetter
    public static Symbol getIterator(final Scriptable thisObj) {
        return getSymbol(thisObj, "iterator");
    }

    private static Symbol getSymbol(final Scriptable thisObj, final String name) {
        final SimpleScriptable scope = (SimpleScriptable) thisObj.getParentScope();
        final BrowserVersion browserVersion = scope.getBrowserVersion();

        java.util.Map map = SYMBOL_MAP_.get(browserVersion);
        if (map == null) {
            map = new HashMap<>();
            SYMBOL_MAP_.put(browserVersion, map);
        }

        Symbol symbol = map.get(name);
        if (symbol == null) {
            symbol = new Symbol(name);
            symbol.setParentScope(scope);
            symbol.setPrototype(scope.getPrototype(symbol.getClass()));
            map.put(name, symbol);
        }

        return symbol;
    }

    /**
     * Returns the {@code unscopables} static property.
     * @param thisObj the scriptable
     * @return the {@code unscopables} static property
     */
    @JsxStaticGetter(@WebBrowser(CHROME))
    public static Symbol getUnscopables(final Scriptable thisObj) {
        return getSymbol(thisObj, "unscopables");
    }

    /**
     * Returns the {@code isConcatSpreadable} static property.
     * @param thisObj the scriptable
     * @return the {@code isConcatSpreadable} static property
     */
    @JsxStaticGetter(@WebBrowser(CHROME))
    public static Symbol getIsConcatSpreadable(final Scriptable thisObj) {
        return getSymbol(thisObj, "isConcatSpreadable");
    }

    /**
     * Returns the {@code toPrimitive} static property.
     * @param thisObj the scriptable
     * @return the {@code toPrimitive} static property
     */
    @JsxStaticGetter({ @WebBrowser(CHROME), @WebBrowser(value = FF, minVersion = 45) })
    public static Symbol getToPrimitive(final Scriptable thisObj) {
        return getSymbol(thisObj, "toPrimitive");
    }

    /**
     * Returns the {@code toStringTag} static property.
     * @param thisObj the scriptable
     * @return the {@code toStringTag} static property
     */
    @JsxStaticGetter(@WebBrowser(CHROME))
    public static Symbol getToStringTag(final Scriptable thisObj) {
        return getSymbol(thisObj, "toStringTag");
    }

    /**
     * Returns the {@code match} static property.
     * @param thisObj the scriptable
     * @return the {@code match} static property
     */
    @JsxStaticGetter(@WebBrowser(value = FF, minVersion = 45))
    public static Symbol getMatch(final Scriptable thisObj) {
        return getSymbol(thisObj, "match");
    }

    /**
     * Returns the {@code species} static property.
     * @param thisObj the scriptable
     * @return the {@code species} static property
     */
    @JsxStaticGetter(@WebBrowser(value = FF, minVersion = 45))
    public static Symbol getSpecies(final Scriptable thisObj) {
        return getSymbol(thisObj, "species");
    }

    /**
     * Searches for existing symbols in a runtime-wide symbol registry with the given key and returns it if found.
     * Otherwise a new symbol gets created in the global symbol registry with this key.
     * @param context the context
     * @param thisObj this object
     * @param args the arguments
     * @param function the function
     * @return the symbol
     */
    @JsxStaticFunction(functionName = "for")
    public static Symbol forFunction(final Context context, final Scriptable thisObj, final Object[] args,
            final Function function) {
        final String key = Context.toString(args.length != 0 ? args[0] : Undefined.instance);

        Symbol symbol = (Symbol) ((ScriptableObject) thisObj).get(key);
        if (symbol == null) {
            final SimpleScriptable parentScope = (SimpleScriptable) thisObj.getParentScope();

            symbol = new Symbol(key);
            symbol.setParentScope(parentScope);
            symbol.setPrototype(parentScope.getPrototype(symbol.getClass()));
            thisObj.put(key, thisObj, symbol);
        }
        return symbol;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String getTypeOf() {
        return "symbol";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @JsxFunction
    public String toString() {
        String name;
        if (name_ == Undefined.instance) {
            name = "";
        }
        else {
            name = Context.toString(name_);
            final ClassConfiguration config = AbstractJavaScriptConfiguration
                    .getClassConfiguration(getClass(), getBrowserVersion());

            for (final Entry propertyEntry
                    : config.getStaticPropertyEntries()) {
                if (propertyEntry.getKey().equals(name)) {
                    name = "Symbol." + name;
                    break;
                }
            }
        }
        return "Symbol(" + name + ')';
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Object getDefaultValue(final Class hint) {
        if (String.class.equals(hint) || hint == null) {
            return toString();
        }
        return super.getDefaultValue(hint);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy