alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (Global.java)

This example Java source code file (Global.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

class, classreference, global, methodhandle, nativearray, nativejavapackage, nativeregexp, object, override, propertymap, reflection, scriptfunction, scriptobject, string, threading, threads, undefined, util

The Global.java Java example source code

/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.nashorn.internal.objects;

import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.GlobalObject;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.NativeJavaPackage;
import jdk.nashorn.internal.runtime.PropertyDescriptor;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptEnvironment;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
import jdk.nashorn.internal.runtime.regexp.RegExpResult;
import jdk.nashorn.internal.scripts.JO;

/**
 * Representation of global scope.
 */
@ScriptClass("Global")
public final class Global extends ScriptObject implements GlobalObject, Scope {
    private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class);
    private final InvokeByName VALUE_OF  = new InvokeByName("valueOf",  ScriptObject.class);

    /** ECMA 15.1.2.2 parseInt (string , radix) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object parseInt;

    /** ECMA 15.1.2.3 parseFloat (string) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object parseFloat;

    /** ECMA 15.1.2.4 isNaN (number) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object isNaN;

    /** ECMA 15.1.2.5 isFinite (number) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object isFinite;

    /** ECMA 15.1.3.3 encodeURI */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object encodeURI;

    /** ECMA 15.1.3.4 encodeURIComponent */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object encodeURIComponent;

    /** ECMA 15.1.3.1 decodeURI */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object decodeURI;

    /** ECMA 15.1.3.2 decodeURIComponent */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object decodeURIComponent;

    /** ECMA B.2.1 escape (string) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object escape;

    /** ECMA B.2.2 unescape (string) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object unescape;

    /** Nashorn extension: global.print */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object print;

    /** Nashorn extension: global.load */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object load;

    /** Nashorn extension: global.loadWithNewGlobal */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object loadWithNewGlobal;

    /** Nashorn extension: global.exit */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object exit;

    /** Nashorn extension: global.quit */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object quit;

    /** Value property NaN of the Global Object - ECMA 15.1.1.1 NaN */
    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
    public final Object NaN = Double.NaN;

    /** Value property Infinity of the Global Object - ECMA 15.1.1.2 Infinity */
    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
    public final Object Infinity = Double.POSITIVE_INFINITY;

    /** Value property Undefined of the Global Object - ECMA 15.1.1.3 Undefined */
    @Property(attributes = Attribute.NON_ENUMERABLE_CONSTANT)
    public final Object undefined = UNDEFINED;

    /** ECMA 15.1.2.1 eval(x) */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public Object eval;

    /** ECMA 15.1.4.1 Object constructor. */
    @Property(name = "Object", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object object;

    /** ECMA 15.1.4.2 Function constructor. */
    @Property(name = "Function", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object function;

    /** ECMA 15.1.4.3 Array constructor. */
    @Property(name = "Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object array;

    /** ECMA 15.1.4.4 String constructor */
    @Property(name = "String", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object string;

    /** ECMA 15.1.4.5 Boolean constructor */
    @Property(name = "Boolean", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object _boolean;

    /** ECMA 15.1.4.6 - Number constructor */
    @Property(name = "Number", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object number;

    /** ECMA 15.1.4.7 Date constructor */
    @Property(name = "Date", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object date;

    /** ECMA 15.1.4.8 RegExp constructor */
    @Property(name = "RegExp", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object regexp;

    /** ECMA 15.12 - The JSON object */
    @Property(name = "JSON", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object json;

    /** Nashorn extension: global.JSAdapter */
    @Property(name = "JSAdapter", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object jsadapter;

    /** ECMA 15.8 - The Math object */
    @Property(name = "Math", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object math;

    /** Error object */
    @Property(name = "Error", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object error;

    /** EvalError object */
    @Property(name = "EvalError", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object evalError;

    /** RangeError object */
    @Property(name = "RangeError", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object rangeError;

    /** ReferenceError object */
    @Property(name = "ReferenceError", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object referenceError;

    /** SyntaxError object */
    @Property(name = "SyntaxError", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object syntaxError;

    /** TypeError object */
    @Property(name = "TypeError", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object typeError;

    /** URIError object */
    @Property(name = "URIError", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object uriError;

    /** ArrayBuffer object */
    @Property(name = "ArrayBuffer", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object arrayBuffer;

    /** TypedArray (int8) */
    @Property(name = "Int8Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object int8Array;

    /** TypedArray (uint8) */
    @Property(name = "Uint8Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object uint8Array;

    /** TypedArray (uint8) - Clamped */
    @Property(name = "Uint8ClampedArray", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object uint8ClampedArray;

    /** TypedArray (int16) */
    @Property(name = "Int16Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object int16Array;

    /** TypedArray (uint16) */
    @Property(name = "Uint16Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object uint16Array;

    /** TypedArray (int32) */
    @Property(name = "Int32Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object int32Array;

    /** TypedArray (uint32) */
    @Property(name = "Uint32Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object uint32Array;

    /** TypedArray (float32) */
    @Property(name = "Float32Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object float32Array;

    /** TypedArray (float64) */
    @Property(name = "Float64Array", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object float64Array;

    /** Nashorn extension: Java access - global.Packages */
    @Property(name = "Packages", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object packages;

    /** Nashorn extension: Java access - global.com */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object com;

    /** Nashorn extension: Java access - global.edu */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object edu;

    /** Nashorn extension: Java access - global.java */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object java;

    /** Nashorn extension: Java access - global.javafx */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object javafx;

    /** Nashorn extension: Java access - global.javax */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object javax;

    /** Nashorn extension: Java access - global.org */
    @Property(attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object org;

    /** Nashorn extension: Java access - global.javaImporter */
    @Property(name = "JavaImporter", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object javaImporter;

    /** Nashorn extension: global.Java Object constructor. */
    @Property(name = "Java", attributes = Attribute.NOT_ENUMERABLE)
    public volatile Object javaApi;

    /** Nashorn extension: current script's file name */
    @Property(name = "__FILE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
    public Object __FILE__;

    /** Nashorn extension: current script's directory */
    @Property(name = "__DIR__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
    public Object __DIR__;

    /** Nashorn extension: current source line number being executed */
    @Property(name = "__LINE__", attributes = Attribute.NON_ENUMERABLE_CONSTANT)
    public Object __LINE__;

    /** Used as Date.prototype's default value */
    public NativeDate   DEFAULT_DATE;

    /** Used as RegExp.prototype's default value */
    public NativeRegExp DEFAULT_REGEXP;

    /*
     * Built-in constructor objects: Even if user changes dynamic values of
     * "Object", "Array" etc., we still want to keep original values of these
     * constructors here. For example, we need to be able to create array,
     * regexp literals even after user overwrites global "Array" or "RegExp"
     * constructor - see also ECMA 262 spec. Annex D.
     */
    private ScriptFunction builtinFunction;
    private ScriptFunction builtinObject;
    private ScriptFunction builtinArray;
    private ScriptFunction builtinBoolean;
    private ScriptFunction builtinDate;
    private ScriptObject   builtinJSON;
    private ScriptFunction builtinJSAdapter;
    private ScriptObject   builtinMath;
    private ScriptFunction builtinNumber;
    private ScriptFunction builtinRegExp;
    private ScriptFunction builtinString;
    private ScriptFunction builtinError;
    private ScriptFunction builtinEval;
    private ScriptFunction builtinEvalError;
    private ScriptFunction builtinRangeError;
    private ScriptFunction builtinReferenceError;
    private ScriptFunction builtinSyntaxError;
    private ScriptFunction builtinTypeError;
    private ScriptFunction builtinURIError;
    private ScriptObject   builtinPackages;
    private ScriptObject   builtinCom;
    private ScriptObject   builtinEdu;
    private ScriptObject   builtinJava;
    private ScriptObject   builtinJavafx;
    private ScriptObject   builtinJavax;
    private ScriptObject   builtinOrg;
    private ScriptObject   builtinJavaImporter;
    private ScriptObject   builtinJavaApi;
    private ScriptObject   builtinArrayBuffer;
    private ScriptObject   builtinInt8Array;
    private ScriptObject   builtinUint8Array;
    private ScriptObject   builtinUint8ClampedArray;
    private ScriptObject   builtinInt16Array;
    private ScriptObject   builtinUint16Array;
    private ScriptObject   builtinInt32Array;
    private ScriptObject   builtinUint32Array;
    private ScriptObject   builtinFloat32Array;
    private ScriptObject   builtinFloat64Array;

    /*
     * ECMA section 13.2.3 The [[ThrowTypeError]] Function Object
     */
    private ScriptFunction typeErrorThrower;

    private PropertyMap    accessorPropertyDescriptorMap;
    private PropertyMap    arrayBufferViewMap;
    private PropertyMap    dataPropertyDescriptorMap;
    private PropertyMap    genericPropertyDescriptorMap;
    private PropertyMap    nativeArgumentsMap;
    private PropertyMap    nativeArrayMap;
    private PropertyMap    nativeArrayBufferMap;
    private PropertyMap    nativeBooleanMap;
    private PropertyMap    nativeDateMap;
    private PropertyMap    nativeErrorMap;
    private PropertyMap    nativeEvalErrorMap;
    private PropertyMap    nativeJSAdapterMap;
    private PropertyMap    nativeJavaImporterMap;
    private PropertyMap    nativeNumberMap;
    private PropertyMap    nativeRangeErrorMap;
    private PropertyMap    nativeReferenceErrorMap;
    private PropertyMap    nativeRegExpMap;
    private PropertyMap    nativeRegExpExecResultMap;
    private PropertyMap    nativeStrictArgumentsMap;
    private PropertyMap    nativeStringMap;
    private PropertyMap    nativeSyntaxErrorMap;
    private PropertyMap    nativeTypeErrorMap;
    private PropertyMap    nativeURIErrorMap;
    private PropertyMap    prototypeObjectMap;
    private PropertyMap    objectMap;
    private PropertyMap    functionMap;
    private PropertyMap    anonymousFunctionMap;
    private PropertyMap    strictFunctionMap;
    private PropertyMap    boundFunctionMap;

    // Flag to indicate that a split method issued a return statement
    private int splitState = -1;

    // class cache
    private ClassCache classCache;

    // Used to store the last RegExp result to support deprecated RegExp constructor properties
    private RegExpResult lastRegExpResult;

    private static final MethodHandle EVAL              = findOwnMH("eval",              Object.class, Object.class, Object.class);
    private static final MethodHandle PRINT             = findOwnMH("print",             Object.class, Object.class, Object[].class);
    private static final MethodHandle PRINTLN           = findOwnMH("println",           Object.class, Object.class, Object[].class);
    private static final MethodHandle LOAD              = findOwnMH("load",              Object.class, Object.class, Object.class);
    private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH("loadWithNewGlobal", Object.class, Object.class, Object[].class);
    private static final MethodHandle EXIT              = findOwnMH("exit",              Object.class, Object.class, Object.class);

    // initialized by nasgen
    private static PropertyMap $nasgenmap$;

    // context to which this global belongs to
    private final Context context;

    @Override
    protected Context getContext() {
        return context;
    }

    // performs initialization checks for Global constructor and returns the
    // PropertyMap, if everything is fine.
    private static PropertyMap checkAndGetMap(final Context context) {
        // security check first
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new RuntimePermission(Context.NASHORN_CREATE_GLOBAL));
        }

        // null check on context
        context.getClass();

        /*
         * Duplicate global's map and use it. This way the initial Map filled
         * by nasgen (referenced from static field in this class) is retained
         * 'as is' (as that one is process wide singleton.
         */
        return $nasgenmap$.duplicate();
    }

    /**
     * Constructor
     *
     * @param context the context
     */
    public Global(final Context context) {
        super(checkAndGetMap(context));
        this.context = context;
        this.setIsScope();

        final int cacheSize = context.getEnv()._class_cache_size;
        if (cacheSize > 0) {
            classCache = new ClassCache(cacheSize);
        }
    }

    /**
     * Script access to "current" Global instance
     *
     * @return the global singleton
     */
    public static Global instance() {
        ScriptObject global = Context.getGlobal();
        if (! (global instanceof Global)) {
            throw new IllegalStateException("no current global instance");
        }
        return (Global)global;
    }

    /**
     * Script access to {@link ScriptEnvironment}
     *
     * @return the script environment
     */
    static ScriptEnvironment getEnv() {
        return instance().getContext().getEnv();
    }

    /**
     * Script access to {@link Context}
     *
     * @return the context
     */
    static Context getThisContext() {
        return instance().getContext();
    }

    // GlobalObject interface implementation

    @Override
    public boolean isOfContext(final Context ctxt) {
        return this.context == ctxt;
    }

    @Override
    public boolean isStrictContext() {
        return context.getEnv()._strict;
    }

    @Override
    public void initBuiltinObjects() {
        if (this.builtinObject != null) {
            // already initialized, just return
            return;
        }

        init();
    }

    @Override
    public ScriptFunction newScriptFunction(final String name, final MethodHandle handle, final ScriptObject scope, final boolean strict) {
        return new ScriptFunctionImpl(name, handle, scope, null, strict, false, true);
    }

    @Override
    public Object wrapAsObject(final Object obj) {
        if (obj instanceof Boolean) {
            return new NativeBoolean((Boolean)obj, this);
        } else if (obj instanceof Number) {
            return new NativeNumber(((Number)obj).doubleValue(), this);
        } else if (obj instanceof String || obj instanceof ConsString) {
            return new NativeString((CharSequence)obj, this);
        } else if (obj instanceof Object[]) { // extension
            return new NativeArray((Object[])obj);
        } else if (obj instanceof double[]) { // extension
            return new NativeArray((double[])obj);
        } else if (obj instanceof long[]) {
            return new NativeArray((long[])obj);
        } else if (obj instanceof int[]) {
            return new NativeArray((int[])obj);
        } else {
            // FIXME: more special cases? Map? List?
            return obj;
        }
    }

    @Override
    public GuardedInvocation primitiveLookup(final LinkRequest request, final Object self) {
        if (self instanceof String || self instanceof ConsString) {
            return NativeString.lookupPrimitive(request, self);
        } else if (self instanceof Number) {
            return NativeNumber.lookupPrimitive(request, self);
        } else if (self instanceof Boolean) {
            return NativeBoolean.lookupPrimitive(request, self);
        }
        throw new IllegalArgumentException("Unsupported primitive: " + self);
    }

    @Override
    public ScriptObject newObject() {
        return new JO(getObjectPrototype(), getObjectMap());
    }

    @Override
    public Object getDefaultValue(final ScriptObject sobj, final Class<?> typeHint) {
        // When the [[DefaultValue]] internal method of O is called with no hint,
        // then it behaves as if the hint were Number, unless O is a Date object
        // in which case it behaves as if the hint were String.
        Class<?> hint = typeHint;
        if (hint == null) {
            hint = Number.class;
        }

        try {
            if (hint == String.class) {

                final Object toString = TO_STRING.getGetter().invokeExact(sobj);

                if (Bootstrap.isCallable(toString)) {
                    final Object value = TO_STRING.getInvoker().invokeExact(toString, sobj);
                    if (JSType.isPrimitive(value)) {
                        return value;
                    }
                }

                final Object valueOf = VALUE_OF.getGetter().invokeExact(sobj);
                if (Bootstrap.isCallable(valueOf)) {
                    final Object value = VALUE_OF.getInvoker().invokeExact(valueOf, sobj);
                    if (JSType.isPrimitive(value)) {
                        return value;
                    }
                }
                throw typeError(this, "cannot.get.default.string");
            }

            if (hint == Number.class) {
                final Object valueOf = VALUE_OF.getGetter().invokeExact(sobj);
                if (Bootstrap.isCallable(valueOf)) {
                    final Object value = VALUE_OF.getInvoker().invokeExact(valueOf, sobj);
                    if (JSType.isPrimitive(value)) {
                        return value;
                    }
                }

                final Object toString = TO_STRING.getGetter().invokeExact(sobj);
                if (Bootstrap.isCallable(toString)) {
                    final Object value = TO_STRING.getInvoker().invokeExact(toString, sobj);
                    if (JSType.isPrimitive(value)) {
                        return value;
                    }
                }

                throw typeError(this, "cannot.get.default.number");
            }
        } catch (final RuntimeException | Error e) {
            throw e;
        } catch (final Throwable t) {
            throw new RuntimeException(t);
        }

        return UNDEFINED;
    }

    @Override
    public boolean isError(final ScriptObject sobj) {
        final ScriptObject errorProto = getErrorPrototype();
        ScriptObject proto = sobj.getProto();
        while (proto != null) {
            if (proto == errorProto) {
                return true;
            }
            proto = proto.getProto();
        }
        return false;
    }

    @Override
    public ScriptObject newError(final String msg) {
        return new NativeError(msg, this);
    }

    @Override
    public ScriptObject newEvalError(final String msg) {
        return new NativeEvalError(msg, this);
    }

    @Override
    public ScriptObject newRangeError(final String msg) {
        return new NativeRangeError(msg, this);
    }

    @Override
    public ScriptObject newReferenceError(final String msg) {
        return new NativeReferenceError(msg, this);
    }

    @Override
    public ScriptObject newSyntaxError(final String msg) {
        return new NativeSyntaxError(msg, this);
    }

    @Override
    public ScriptObject newTypeError(final String msg) {
        return new NativeTypeError(msg, this);
    }

    @Override
    public ScriptObject newURIError(final String msg) {
        return new NativeURIError(msg, this);
    }

    @Override
    public PropertyDescriptor newGenericDescriptor(final boolean configurable, final boolean enumerable) {
        return new GenericPropertyDescriptor(configurable, enumerable, this);
    }

    @Override
    public PropertyDescriptor newDataDescriptor(final Object value, final boolean configurable, final boolean enumerable, final boolean writable) {
        return new DataPropertyDescriptor(configurable, enumerable, writable, value, this);
    }

    @Override
    public PropertyDescriptor newAccessorDescriptor(final Object get, final Object set, final boolean configurable, final boolean enumerable) {
        final AccessorPropertyDescriptor desc = new AccessorPropertyDescriptor(configurable, enumerable, get == null ? UNDEFINED : get, set == null ? UNDEFINED : set, this);

        if (get == null) {
            desc.delete(PropertyDescriptor.GET, false);
        }

        if (set == null) {
            desc.delete(PropertyDescriptor.SET, false);
        }

        return desc;
    }


    /**
     * Cache for compiled script classes.
     */
    @SuppressWarnings("serial")
    private static class ClassCache extends LinkedHashMap<Source, ClassReference> {
        private final int size;
        private final ReferenceQueue<Class queue;

        ClassCache(int size) {
            super(size, 0.75f, true);
            this.size = size;
            this.queue = new ReferenceQueue<>();
        }

        void cache(final Source source, final Class<?> clazz) {
            put(source, new ClassReference(clazz, queue, source));
        }

        @Override
        protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
            return size() > size;
        }

        @Override
        public ClassReference get(Object key) {
            for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
                remove(ref.source);
            }
            return super.get(key);
        }

    }

    private static class ClassReference extends SoftReference<Class {
        private final Source source;

        ClassReference(final Class<?> clazz, final ReferenceQueue> queue, final Source source) {
            super(clazz, queue);
            this.source = source;
        }
    }

    // Class cache management
    @Override
    public Class<?> findCachedClass(final Source source) {
        assert classCache != null : "Class cache used without being initialized";
        ClassReference ref = classCache.get(source);
        return ref != null ? ref.get() : null;
    }

    @Override
    public void cacheClass(final Source source, final Class<?> clazz) {
        assert classCache != null : "Class cache used without being initialized";
        classCache.cache(source, clazz);
    }

    private static <T> T getLazilyCreatedValue(final Object key, final Callable creator, final Map map) {
        final T obj = map.get(key);
        if (obj != null) {
            return obj;
        }

        try {
            final T newObj = creator.call();
            final T existingObj = map.putIfAbsent(key, newObj);
            return existingObj != null ? existingObj : newObj;
        } catch (final Exception exp) {
            throw new RuntimeException(exp);
        }
    }

    private final Map<Object, InvokeByName> namedInvokers = new ConcurrentHashMap<>();

    @Override
    public InvokeByName getInvokeByName(final Object key, final Callable<InvokeByName> creator) {
        return getLazilyCreatedValue(key, creator, namedInvokers);
    }

    private final Map<Object, MethodHandle> dynamicInvokers = new ConcurrentHashMap<>();

    @Override
    public MethodHandle getDynamicInvoker(final Object key, final Callable<MethodHandle> creator) {
        return getLazilyCreatedValue(key, creator, dynamicInvokers);
    }

    /**
     * This is the eval used when 'indirect' eval call is made.
     *
     * var global = this;
     * global.eval("print('hello')");
     *
     * @param self  eval scope
     * @param str   eval string
     *
     * @return the result of eval
     */
    public static Object eval(final Object self, final Object str) {
        return directEval(self, str, UNDEFINED, UNDEFINED, UNDEFINED);
    }

    /**
     * Direct eval
     *
     * @param self     The scope of eval passed as 'self'
     * @param str      Evaluated code
     * @param callThis "this" to be passed to the evaluated code
     * @param location location of the eval call
     * @param strict   is eval called a strict mode code?
     *
     * @return the return value of the eval
     *
     * This is directly invoked from generated when eval(code) is called in user code
     */
    public static Object directEval(final Object self, final Object str, final Object callThis, final Object location, final Object strict) {
        if (!(str instanceof String || str instanceof ConsString)) {
            return str;
        }
        final Global global = Global.instance();
        final ScriptObject scope = (self instanceof ScriptObject) ? (ScriptObject)self : global;

        return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict));
    }

    /**
     * Global print implementation - Nashorn extension
     *
     * @param self    scope
     * @param objects arguments to print
     *
     * @return result of print (undefined)
     */
    public static Object print(final Object self, final Object... objects) {
        return printImpl(false, objects);
    }

    /**
     * Global println implementation - Nashorn extension
     *
     * @param self    scope
     * @param objects arguments to print
     *
     * @return result of println (undefined)
     */
    public static Object println(final Object self, final Object... objects) {
        return printImpl(true, objects);
    }

    /**
     * Global load implementation - Nashorn extension
     *
     * @param self    scope
     * @param source  source to load
     *
     * @return result of load (undefined)
     *
     * @throws IOException if source could not be read
     */
    public static Object load(final Object self, final Object source) throws IOException {
        final Global global = Global.instance();
        final ScriptObject scope = (self instanceof ScriptObject) ? (ScriptObject)self : global;
        return global.getContext().load(scope, source);
    }

    /**
     * Global loadWithNewGlobal implementation - Nashorn extension
     *
     * @param self scope
     * @param args from plus (optional) arguments to be passed to the loaded script
     *
     * @return result of load (may be undefined)
     *
     * @throws IOException if source could not be read
     */
    public static Object loadWithNewGlobal(final Object self, final Object...args) throws IOException {
        final Global global = Global.instance();
        final int length = args.length;
        final boolean hasArgs = 0 < length;
        final Object from = hasArgs ? args[0] : UNDEFINED;
        final Object[] arguments = hasArgs ? Arrays.copyOfRange(args, 1, length) : args;

        return global.getContext().loadWithNewGlobal(from, arguments);
    }

    /**
     * Global exit and quit implementation - Nashorn extension: perform a {@code System.exit} call from the script
     *
     * @param self  self reference
     * @param code  exit code
     *
     * @return undefined (will never be reacheD)
     */
    public static Object exit(final Object self, final Object code) {
        System.exit(JSType.toInt32(code));
        return UNDEFINED;
    }

    // builtin prototype accessors
    ScriptObject getFunctionPrototype() {
        return ScriptFunction.getPrototype(builtinFunction);
    }

    ScriptObject getObjectPrototype() {
        return ScriptFunction.getPrototype(builtinObject);
    }

    ScriptObject getArrayPrototype() {
        return ScriptFunction.getPrototype(builtinArray);
    }

    ScriptObject getBooleanPrototype() {
        return ScriptFunction.getPrototype(builtinBoolean);
    }

    ScriptObject getNumberPrototype() {
        return ScriptFunction.getPrototype(builtinNumber);
    }

    ScriptObject getDatePrototype() {
        return ScriptFunction.getPrototype(builtinDate);
    }

    ScriptObject getRegExpPrototype() {
        return ScriptFunction.getPrototype(builtinRegExp);
    }

    ScriptObject getStringPrototype() {
        return ScriptFunction.getPrototype(builtinString);
    }

    ScriptObject getErrorPrototype() {
        return ScriptFunction.getPrototype(builtinError);
    }

    ScriptObject getEvalErrorPrototype() {
        return ScriptFunction.getPrototype(builtinEvalError);
    }

    ScriptObject getRangeErrorPrototype() {
        return ScriptFunction.getPrototype(builtinRangeError);
    }

    ScriptObject getReferenceErrorPrototype() {
        return ScriptFunction.getPrototype(builtinReferenceError);
    }

    ScriptObject getSyntaxErrorPrototype() {
        return ScriptFunction.getPrototype(builtinSyntaxError);
    }

    ScriptObject getTypeErrorPrototype() {
        return ScriptFunction.getPrototype(builtinTypeError);
    }

    ScriptObject getURIErrorPrototype() {
        return ScriptFunction.getPrototype(builtinURIError);
    }

    ScriptObject getJavaImporterPrototype() {
        return ScriptFunction.getPrototype(builtinJavaImporter);
    }

    ScriptObject getJSAdapterPrototype() {
        return ScriptFunction.getPrototype(builtinJSAdapter);
    }

    ScriptObject getArrayBufferPrototype() {
        return ScriptFunction.getPrototype(builtinArrayBuffer);
    }

    ScriptObject getInt8ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinInt8Array);
    }

    ScriptObject getUint8ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinUint8Array);
    }

    ScriptObject getUint8ClampedArrayPrototype() {
        return ScriptFunction.getPrototype(builtinUint8ClampedArray);
    }

    ScriptObject getInt16ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinInt16Array);
    }

    ScriptObject getUint16ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinUint16Array);
    }

    ScriptObject getInt32ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinInt32Array);
    }

    ScriptObject getUint32ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinUint32Array);
    }

    ScriptObject getFloat32ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinFloat32Array);
    }

    ScriptObject getFloat64ArrayPrototype() {
        return ScriptFunction.getPrototype(builtinFloat64Array);
    }

    // Builtin PropertyMap accessors
    PropertyMap getAccessorPropertyDescriptorMap() {
        return accessorPropertyDescriptorMap;
    }

    PropertyMap getArrayBufferViewMap() {
        return arrayBufferViewMap;
    }

    PropertyMap getDataPropertyDescriptorMap() {
        return dataPropertyDescriptorMap;
    }

    PropertyMap getGenericPropertyDescriptorMap() {
        return genericPropertyDescriptorMap;
    }

    PropertyMap getArgumentsMap() {
        return nativeArgumentsMap;
    }

    PropertyMap getArrayMap() {
        return nativeArrayMap;
    }

    PropertyMap getArrayBufferMap() {
        return nativeArrayBufferMap;
    }

    PropertyMap getBooleanMap() {
        return nativeBooleanMap;
    }

    PropertyMap getDateMap() {
        return nativeDateMap;
    }

    PropertyMap getErrorMap() {
        return nativeErrorMap;
    }

    PropertyMap getEvalErrorMap() {
        return nativeEvalErrorMap;
    }

    PropertyMap getJSAdapterMap() {
        return nativeJSAdapterMap;
    }

    PropertyMap getJavaImporterMap() {
        return nativeJavaImporterMap;
    }

    PropertyMap getNumberMap() {
        return nativeNumberMap;
    }

    PropertyMap getRangeErrorMap() {
        return nativeRangeErrorMap;
    }

    PropertyMap getReferenceErrorMap() {
        return nativeReferenceErrorMap;
    }

    PropertyMap getRegExpMap() {
        return nativeRegExpMap;
    }

    PropertyMap getRegExpExecResultMap() {
        return nativeRegExpExecResultMap;
    }

    PropertyMap getStrictArgumentsMap() {
        return nativeStrictArgumentsMap;
    }

    PropertyMap getStringMap() {
        return nativeStringMap;
    }

    PropertyMap getSyntaxErrorMap() {
        return nativeSyntaxErrorMap;
    }

    PropertyMap getTypeErrorMap() {
        return nativeTypeErrorMap;
    }

    PropertyMap getURIErrorMap() {
        return nativeURIErrorMap;
    }

    PropertyMap getPrototypeObjectMap() {
        return prototypeObjectMap;
    }

    PropertyMap getObjectMap() {
        return objectMap;
    }

    PropertyMap getFunctionMap() {
        return functionMap;
    }

    PropertyMap getAnonymousFunctionMap() {
        return anonymousFunctionMap;
    }

    PropertyMap getStrictFunctionMap() {
        return strictFunctionMap;
    }

    PropertyMap getBoundFunctionMap() {
        return boundFunctionMap;
    }

    private ScriptFunction getBuiltinArray() {
        return builtinArray;
    }

    ScriptFunction getTypeErrorThrower() {
        return typeErrorThrower;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin array has not been overridden
     */
    public static boolean isBuiltinArray() {
        final Global instance = Global.instance();
        return instance.array == instance.getBuiltinArray();
    }

    private ScriptFunction getBuiltinBoolean() {
        return builtinBoolean;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin boolean has not been overridden
     */
    public static boolean isBuiltinBoolean() {
        final Global instance = Global.instance();
        return instance._boolean == instance.getBuiltinBoolean();
    }

    private ScriptFunction getBuiltinDate() {
        return builtinDate;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin date has not been overridden
     */
    public static boolean isBuiltinDate() {
        final Global instance = Global.instance();
        return instance.date == instance.getBuiltinDate();
    }

    private ScriptFunction getBuiltinError() {
        return builtinError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin error has not been overridden
     */
    public static boolean isBuiltinError() {
        final Global instance = Global.instance();
        return instance.error == instance.getBuiltinError();
    }

    private ScriptFunction getBuiltinEvalError() {
        return builtinEvalError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin eval error has not been overridden
     */
    public static boolean isBuiltinEvalError() {
        final Global instance = Global.instance();
        return instance.evalError == instance.getBuiltinEvalError();
    }

    private ScriptFunction getBuiltinFunction() {
        return builtinFunction;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin function has not been overridden
     */
    public static boolean isBuiltinFunction() {
        final Global instance = Global.instance();
        return instance.function == instance.getBuiltinFunction();
    }

    private ScriptFunction getBuiltinJSAdapter() {
        return builtinJSAdapter;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin JSAdapter has not been overridden
     */
    public static boolean isBuiltinJSAdapter() {
        final Global instance = Global.instance();
        return instance.jsadapter == instance.getBuiltinJSAdapter();
    }

    private ScriptObject getBuiltinJSON() {
        return builtinJSON;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin JSON has has not been overridden
     */
    public static boolean isBuiltinJSON() {
        final Global instance = Global.instance();
        return instance.json == instance.getBuiltinJSON();
    }

    private ScriptObject getBuiltinJava() {
        return builtinJava;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin Java has not been overridden
     */
    public static boolean isBuiltinJava() {
        final Global instance = Global.instance();
        return instance.java == instance.getBuiltinJava();
    }

    private ScriptObject getBuiltinJavax() {
        return builtinJavax;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin Javax has not been overridden
     */
    public static boolean isBuiltinJavax() {
        final Global instance = Global.instance();
        return instance.javax == instance.getBuiltinJavax();
    }

    private ScriptObject getBuiltinJavaImporter() {
        return builtinJavaImporter;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin Java importer has not been overridden
     */
    public static boolean isBuiltinJavaImporter() {
        final Global instance = Global.instance();
        return instance.javaImporter == instance.getBuiltinJavaImporter();
    }

    private ScriptObject getBuiltinMath() {
        return builtinMath;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin math has not been overridden
     */
    public static boolean isBuiltinMath() {
        final Global instance = Global.instance();
        return instance.math == instance.getBuiltinMath();
    }

    private ScriptFunction getBuiltinNumber() {
        return builtinNumber;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin number has not been overridden
     */
    public static boolean isBuiltinNumber() {
        final Global instance = Global.instance();
        return instance.number == instance.getBuiltinNumber();
    }

    private ScriptFunction getBuiltinObject() {
        return builtinObject;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin object has not been overridden
     */
    public static boolean isBuiltinObject() {
        final Global instance = Global.instance();
        return instance.object == instance.getBuiltinObject();
    }

    private ScriptObject getBuiltinPackages() {
        return builtinPackages;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin package has not been overridden
     */
    public static boolean isBuiltinPackages() {
        final Global instance = Global.instance();
        return instance.packages == instance.getBuiltinPackages();
    }

    private ScriptFunction getBuiltinRangeError() {
        return builtinRangeError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin range error has not been overridden
     */
    public static boolean isBuiltinRangeError() {
        final Global instance = Global.instance();
        return instance.rangeError == instance.getBuiltinRangeError();
    }

    private ScriptFunction getBuiltinReferenceError() {
        return builtinReferenceError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin reference error has not been overridden
     */
    public static boolean isBuiltinReferenceError() {
        final Global instance = Global.instance();
        return instance.referenceError == instance.getBuiltinReferenceError();
    }

    private ScriptFunction getBuiltinRegExp() {
        return builtinRegExp;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin regexp has not been overridden
     */
    public static boolean isBuiltinRegExp() {
        final Global instance = Global.instance();
        return instance.regexp == instance.getBuiltinRegExp();
    }

    private ScriptFunction getBuiltinString() {
        return builtinString;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin Java has not been overridden
     */
    public static boolean isBuiltinString() {
        final Global instance = Global.instance();
        return instance.string == instance.getBuiltinString();
    }

    private ScriptFunction getBuiltinSyntaxError() {
        return builtinSyntaxError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin syntax error has not been overridden
     */
    public static boolean isBuiltinSyntaxError() {
        final Global instance = Global.instance();
        return instance.syntaxError == instance.getBuiltinSyntaxError();
    }

    private ScriptFunction getBuiltinTypeError() {
        return builtinTypeError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin type error has not been overridden
     */
    public static boolean isBuiltinTypeError() {
        final Global instance = Global.instance();
        return instance.typeError == instance.getBuiltinTypeError();
    }

    private ScriptFunction getBuiltinURIError() {
        return builtinURIError;
    }

    /**
     * Called from compiled script code to test if builtin has been overridden
     *
     * @return true if builtin URI error has not been overridden
     */
    public static boolean isBuiltinURIError() {
        final Global instance = Global.instance();
        return instance.uriError == instance.getBuiltinURIError();
    }

    @Override
    public String getClassName() {
        return "global";
    }

    /**
     * Copy function used to clone NativeRegExp objects.
     *
     * @param regexp a NativeRegExp to clone
     *
     * @return copy of the given regexp object
     */
    public static Object regExpCopy(final Object regexp) {
        return new NativeRegExp((NativeRegExp)regexp);
    }

    /**
     * Convert given object to NativeRegExp type.
     *
     * @param obj object to be converted
     * @return NativeRegExp instance
     */
    public static NativeRegExp toRegExp(final Object obj) {
        if (obj instanceof NativeRegExp) {
            return (NativeRegExp)obj;
        }
        return new NativeRegExp(JSType.toString(obj));
    }

    /**
     * ECMA 9.9 ToObject implementation
     *
     * @param obj  an item for which to run ToObject
     * @return ToObject version of given item
     */
    public static Object toObject(final Object obj) {
        if (obj == null || obj == UNDEFINED) {
            throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
        }

        if (obj instanceof ScriptObject) {
            return obj;
        }

        return instance().wrapAsObject(obj);
    }

    /**
     * Allocate a new object array.
     *
     * @param initial object values.
     * @return the new array
     */
    public static NativeArray allocate(final Object[] initial) {
        ArrayData arrayData = ArrayData.allocate(initial);

        for (int index = 0; index < initial.length; index++) {
            final Object value = initial[index];

            if (value == ScriptRuntime.EMPTY) {
                arrayData = arrayData.delete(index);
            }
        }

        return new NativeArray(arrayData);
    }

    /**
     * Allocate a new number array.
     *
     * @param initial number values.
     * @return the new array
     */
    public static NativeArray allocate(final double[] initial) {
        return new NativeArray(ArrayData.allocate(initial));
    }

    /**
     * Allocate a new long array.
     *
     * @param initial number values.
     * @return the new array
     */
    public static NativeArray allocate(final long[] initial) {
        return new NativeArray(ArrayData.allocate(initial));
    }

    /**
     * Allocate a new integer array.
     *
     * @param initial number values.
     * @return the new array
     */
    public static NativeArray allocate(final int[] initial) {
        return new NativeArray(ArrayData.allocate(initial));
    }

    /**
     * Allocate a new object array for arguments.
     *
     * @param arguments initial arguments passed.
     * @param callee reference to the function that uses arguments object
     * @param numParams actual number of declared parameters
     *
     * @return the new array
     */
    public static ScriptObject allocateArguments(final Object[] arguments, final Object callee, final int numParams) {
        return NativeArguments.allocate(arguments, (ScriptFunction)callee, numParams);
    }

    /**
     * Called from generated to check if given function is the builtin 'eval'. If
     * eval is used in a script, a lot of optimizations and assumptions cannot be done.
     *
     * @param  fn function object that is checked
     * @return true if fn is the builtin eval
     */
    public static boolean isEval(final Object fn) {
        return fn == Global.instance().builtinEval;
    }

    /**
     * Create a new RegExp object.
     *
     * @param expression Regular expression.
     * @param options    Search options.
     *
     * @return New RegExp object.
     */
    public static Object newRegExp(final String expression, final String options) {
        if (options == null) {
            return new NativeRegExp(expression);
        }
        return new NativeRegExp(expression, options);
    }

    /**
     * Get the object prototype
     *
     * @return the object prototype
     */
    public static ScriptObject objectPrototype() {
        return Global.instance().getObjectPrototype();
    }

    /**
     * Create a new empty object instance.
     *
     * @return New empty object.
     */
    public static ScriptObject newEmptyInstance() {
        return Global.instance().newObject();
    }

    /**
     * Check if a given object is a ScriptObject, raises an exception if this is
     * not the case
     *
     * @param obj and object to check
     */
    public static void checkObject(final Object obj) {
        if (!(obj instanceof ScriptObject)) {
            throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
        }
    }

    /**
     * ECMA 9.10 - implementation of CheckObjectCoercible, i.e. raise an exception
     * if this object is null or undefined.
     *
     * @param obj an object to check
     */
    public static void checkObjectCoercible(final Object obj) {
        if (obj == null || obj == UNDEFINED) {
            throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
        }
    }

    /**
     * Get the current split state.
     *
     * @return current split state
     */
    @Override
    public int getSplitState() {
        return splitState;
    }

    /**
     * Set the current split state.
     *
     * @param state current split state
     */
    @Override
    public void setSplitState(final int state) {
        splitState = state;
    }

    private void init() {
        assert Context.getGlobal() == this : "this global is not set as current";

        final ScriptEnvironment env = getContext().getEnv();

        // duplicate PropertyMaps of Native* classes
        copyInitialMaps(env);

        // initialize Function and Object constructor
        initFunctionAndObject();

        // Now fix Global's own proto.
        this.setProto(getObjectPrototype());

        // initialize global function properties
        this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);

        this.parseInt           = ScriptFunctionImpl.makeFunction("parseInt",   GlobalFunctions.PARSEINT);
        this.parseFloat         = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
        this.isNaN              = ScriptFunctionImpl.makeFunction("isNaN",      GlobalFunctions.IS_NAN);
        this.isFinite           = ScriptFunctionImpl.makeFunction("isFinite",   GlobalFunctions.IS_FINITE);
        this.encodeURI          = ScriptFunctionImpl.makeFunction("encodeURI",  GlobalFunctions.ENCODE_URI);
        this.encodeURIComponent = ScriptFunctionImpl.makeFunction("encodeURIComponent", GlobalFunctions.ENCODE_URICOMPONENT);
        this.decodeURI          = ScriptFunctionImpl.makeFunction("decodeURI",  GlobalFunctions.DECODE_URI);
        this.decodeURIComponent = ScriptFunctionImpl.makeFunction("decodeURIComponent", GlobalFunctions.DECODE_URICOMPONENT);
        this.escape             = ScriptFunctionImpl.makeFunction("escape",     GlobalFunctions.ESCAPE);
        this.unescape           = ScriptFunctionImpl.makeFunction("unescape",   GlobalFunctions.UNESCAPE);
        this.print              = ScriptFunctionImpl.makeFunction("print",      env._print_no_newline ? PRINT : PRINTLN);
        this.load               = ScriptFunctionImpl.makeFunction("load",       LOAD);
        this.loadWithNewGlobal  = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOADWITHNEWGLOBAL);
        this.exit               = ScriptFunctionImpl.makeFunction("exit",       EXIT);
        this.quit               = ScriptFunctionImpl.makeFunction("quit",       EXIT);

        // built-in constructors
        this.builtinArray     = (ScriptFunction)initConstructor("Array");
        this.builtinBoolean   = (ScriptFunction)initConstructor("Boolean");
        this.builtinDate      = (ScriptFunction)initConstructor("Date");
        this.builtinJSON      = initConstructor("JSON");
        this.builtinJSAdapter = (ScriptFunction)initConstructor("JSAdapter");
        this.builtinMath      = initConstructor("Math");
        this.builtinNumber    = (ScriptFunction)initConstructor("Number");
        this.builtinRegExp    = (ScriptFunction)initConstructor("RegExp");
        this.builtinString    = (ScriptFunction)initConstructor("String");

        // initialize String.prototype.length to 0
        // add String.prototype.length
        final ScriptObject stringPrototype = getStringPrototype();
        stringPrototype.addOwnProperty("length", Attribute.NON_ENUMERABLE_CONSTANT, 0.0);

        // set isArray flag on Array.prototype
        final ScriptObject arrayPrototype = getArrayPrototype();
        arrayPrototype.setIsArray();

        this.DEFAULT_DATE = new NativeDate(Double.NaN, this);

        // initialize default regexp object
        this.DEFAULT_REGEXP = new NativeRegExp("(?:)", this);

        // RegExp.prototype should behave like a RegExp object. So copy the
        // properties.
        final ScriptObject regExpProto = getRegExpPrototype();
        regExpProto.addBoundProperties(DEFAULT_REGEXP);

        // Error stuff
        initErrorObjects();

        // java access
        if (! env._no_java) {
            initJavaAccess();
        }

        if (! env._no_typed_arrays) {
            initTypedArray();
        }

        if (env._scripting) {
            initScripting(env);
        }

        if (Context.DEBUG && System.getSecurityManager() == null) {
            initDebug();
        }

        copyBuiltins();

        // initialized with strings so that typeof will work as expected.
        this.__FILE__ = "";
        this.__DIR__  = "";
        this.__LINE__ = 0.0;

        // expose script (command line) arguments as "arguments" property of global
        final List<String> arguments = env.getArguments();
        final Object argsObj = wrapAsObject(arguments.toArray());

        addOwnProperty("arguments", Attribute.NOT_ENUMERABLE, argsObj);
        if (env._scripting) {
            // synonym for "arguments" in scripting mode
            addOwnProperty("$ARG", Attribute.NOT_ENUMERABLE, argsObj);
        }
    }

    private void initErrorObjects() {
        // Error objects
        this.builtinError = (ScriptFunction)initConstructor("Error");
        final ScriptObject errorProto = getErrorPrototype();

        // Nashorn specific accessors on Error.prototype - stack, lineNumber, columnNumber and fileName
        final ScriptFunction getStack = ScriptFunctionImpl.makeFunction("getStack", NativeError.GET_STACK);
        final ScriptFunction setStack = ScriptFunctionImpl.makeFunction("setStack", NativeError.SET_STACK);
        errorProto.addOwnProperty("stack", Attribute.NOT_ENUMERABLE, getStack, setStack);
        final ScriptFunction getLineNumber = ScriptFunctionImpl.makeFunction("getLineNumber", NativeError.GET_LINENUMBER);
        final ScriptFunction setLineNumber = ScriptFunctionImpl.makeFunction("setLineNumber", NativeError.SET_LINENUMBER);
        errorProto.addOwnProperty("lineNumber", Attribute.NOT_ENUMERABLE, getLineNumber, setLineNumber);
        final ScriptFunction getColumnNumber = ScriptFunctionImpl.makeFunction("getColumnNumber", NativeError.GET_COLUMNNUMBER);
        final ScriptFunction setColumnNumber = ScriptFunctionImpl.makeFunction("setColumnNumber", NativeError.SET_COLUMNNUMBER);
        errorProto.addOwnProperty("columnNumber", Attribute.NOT_ENUMERABLE, getColumnNumber, setColumnNumber);
        final ScriptFunction getFileName = ScriptFunctionImpl.makeFunction("getFileName", NativeError.GET_FILENAME);
        final ScriptFunction setFileName = ScriptFunctionImpl.makeFunction("setFileName", NativeError.SET_FILENAME);
        errorProto.addOwnProperty("fileName", Attribute.NOT_ENUMERABLE, getFileName, setFileName);

        // ECMA 15.11.4.2 Error.prototype.name
        // Error.prototype.name = "Error";
        errorProto.set(NativeError.NAME, "Error", false);
        // ECMA 15.11.4.3 Error.prototype.message
        // Error.prototype.message = "";
        errorProto.set(NativeError.MESSAGE, "", false);

        this.builtinEvalError = initErrorSubtype("EvalError", errorProto);
        this.builtinRangeError = initErrorSubtype("RangeError", errorProto);
        this.builtinReferenceError = initErrorSubtype("ReferenceError", errorProto);
        this.builtinSyntaxError = initErrorSubtype("SyntaxError", errorProto);
        this.builtinTypeError = initErrorSubtype("TypeError", errorProto);
        this.builtinURIError = initErrorSubtype("URIError", errorProto);
    }

    private ScriptFunction initErrorSubtype(final String name, final ScriptObject errorProto) {
        final ScriptObject cons = initConstructor(name);
        final ScriptObject prototype = ScriptFunction.getPrototype(cons);
        prototype.set(NativeError.NAME, name, false);
        prototype.set(NativeError.MESSAGE, "", false);
        prototype.setProto(errorProto);
        return (ScriptFunction)cons;
    }

    private void initJavaAccess() {
        final ScriptObject objectProto = getObjectPrototype();
        this.builtinPackages = new NativeJavaPackage("", objectProto);
        this.builtinCom = new NativeJavaPackage("com", objectProto);
        this.builtinEdu = new NativeJavaPackage("edu", objectProto);
        this.builtinJava = new NativeJavaPackage("java", objectProto);
        this.builtinJavafx = new NativeJavaPackage("javafx", objectProto);
        this.builtinJavax = new NativeJavaPackage("javax", objectProto);
        this.builtinOrg = new NativeJavaPackage("org", objectProto);
        this.builtinJavaImporter = initConstructor("JavaImporter");
        this.builtinJavaApi = initConstructor("Java");
    }

    private void initScripting(final ScriptEnvironment scriptEnv) {
        Object value;
        value = ScriptFunctionImpl.makeFunction("readLine", ScriptingFunctions.READLINE);
        addOwnProperty("readLine", Attribute.NOT_ENUMERABLE, value);

        value = ScriptFunctionImpl.makeFunction("readFully", ScriptingFunctions.READFULLY);
        addOwnProperty("readFully", Attribute.NOT_ENUMERABLE, value);

        final String execName = ScriptingFunctions.EXEC_NAME;
        value = ScriptFunctionImpl.makeFunction(execName, ScriptingFunctions.EXEC);
        addOwnProperty(execName, Attribute.NOT_ENUMERABLE, value);

        // Nashorn extension: global.echo (scripting-mode-only)
        // alias for "print"
        value = get("print");
        addOwnProperty("echo", Attribute.NOT_ENUMERABLE, value);

        // Nashorn extension: global.$OPTIONS (scripting-mode-only)
        final ScriptObject options = newObject();
        copyOptions(options, scriptEnv);
        addOwnProperty("$OPTIONS", Attribute.NOT_ENUMERABLE, options);

        // Nashorn extension: global.$ENV (scripting-mode-only)
        if (System.getSecurityManager() == null) {
            // do not fill $ENV if we have a security manager around
            // Retrieve current state of ENV variables.
            final ScriptObject env = newObject();
            env.putAll(System.getenv(), scriptEnv._strict);
            addOwnProperty(ScriptingFunctions.ENV_NAME, Attribute.NOT_ENUMERABLE, env);
        } else {
            addOwnProperty(ScriptingFunctions.ENV_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED);
        }

        // add other special properties for exec support
        addOwnProperty(ScriptingFunctions.OUT_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED);
        addOwnProperty(ScriptingFunctions.ERR_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED);
        addOwnProperty(ScriptingFunctions.EXIT_NAME, Attribute.NOT_ENUMERABLE, UNDEFINED);
    }

    private static void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) {
        for (Field f : scriptEnv.getClass().getFields()) {
            try {
                options.set(f.getName(), f.get(scriptEnv), false);
            } catch (final IllegalArgumentException | IllegalAccessException exp) {
                throw new RuntimeException(exp);
            }
        }
    }

    private void initTypedArray() {
        this.builtinArrayBuffer       = initConstructor("ArrayBuffer");
        this.builtinInt8Array         = initConstructor("Int8Array");
        this.builtinUint8Array        = initConstructor("Uint8Array");
        this.builtinUint8ClampedArray = initConstructor("Uint8ClampedArray");
        this.builtinInt16Array        = initConstructor("Int16Array");
        this.builtinUint16Array       = initConstructor("Uint16Array");
        this.builtinInt32Array        = initConstructor("Int32Array");
        this.builtinUint32Array       = initConstructor("Uint32Array");
        this.builtinFloat32Array      = initConstructor("Float32Array");
        this.builtinFloat64Array      = initConstructor("Float64Array");
    }

    private void copyBuiltins() {
        this.array             = this.builtinArray;
        this._boolean          = this.builtinBoolean;
        this.date              = this.builtinDate;
        this.error             = this.builtinError;
        this.evalError         = this.builtinEvalError;
        this.function          = this.builtinFunction;
        this.jsadapter         = this.builtinJSAdapter;
        this.json              = this.builtinJSON;
        this.com               = this.builtinCom;
        this.edu               = this.builtinEdu;
        this.java              = this.builtinJava;
        this.javafx            = this.builtinJavafx;
        this.javax             = this.builtinJavax;
        this.org               = this.builtinOrg;
        this.javaImporter      = this.builtinJavaImporter;
        this.javaApi           = this.builtinJavaApi;
        this.math              = this.builtinMath;
        this.number            = this.builtinNumber;
        this.object            = this.builtinObject;
        this.packages          = this.builtinPackages;
        this.rangeError        = this.builtinRangeError;
        this.referenceError    = this.builtinReferenceError;
        this.regexp            = this.builtinRegExp;
        this.string            = this.builtinString;
        this.syntaxError       = this.builtinSyntaxError;
        this.typeError         = this.builtinTypeError;
        this.uriError          = this.builtinURIError;
        this.arrayBuffer       = this.builtinArrayBuffer;
        this.int8Array         = this.builtinInt8Array;
        this.uint8Array        = this.builtinUint8Array;
        this.uint8ClampedArray = this.builtinUint8ClampedArray;
        this.int16Array        = this.builtinInt16Array;
        this.uint16Array       = this.builtinUint16Array;
        this.int32Array        = this.builtinInt32Array;
        this.uint32Array       = this.builtinUint32Array;
        this.float32Array      = this.builtinFloat32Array;
        this.float64Array      = this.builtinFloat64Array;
    }

    private void initDebug() {
        this.addOwnProperty("Debug", Attribute.NOT_ENUMERABLE, initConstructor("Debug"));
    }

    @SuppressWarnings("resource")
    private static Object printImpl(final boolean newLine, final Object... objects) {
        final PrintWriter out = Global.getEnv().getOut();
        final StringBuilder sb = new StringBuilder();

        for (final Object object : objects) {
            if (sb.length() != 0) {
                sb.append(' ');
            }

            sb.append(JSType.toString(object));
        }

        // Print all at once to ensure thread friendly result.
        if (newLine) {
            out.println(sb.toString());
        } else {
            out.print(sb.toString());
        }

        out.flush();

        return UNDEFINED;
    }

    /**
     * These classes are generated by nasgen tool and so we have to use
     * reflection to load and create new instance of these classes.
     */
    private ScriptObject initConstructor(final String name) {
        try {
            // Assuming class name pattern for built-in JS constructors.
            final StringBuilder sb = new StringBuilder("jdk.nashorn.internal.objects.");

            sb.append("Native");
            sb.append(name);
            sb.append("$Constructor");

            final Class<?>     funcClass = Class.forName(sb.toString());
            final ScriptObject res       = (ScriptObject)funcClass.newInstance();

            if (res instanceof ScriptFunction) {
                // All global constructor prototypes are not-writable,
                // not-enumerable and not-configurable.
                final ScriptFunction func = (ScriptFunction)res;
                func.modifyOwnProperty(func.getProperty("prototype"), Attribute.NON_ENUMERABLE_CONSTANT);
            }

            if (res.getProto() == null) {
                res.setProto(getObjectPrototype());
            }

            return res;

        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private void copyInitialMaps(final ScriptEnvironment env) {
        this.accessorPropertyDescriptorMap = AccessorPropertyDescriptor.getInitialMap().duplicate();
        this.dataPropertyDescriptorMap = DataPropertyDescriptor.getInitialMap().duplicate();
        this.genericPropertyDescriptorMap = GenericPropertyDescriptor.getInitialMap().duplicate();
        this.nativeArgumentsMap = NativeArguments.getInitialMap().duplicate();
        this.nativeArrayMap = NativeArray.getInitialMap().duplicate();
        this.nativeBooleanMap = NativeBoolean.getInitialMap().duplicate();
        this.nativeDateMap = NativeDate.getInitialMap().duplicate();
        this.nativeErrorMap = NativeError.getInitialMap().duplicate();
        this.nativeEvalErrorMap = NativeEvalError.getInitialMap().duplicate();
        this.nativeJSAdapterMap = NativeJSAdapter.getInitialMap().duplicate();
        this.nativeNumberMap = NativeNumber.getInitialMap().duplicate();
        this.nativeRangeErrorMap = NativeRangeError.getInitialMap().duplicate();
        this.nativeReferenceErrorMap = NativeReferenceError.getInitialMap().duplicate();
        this.nativeRegExpMap = NativeRegExp.getInitialMap().duplicate();
        this.nativeRegExpExecResultMap = NativeRegExpExecResult.getInitialMap().duplicate();
        this.nativeStrictArgumentsMap = NativeStrictArguments.getInitialMap().duplicate();
        this.nativeStringMap = NativeString.getInitialMap().duplicate();
        this.nativeSyntaxErrorMap = NativeSyntaxError.getInitialMap().duplicate();
        this.nativeTypeErrorMap = NativeTypeError.getInitialMap().duplicate();
        this.nativeURIErrorMap = NativeURIError.getInitialMap().duplicate();
        this.prototypeObjectMap = PrototypeObject.getInitialMap().duplicate();
        this.objectMap = JO.getInitialMap().duplicate();
        this.functionMap = ScriptFunctionImpl.getInitialMap().duplicate();
        this.anonymousFunctionMap = ScriptFunctionImpl.getInitialAnonymousMap().duplicate();
        this.strictFunctionMap = ScriptFunctionImpl.getInitialStrictMap().duplicate();
        this.boundFunctionMap = ScriptFunctionImpl.getInitialBoundMap().duplicate();

        // java
        if (! env._no_java) {
            this.nativeJavaImporterMap = NativeJavaImporter.getInitialMap().duplicate();
        }

        // typed arrays
        if (! env._no_typed_arrays) {
            this.arrayBufferViewMap = ArrayBufferView.getInitialMap().duplicate();
            this.nativeArrayBufferMap = NativeArrayBuffer.getInitialMap().duplicate();
        }
    }

    // Function and Object constructors are inter-dependent. Also,
    // Function.prototype
    // functions are not properly initialized. We fix the references here.
    // NOTE: be careful if you want to re-order the operations here. You may
    // have
    // to play with object references carefully!!
    private void initFunctionAndObject() {
        // First-n-foremost is Function
        this.builtinFunction = (ScriptFunction)initConstructor("Function");

        // create global anonymous function
        final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction(this);
        // need to copy over members of Function.prototype to anon function
        anon.addBoundProperties(getFunctionPrototype());

        // Function.prototype === Object.getPrototypeOf(Function) ===
        // <anon-function>
        builtinFunction.setProto(anon);
        builtinFunction.setPrototype(anon);
        anon.set("constructor", builtinFunction, false);
        anon.deleteOwnProperty(anon.getMap().findProperty("prototype"));

        // use "getter" so that [[ThrowTypeError]] function's arity is 0 - as specified in step 10 of section 13.2.3
        this.typeErrorThrower = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_GETTER, null, null, false, false, false);
        typeErrorThrower.setPrototype(UNDEFINED);
        // Non-constructor built-in functions do not have "prototype" property
        typeErrorThrower.deleteOwnProperty(typeErrorThrower.getMap().findProperty("prototype"));
        typeErrorThrower.preventExtensions();

        // now initialize Object
        this.builtinObject = (ScriptFunction)initConstructor("Object");
        final ScriptObject ObjectPrototype = getObjectPrototype();
        // Object.getPrototypeOf(Function.prototype) === Object.prototype
        anon.setProto(ObjectPrototype);

        // Function valued properties of Function.prototype were not properly
        // initialized. Because, these were created before global.function and
        // global.object were not initialized.
        jdk.nashorn.internal.runtime.Property[] properties = getFunctionPrototype().getMap().getProperties();
        for (final jdk.nashorn.internal.runtime.Property property : properties) {
            final Object key = property.getKey();
            final Object value = builtinFunction.get(key);

            if (value instanceof ScriptFunction && value != anon) {
                final ScriptFunction func = (ScriptFunction)value;
                func.setProto(getFunctionPrototype());
                final ScriptObject prototype = ScriptFunction.getPrototype(func);
                if (prototype != null) {
                    prototype.setProto(ObjectPrototype);
                }
            }
        }

        // For function valued properties of Object and Object.prototype, make
        // sure prototype's proto chain ends with Object.prototype
        for (final jdk.nashorn.internal.runtime.Property property : builtinObject.getMap().getProperties()) {
            final Object key = property.getKey();
            final Object value = builtinObject.get(key);

            if (value instanceof ScriptFunction) {
                final ScriptFunction func = (ScriptFunction)value;
                final ScriptObject prototype = ScriptFunction.getPrototype(func);
                if (prototype != null) {
                    prototype.setProto(ObjectPrototype);
                }
            }
        }

        properties = getObjectPrototype().getMap().getProperties();
        for (final jdk.nashorn.internal.runtime.Property property : properties) {
            final Object key   = property.getKey();
            final Object value = ObjectPrototype.get(key);

            if (key.equals("constructor")) {
                continue;
            }

            if (value instanceof ScriptFunction) {
                final ScriptFunction func = (ScriptFunction)value;
                final ScriptObject prototype = ScriptFunction.getPrototype(func);
                if (prototype != null) {
                    prototype.setProto(ObjectPrototype);
                }
            }
        }
    }

    private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class... types) {
        return MH.findStatic(MethodHandles.lookup(), Global.class, name, MH.type(rtype, types));
    }

    RegExpResult getLastRegExpResult() {
        return lastRegExpResult;
    }

    void setLastRegExpResult(final RegExpResult regExpResult) {
        this.lastRegExpResult = regExpResult;
    }

}

Other Java examples (source code examples)

Here is a short list of links related to this Java Global.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.