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