1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 package org.mozilla.javascript; 8 9 import java.util.EnumMap; 10 11 /** 12 * A top-level scope object that provides special means to cache and preserve 13 * the initial values of the built-in constructor properties for better 14 * ECMAScript compliance. 15 * 16 * <p>ECMA 262 requires that most constructors used internally construct 17 * objects with the original prototype object as value of their [[Prototype]] 18 * internal property. Since built-in global constructors are defined as 19 * writable and deletable, this means they should be cached to protect against 20 * redefinition at runtime.</p> 21 * 22 * <p>In order to implement this efficiently, this class provides a mechanism 23 * to access the original built-in global constructors and their prototypes 24 * via numeric class-ids. To make use of this, the new 25 * {@link ScriptRuntime#newBuiltinObject ScriptRuntime.newBuiltinObject} and 26 * {@link ScriptRuntime#setBuiltinProtoAndParent ScriptRuntime.setBuiltinProtoAndParent} 27 * methods should be used to create and initialize objects of built-in classes 28 * instead of their generic counterparts.</p> 29 * 30 * <p>Calling {@link org.mozilla.javascript.Context#initStandardObjects()} 31 * with an instance of this class as argument will automatically cache 32 * built-in classes after initialization. For other setups involving 33 * top-level scopes that inherit global properties from their proptotypes 34 * (e.g. with dynamic scopes) embeddings should explicitly call 35 * {@link #cacheBuiltins()} to initialize the class cache for each top-level 36 * scope.</p> 37 */ 38 public class TopLevel extends IdScriptableObject { 39 40 static final long serialVersionUID = -4648046356662472260L; 41 42 /** 43 * An enumeration of built-in ECMAScript objects. 44 */ 45 public enum Builtins { 46 /** The built-in Object type. */ 47 Object, 48 /** The built-in Array type. */ 49 Array, 50 /** The built-in Function type. */ 51 Function, 52 /** The built-in String type. */ 53 String, 54 /** The built-in Number type. */ 55 Number, 56 /** The built-in Boolean type. */ 57 Boolean, 58 /** The built-in RegExp type. */ 59 RegExp, 60 /** The built-in Error type. */ 61 Error 62 } 63 64 private EnumMap<Builtins, BaseFunction> ctors; 65 66 @Override getClassName()67 public String getClassName() { 68 return "global"; 69 } 70 71 /** 72 * Cache the built-in ECMAScript objects to protect them against 73 * modifications by the script. This method is called automatically by 74 * {@link ScriptRuntime#initStandardObjects ScriptRuntime.initStandardObjects} 75 * if the scope argument is an instance of this class. It only has to be 76 * called by the embedding if a top-level scope is not initialized through 77 * <code>initStandardObjects()</code>. 78 */ cacheBuiltins()79 public void cacheBuiltins() { 80 ctors = new EnumMap<Builtins, BaseFunction>(Builtins.class); 81 for (Builtins builtin : Builtins.values()) { 82 Object value = ScriptableObject.getProperty(this, builtin.name()); 83 if (value instanceof BaseFunction) { 84 ctors.put(builtin, (BaseFunction)value); 85 } 86 } 87 } 88 89 /** 90 * Static helper method to get a built-in object constructor with the given 91 * <code>type</code> from the given <code>scope</code>. If the scope is not 92 * an instance of this class or does have a cache of built-ins, 93 * the constructor is looked up via normal property lookup. 94 * 95 * @param cx the current Context 96 * @param scope the top-level scope 97 * @param type the built-in type 98 * @return the built-in constructor 99 */ getBuiltinCtor(Context cx, Scriptable scope, Builtins type)100 public static Function getBuiltinCtor(Context cx, 101 Scriptable scope, 102 Builtins type) { 103 // must be called with top level scope 104 assert scope.getParentScope() == null; 105 if (scope instanceof TopLevel) { 106 Function result = ((TopLevel)scope).getBuiltinCtor(type); 107 if (result != null) { 108 return result; 109 } 110 } 111 // fall back to normal constructor lookup 112 return ScriptRuntime.getExistingCtor(cx, scope, type.name()); 113 } 114 115 /** 116 * Static helper method to get a built-in object prototype with the given 117 * <code>type</code> from the given <code>scope</code>. If the scope is not 118 * an instance of this class or does have a cache of built-ins, 119 * the prototype is looked up via normal property lookup. 120 * 121 * @param scope the top-level scope 122 * @param type the built-in type 123 * @return the built-in prototype 124 */ getBuiltinPrototype(Scriptable scope, Builtins type)125 public static Scriptable getBuiltinPrototype(Scriptable scope, 126 Builtins type) { 127 // must be called with top level scope 128 assert scope.getParentScope() == null; 129 if (scope instanceof TopLevel) { 130 Scriptable result = ((TopLevel)scope) 131 .getBuiltinPrototype(type); 132 if (result != null) { 133 return result; 134 } 135 } 136 // fall back to normal prototype lookup 137 return ScriptableObject.getClassPrototype(scope, type.name()); 138 } 139 140 /** 141 * Get the cached built-in object constructor from this scope with the 142 * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not 143 * been called on this object. 144 * @param type the built-in type 145 * @return the built-in constructor 146 */ getBuiltinCtor(Builtins type)147 public BaseFunction getBuiltinCtor(Builtins type) { 148 return ctors != null ? ctors.get(type) : null; 149 } 150 151 /** 152 * Get the cached built-in object prototype from this scope with the 153 * given <code>type</code>. Returns null if {@link #cacheBuiltins()} has not 154 * been called on this object. 155 * @param type the built-in type 156 * @return the built-in prototype 157 */ getBuiltinPrototype(Builtins type)158 public Scriptable getBuiltinPrototype(Builtins type) { 159 BaseFunction func = getBuiltinCtor(type); 160 Object proto = func != null ? func.getPrototypeProperty() : null; 161 return proto instanceof Scriptable ? (Scriptable) proto : null; 162 } 163 164 } 165