1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.objects; 27 28 import static jdk.nashorn.internal.lookup.Lookup.MH; 29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 30 31 import java.lang.invoke.MethodHandle; 32 import java.util.ArrayList; 33 import jdk.nashorn.internal.runtime.GlobalFunctions; 34 import jdk.nashorn.internal.runtime.Property; 35 import jdk.nashorn.internal.runtime.PropertyMap; 36 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; 37 import jdk.nashorn.internal.runtime.ScriptFunction; 38 import jdk.nashorn.internal.runtime.ScriptFunctionData; 39 import jdk.nashorn.internal.runtime.ScriptObject; 40 import jdk.nashorn.internal.runtime.AccessorProperty; 41 42 /** 43 * Concrete implementation of ScriptFunction. This sets correct map for the 44 * function objects -- to expose properties like "prototype", "length" etc. 45 */ 46 public class ScriptFunctionImpl extends ScriptFunction { 47 48 /** Reference to constructor prototype. */ 49 private Object prototype; 50 51 // property map for strict mode functions 52 private static final PropertyMap strictmodemap$; 53 // property map for bound functions 54 private static final PropertyMap boundfunctionmap$; 55 // property map for non-strict, non-bound functions. 56 private static final PropertyMap map$; 57 58 static PropertyMap getInitialMap() { 59 return map$; 60 } 61 62 static PropertyMap getInitialAnonymousMap() { 63 return AnonymousFunction.getInitialMap(); 64 } 65 66 static PropertyMap getInitialStrictMap() { 67 return strictmodemap$; 68 } 69 70 static PropertyMap getInitialBoundMap() { 71 return boundfunctionmap$; 72 } 73 74 // Marker object for lazily initialized prototype object 75 private static final Object LAZY_PROTOTYPE = new Object(); 76 77 private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs, final Global global) { 78 super(name, invokeHandle, getInitialMap(), null, specs, false, true, true); 79 init(global); 80 } 81 82 /** 83 * Constructor called by Nasgen generated code, no membercount, use the default map. 84 * Creates builtin functions only. 85 * 86 * @param name name of function 87 * @param invokeHandle handle for invocation 88 * @param specs specialized versions of this method, if available, null otherwise 89 */ 90 ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) { 91 this(name, invokeHandle, specs, Global.instance()); 92 } 93 94 private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs, final Global global) { 95 super(name, invokeHandle, map.addAll(getInitialMap()), null, specs, false, true, true); 96 init(global); 97 } 98 99 /** 100 * Constructor called by Nasgen generated code, no membercount, use the map passed as argument. 101 * Creates builtin functions only. 102 * 103 * @param name name of function 104 * @param invokeHandle handle for invocation 105 * @param map initial property map 106 * @param specs specialized versions of this method, if available, null otherwise 107 */ 108 ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) { 109 this(name, invokeHandle, map, specs, Global.instance()); 110 } 111 112 private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor, final Global global) { 113 super(name, methodHandle, getMap(global, isStrict), scope, specs, isStrict, isBuiltin, isConstructor); 114 init(global); 115 } 116 117 /** 118 * Constructor called by Global.newScriptFunction (runtime). 119 * 120 * @param name name of function 121 * @param methodHandle handle for invocation 122 * @param scope scope object 123 * @param specs specialized versions of this method, if available, null otherwise 124 * @param isStrict are we in strict mode 125 * @param isBuiltin is this a built-in function 126 * @param isConstructor can the function be used as a constructor (most can; some built-ins are restricted). 127 */ 128 ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean isStrict, final boolean isBuiltin, final boolean isConstructor) { 129 this(name, methodHandle, scope, specs, isStrict, isBuiltin, isConstructor, Global.instance()); 130 } 131 132 private ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope, final Global global) { 133 super(data, getMap(global, data.isStrict()), scope); 134 init(global); 135 } 136 137 /** 138 * Constructor called by (compiler) generated code for {@link ScriptObject}s. 139 * 140 * @param data static function data 141 * @param scope scope object 142 */ 143 public ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope) { 144 this(data, scope, Global.instance()); 145 } 146 147 /** 148 * Only invoked internally from {@link BoundScriptFunctionImpl} constructor. 149 * @param data the script function data for the bound function. 150 * @param global the global object 151 */ 152 ScriptFunctionImpl(final ScriptFunctionData data, final Global global) { 153 super(data, getInitialBoundMap(), null); 154 init(global); 155 } 156 157 static { 158 final ArrayList<Property> properties = new ArrayList<>(3); 159 properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); 160 properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); 161 properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); 162 map$ = PropertyMap.newMap(properties); 163 strictmodemap$ = createStrictModeMap(map$); 164 boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); 165 } 166 167 private static PropertyMap createStrictModeMap(final PropertyMap map) { 168 final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; 169 PropertyMap newMap = map; 170 // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. 171 newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags)); 172 newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags)); 173 return newMap; 174 } 175 176 // Choose the map based on strict mode! 177 private static PropertyMap getMap(final Global global, final boolean strict) { 178 return strict ? getInitialStrictMap() : getInitialMap(); 179 } 180 181 private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { 182 // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see 183 // ECMAScript 5.1 section 15.3.4.5 184 return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); 185 } 186 187 // Instance of this class is used as global anonymous function which 188 // serves as Function.prototype object. 189 private static class AnonymousFunction extends ScriptFunctionImpl { 190 private static final PropertyMap anonmap$ = PropertyMap.newMap(); 191 192 static PropertyMap getInitialMap() { 193 return anonmap$; 194 } 195 196 AnonymousFunction(final Global global) { 197 super("", GlobalFunctions.ANONYMOUS, getInitialAnonymousMap(), null); 198 } 199 } 200 201 static ScriptFunctionImpl newAnonymousFunction(final Global global) { 202 return new AnonymousFunction(global); 203 } 204 205 /** 206 * Factory method for non-constructor built-in functions 207 * 208 * @param name function name 209 * @param methodHandle handle for invocation 210 * @param specs specialized versions of function if available, null otherwise 211 * @return new ScriptFunction 212 */ 213 static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) { 214 final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, false, true, false); 215 func.setPrototype(UNDEFINED); 216 // Non-constructor built-in functions do not have "prototype" property 217 func.deleteOwnProperty(func.getMap().findProperty("prototype")); 218 219 return func; 220 } 221 222 /** 223 * Factory method for non-constructor built-in functions 224 * 225 * @param name function name 226 * @param methodHandle handle for invocation 227 * @return new ScriptFunction 228 */ 229 static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) { 230 return makeFunction(name, methodHandle, null); 231 } 232 233 @Override 234 public ScriptFunction makeSynchronizedFunction(final Object sync) { 235 final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); 236 return makeFunction(getName(), mh); 237 } 238 239 /** 240 * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we 241 * can expose it to methods in this package. 242 * @param self the self to bind to this function. Can be null (in which case, null is bound as this). 243 * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. 244 * @return a function with the specified self and parameters bound. 245 */ 246 @Override 247 protected ScriptFunction makeBoundFunction(Object self, Object[] args) { 248 return super.makeBoundFunction(self, args); 249 } 250 251 /** 252 * This method is used to create a bound function based on this function. 253 * 254 * @param data the {@code ScriptFunctionData} specifying the functions immutable portion. 255 * @return a function initialized from the specified data. Its parent scope will be set to null, therefore the 256 * passed in data should not expect a callee. 257 */ 258 @Override 259 protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) { 260 return new BoundScriptFunctionImpl(data, getTargetFunction()); 261 } 262 263 // return Object.prototype - used by "allocate" 264 @Override 265 protected final ScriptObject getObjectPrototype() { 266 return Global.objectPrototype(); 267 } 268 269 @Override 270 public final Object getPrototype() { 271 if (prototype == LAZY_PROTOTYPE) { 272 prototype = new PrototypeObject(this); 273 } 274 return prototype; 275 } 276 277 @Override 278 public final void setPrototype(final Object newProto) { 279 if (newProto instanceof ScriptObject && newProto != this.prototype && allocatorMap != null) { 280 // Replace our current allocator map with one that is associated with the new prototype. 281 allocatorMap = allocatorMap.changeProto((ScriptObject)newProto); 282 } 283 this.prototype = newProto; 284 } 285 286 // Internals below.. 287 private void init(final Global global) { 288 this.setInitialProto(global.getFunctionPrototype()); 289 this.prototype = LAZY_PROTOTYPE; 290 291 // We have to fill user accessor functions late as these are stored 292 // in this object rather than in the PropertyMap of this object. 293 294 final ScriptFunction errorThrower = global.getTypeErrorThrower(); 295 if (findProperty("arguments", true) != null) { 296 setUserAccessors("arguments", errorThrower, errorThrower); 297 } 298 299 if (findProperty("caller", true) != null) { 300 setUserAccessors("caller", errorThrower, errorThrower); 301 } 302 } 303 } --- EOF ---