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, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); 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, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR); 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 int flags, final Global global) { 113 super(name, methodHandle, getMap(global, isStrict(flags)), scope, specs, flags); 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 flags {@link ScriptFunctionData} flags 125 */ 126 ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags) { 127 this(name, methodHandle, scope, specs, flags, Global.instance()); 128 } 129 130 private ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope, final Global global) { 131 super(data, getMap(global, data.isStrict()), scope); 132 init(global); 133 } 134 135 /** 136 * Constructor called by (compiler) generated code for {@link ScriptObject}s. 137 * 138 * @param data static function data 139 * @param scope scope object 140 */ 141 public ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope) { 142 this(data, scope, Global.instance()); 143 } 144 145 /** 146 * Only invoked internally from {@link BoundScriptFunctionImpl} constructor. 147 * @param data the script function data for the bound function. 148 * @param global the global object 149 */ 150 ScriptFunctionImpl(final ScriptFunctionData data, final Global global) { 151 super(data, getInitialBoundMap(), null); 152 init(global); 153 } 154 155 static { 156 final ArrayList<Property> properties = new ArrayList<>(3); 157 properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE)); 158 properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null)); 159 properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null)); 160 map$ = PropertyMap.newMap(properties); 161 strictmodemap$ = createStrictModeMap(map$); 162 boundfunctionmap$ = createBoundFunctionMap(strictmodemap$); 163 } 164 165 private static PropertyMap createStrictModeMap(final PropertyMap map) { 166 final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE; 167 PropertyMap newMap = map; 168 // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors. 169 newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags)); 170 newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags)); 171 return newMap; 172 } 173 174 private static boolean isStrict(final int flags) { 175 return (flags & ScriptFunctionData.IS_STRICT) != 0; 176 } 177 178 // Choose the map based on strict mode! 179 private static PropertyMap getMap(final Global global, final boolean strict) { 180 return strict ? getInitialStrictMap() : getInitialMap(); 181 } 182 183 private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) { 184 // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see 185 // ECMAScript 5.1 section 15.3.4.5 186 return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype")); 187 } 188 189 // Instance of this class is used as global anonymous function which 190 // serves as Function.prototype object. 191 private static class AnonymousFunction extends ScriptFunctionImpl { 192 private static final PropertyMap anonmap$ = PropertyMap.newMap(); 193 194 static PropertyMap getInitialMap() { 195 return anonmap$; 196 } 197 198 AnonymousFunction(final Global global) { 199 super("", GlobalFunctions.ANONYMOUS, getInitialAnonymousMap(), null); 200 } 201 } 202 203 static ScriptFunctionImpl newAnonymousFunction(final Global global) { 204 return new AnonymousFunction(global); 205 } 206 207 /** 208 * Factory method for non-constructor built-in functions 209 * 210 * @param name function name 211 * @param methodHandle handle for invocation 212 * @param specs specialized versions of function if available, null otherwise 213 * @return new ScriptFunction 214 */ 215 static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) { 216 final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, ScriptFunctionData.IS_BUILTIN); 217 func.setPrototype(UNDEFINED); 218 // Non-constructor built-in functions do not have "prototype" property 219 func.deleteOwnProperty(func.getMap().findProperty("prototype")); 220 221 return func; 222 } 223 224 /** 225 * Factory method for non-constructor built-in functions 226 * 227 * @param name function name 228 * @param methodHandle handle for invocation 229 * @return new ScriptFunction 230 */ 231 static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) { 232 return makeFunction(name, methodHandle, null); 233 } 234 235 @Override 236 public ScriptFunction makeSynchronizedFunction(final Object sync) { 237 final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync); 238 return makeFunction(getName(), mh); 239 } 240 241 /** 242 * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we 243 * can expose it to methods in this package. 244 * @param self the self to bind to this function. Can be null (in which case, null is bound as this). 245 * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments. 246 * @return a function with the specified self and parameters bound. 247 */ 248 @Override 249 protected ScriptFunction makeBoundFunction(Object self, Object[] args) { 250 return super.makeBoundFunction(self, args); 251 } 252 253 /** 254 * This method is used to create a bound function based on this function. 255 * 256 * @param data the {@code ScriptFunctionData} specifying the functions immutable portion. 257 * @return a function initialized from the specified data. Its parent scope will be set to null, therefore the 258 * passed in data should not expect a callee. 259 */ 260 @Override 261 protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) { 262 return new BoundScriptFunctionImpl(data, getTargetFunction()); 263 } 264 265 // return Object.prototype - used by "allocate" 266 @Override 267 protected final ScriptObject getObjectPrototype() { 268 return Global.objectPrototype(); 269 } 270 271 @Override 272 public final Object getPrototype() { 273 if (prototype == LAZY_PROTOTYPE) { 274 prototype = new PrototypeObject(this); 275 } 276 return prototype; 277 } 278 279 @Override 280 public final void setPrototype(final Object newProto) { 281 if (newProto instanceof ScriptObject && newProto != this.prototype && allocatorMap != null) { 282 // Replace our current allocator map with one that is associated with the new prototype. 283 allocatorMap = allocatorMap.changeProto((ScriptObject)newProto); 284 } 285 this.prototype = newProto; 286 } 287 288 // Internals below.. 289 private void init(final Global global) { 290 this.setInitialProto(global.getFunctionPrototype()); 291 this.prototype = LAZY_PROTOTYPE; 292 293 // We have to fill user accessor functions late as these are stored 294 // in this object rather than in the PropertyMap of this object. 295 296 final ScriptFunction errorThrower = global.getTypeErrorThrower(); 297 if (findProperty("arguments", true) != null) { 298 setUserAccessors("arguments", errorThrower, errorThrower); 299 } 300 301 if (findProperty("caller", true) != null) { 302 setUserAccessors("caller", errorThrower, errorThrower); 303 } 304 } 305 }