/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1999 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Hannes Wallnoefer * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License Version 2 or later (the "GPL"), in which * case the provisions of the GPL are applicable instead of those above. If * you wish to allow use of your version of this file only under the terms of * the GPL and not to allow others to use your version of this file under the * MPL, indicate your decision by deleting the provisions above and replacing * them with the notice and other provisions required by the GPL. If you do * not delete the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * ***** END LICENSE BLOCK ***** */ package org.mozilla.javascript; import java.util.EnumMap; /** * A top-level scope object that provides special means to cache and preserve * the initial values of the built-in constructor properties for better * ECMAScript compliance. * * <p>ECMA 262 requires that most constructors used internally construct * objects with the original prototype object as value of their [[Prototype]] * internal property. Since built-in global constructors are defined as * writable and deletable, this means they should be cached to protect against * redefinition at runtime.</p> * * <p>In order to implement this efficiently, this class provides a mechanism * to access the original built-in global constructors and their prototypes * via numeric class-ids. To make use of this, the new * {@link ScriptRuntime#newBuiltinObject ScriptRuntime.newBuiltinObject} and * {@link ScriptRuntime#setBuiltinProtoAndParent ScriptRuntime.setBuiltinProtoAndParent} * methods should be used to create and initialize objects of built-in classes * instead of their generic counterparts.</p> * * <p>Calling {@link org.mozilla.javascript.Context#initStandardObjects()} * with an instance of this class as argument will automatically cache * built-in classes after initialization. For other setups involving * top-level scopes that inherit global properties from their proptotypes * (e.g. with dynamic scopes) embeddings should explicitly call * {@link #cacheBuiltins()} to initialize the class cache for each top-level * scope.</p> */ public class TopLevel extends IdScriptableObject { static final long serialVersionUID = -4648046356662472260L; /** * An enumeration of built-in ECMAScript objects. */ public enum Builtins { /** The built-in Object type. */ Object, /** The built-in Array type. */ Array, /** The built-in Function type. */ Function, /** The built-in String type. */ String, /** The built-in Number type. */ Number, /** The built-in Boolean type. */ Boolean, /** The built-in RegExp type. */ RegExp, /** The built-in Error type. */ Error } private EnumMap<Builtins, BaseFunction> ctors; @Override public String getClassName() { return "global"; } /** * Cache the built-in ECMAScript objects to protect them against * modifications by the script. This method is called automatically by * {@link ScriptRuntime#initStandardObjects ScriptRuntime.initStandardObjects} * if the scope argument is an instance of this class. It only has to be * called by the embedding if a top-level scope is not initialized through * <code>initStandardObjects()</code>. */ public void cacheBuiltins() { ctors = new EnumMap<Builtins, BaseFunction>(Builtins.class); for (Builtins builtin : Builtins.values()) { Object value = ScriptableObject.getProperty(this, builtin.name()); if (value instanceof BaseFunction) { ctors.put(builtin, (BaseFunction)value); } } } /** * Static helper method to get a built-in object constructor with the given * <code>type</code> from the given <code>scope</code>. If the scope is not * an instance of this class or does have a cache of built-ins, * the constructor is looked up via normal property lookup. * * @param cx the current Context * @param scope the top-level scope * @param type the built-in type * @return the built-in constructor */ public static Function getBuiltinCtor(Context cx, Scriptable scope, Builtins type) { // must be called with top level scope assert scope.getParentScope() == null; if (scope instanceof TopLevel) { Function result = ((TopLevel)scope).getBuiltinCtor(type); if (result != null) { return result; } } // fall back to normal constructor lookup return ScriptRuntime.getExistingCtor(cx, scope, type.name()); } /** * Static helper method to get a built-in object prototype with the given * <code>type</code> from the given <code>scope</code>. If the scope is not * an instance of this class or does have a cache of built-ins, * the prototype is looked up via normal property lookup. * * @param scope the top-level scope * @param type the built-in type * @return the built-in prototype */ public static Scriptable getBuiltinPrototype(Scriptable scope, Builtins type) { // must be called with top level scope assert scope.getParentScope() == null; if (scope instanceof TopLevel) { Scriptable result = ((TopLevel)scope) .getBuiltinPrototype(type); if (result != null) { return result; } } // fall back to normal prototype lookup return ScriptableObject.getClassPrototype(scope, type.name()); } /** * Get the cached built-in object constructor from this scope with the * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not * been called on this object. * @param type the built-in type * @return the built-in constructor */ public BaseFunction getBuiltinCtor(Builtins type) { return ctors != null ? ctors.get(type) : null; } /** * Get the cached built-in object prototype from this scope with the * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not * been called on this object. * @param type the built-in type * @return the built-in prototype */ public Scriptable getBuiltinPrototype(Builtins type) { BaseFunction func = getBuiltinCtor(type); Object proto = func != null ? func.getPrototypeProperty() : null; return proto instanceof Scriptable ? (Scriptable) proto : null; } }