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.runtime.ScriptRuntime.UNDEFINED; 29 import static jdk.nashorn.internal.runtime.linker.Lookup.MH; 30 31 import java.lang.invoke.MethodHandle; 32 import java.lang.invoke.MethodHandles; 33 import jdk.nashorn.internal.codegen.objects.FunctionObjectCreator; 34 import jdk.nashorn.internal.runtime.GlobalFunctions; 35 import jdk.nashorn.internal.runtime.Property; 36 import jdk.nashorn.internal.runtime.PropertyMap; 37 import jdk.nashorn.internal.runtime.ScriptFunction; 38 import jdk.nashorn.internal.runtime.ScriptObject; 39 import jdk.nashorn.internal.runtime.ScriptRuntime; 40 import jdk.nashorn.internal.runtime.Source; 41 import jdk.nashorn.internal.runtime.linker.Lookup; 42 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory; 43 44 /** 45 * Concrete implementation of ScriptFunction. This sets correct map for the 46 * function objects -- to expose properties like "prototype", "length" etc. 47 */ 48 public class ScriptFunctionImpl extends ScriptFunction { 49 // per-function object flags 50 private static final int IS_STRICT = 0b0000_0001; 51 private static final int IS_BUILTIN = 0b0000_0010; 52 private static final int HAS_CALLEE = 0b0000_0100; 53 54 // set this function to be a builtin function 55 private void setIsBuiltin() { 56 flags |= IS_BUILTIN; 57 } 58 59 // set this function to be a ECMAScript strict function 60 private void setIsStrict() { 61 flags |= IS_STRICT; 62 } 63 64 private static final MethodHandle BOUND_FUNCTION = findOwnMH("boundFunction", Object.class, ScriptFunction.class, Object.class, Object[].class, Object.class, Object[].class); 65 private static final MethodHandle BOUND_CONSTRUCTOR = findOwnMH("boundConstructor", Object.class, ScriptFunction.class, Object[].class, Object.class, Object[].class); 66 67 private static final PropertyMap nasgenmap$; 68 69 private int flags; 70 71 /** 72 * Constructor 73 * 74 * Called by Nasgen generated code, no membercount, use the default map 75 * Creates builtin functions only 76 * 77 * @param name name of function 78 * @param invokeHandle handle for invocation 79 * @param specs specialized versions of this method, if available, null otherwise 80 */ 81 ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) { 82 this(name, invokeHandle, nasgenmap$, specs); 83 } 84 85 /** 86 * Constructor 87 * 88 * Called by Nasgen generated code, no membercount, use the default map 89 * Creates builtin functions only 90 * 91 * @param name name of function 92 * @param methodHandle handle for invocation 93 * @param map initial property map 94 * @param specs specialized versions of this method, if available, null otherwise 95 */ 96 ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final PropertyMap map, final MethodHandle[] specs) { 97 super(name, methodHandle, (nasgenmap$ == map) ? nasgenmap$ : map.addAll(nasgenmap$), null, null, 0, false, specs); 98 this.setIsBuiltin(); 99 init(); 100 } 101 102 /** 103 * Constructor 104 * 105 * Called by Global.newScriptFunction (runtime) 106 * 107 * @param name name of function 108 * @param methodHandle handle for invocation 109 * @param scope scope object 110 * @param strict are we in strict mode 111 * @param specs specialized versions of this method, if available, null otherwise 112 */ 113 ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final boolean strict, final MethodHandle[] specs) { 114 super(name, methodHandle, getMap(strict), scope, specs); 115 if (strict) { 116 this.setIsStrict(); 117 } 118 init(); 119 } 120 121 /** 122 * Constructor 123 * 124 * Called by (compiler) generated code for {@link ScriptObject}s. Code is 125 * generated by {@link FunctionObjectCreator} 126 * 127 * TODO this is a horrible constructor - can we do it with fewer args? 128 * 129 * @param name name of function 130 * @param methodHandle handle for invocation 131 * @param scope scope object 132 * @param source source 133 * @param token token 134 * @param allocator instance constructor for function 135 * @param allocatorMap initial map that constructor will keep reference to for future instantiations 136 * @param needCallee does the function use the {@code callee} variable 137 * @param strict are we in strict mode 138 */ 139 public ScriptFunctionImpl( 140 final String name, 141 final MethodHandle methodHandle, 142 final ScriptObject scope, 143 final Source source, 144 final long token, 145 final MethodHandle allocator, 146 final PropertyMap allocatorMap, 147 final boolean needCallee, 148 final boolean strict) { 149 super(name, methodHandle, getMap(strict), scope, source, token, allocator, allocatorMap, needCallee, null); 150 if (strict) { 151 this.setIsStrict(); 152 } 153 init(); 154 } 155 156 static { 157 PropertyMap map = PropertyMap.newMap(ScriptFunctionImpl.class); 158 map = Lookup.newProperty(map, "prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE); 159 map = Lookup.newProperty(map, "length", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null); 160 map = Lookup.newProperty(map, "name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null); 161 nasgenmap$ = map; 162 } 163 164 // function object representing TypeErrorThrower 165 private static ScriptFunction typeErrorThrower; 166 167 static synchronized ScriptFunction getTypeErrorThrower() { 168 if (typeErrorThrower == null) { 169 //name handle 170 final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, false, null); 171 // clear constructor handle... 172 func.constructHandle = null; 173 func.prototype = UNDEFINED; 174 typeErrorThrower = func; 175 } 176 177 return typeErrorThrower; 178 } 179 180 // add a new property that throws TypeError on get as well as set 181 static synchronized PropertyMap newThrowerProperty(final PropertyMap map, final String name, final int flags) { 182 return map.newProperty(name, flags, Lookup.TYPE_ERROR_THROWER_GETTER, Lookup.TYPE_ERROR_THROWER_SETTER); 183 } 184 185 // property map for strict mode functions - lazily initialized 186 private static PropertyMap strictmodemap$; 187 188 // Choose the map based on strict mode! 189 private static PropertyMap getMap(final boolean strict) { 190 if (strict) { 191 synchronized (ScriptFunctionImpl.class) { 192 if (strictmodemap$ == null) { 193 // In strict mode, the following properties should throw TypeError 194 strictmodemap$ = nasgenmap$; 195 strictmodemap$ = newThrowerProperty(strictmodemap$, "arguments", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE); 196 strictmodemap$ = newThrowerProperty(strictmodemap$, "caller", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE); 197 } 198 } 199 return strictmodemap$; 200 } 201 202 return nasgenmap$; 203 } 204 205 // Instance of this class is used as global anonymous function which 206 // serves as Function.prototype object. 207 private static class AnonymousFunction extends ScriptFunctionImpl { 208 private static final PropertyMap nasgenmap$$ = PropertyMap.newMap(AnonymousFunction.class); 209 210 AnonymousFunction() { 211 super("", GlobalFunctions.ANONYMOUS, nasgenmap$$, null); 212 } 213 } 214 215 static ScriptFunctionImpl newAnonymousFunction() { 216 return new AnonymousFunction(); 217 } 218 219 @Override 220 public final boolean isStrict() { 221 return (flags & IS_STRICT) != 0; 222 } 223 224 @Override 225 public final boolean hasCalleeParameter() { 226 return (flags & HAS_CALLEE) != 0; 227 } 228 229 @Override 230 protected void setHasCalleeParameter() { 231 flags |= HAS_CALLEE; 232 } 233 234 @Override 235 public final boolean isBuiltin() { 236 return (flags & IS_BUILTIN) != 0; 237 } 238 239 /** 240 * Factory method for non-constructor functions 241 * 242 * @param name function name 243 * @param methodHandle handle for invocation 244 * @param specs specialized versions of function if available, null otherwise 245 * @param strict are we in strict mode 246 * @return new ScriptFunction 247 */ 248 public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict) { 249 final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, strict, specs); 250 251 func.setIsBuiltin(); 252 func.setConstructHandle(null); 253 func.setPrototype(UNDEFINED); 254 255 return func; 256 } 257 258 /** 259 * Factory method for non-constructor functions 260 * 261 * @param name function name 262 * @param methodHandle handle for invocation 263 * @param specs specialized versions of function if available, null otherwise 264 * @return new ScriptFunction 265 */ 266 public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) { 267 return makeFunction(name, methodHandle, specs, false); 268 } 269 270 /** 271 * Factory method for non-constructor functions 272 * 273 * @param name function name 274 * @param methodHandle handle for invocation 275 * @return new ScriptFunction 276 */ 277 public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) { 278 return makeFunction(name, methodHandle, null); 279 } 280 281 /** 282 * This method is used to create a bound function. See also 283 * {@link NativeFunction#bind(Object, Object...)} method implementation. 284 * 285 * @param thiz this reference to bind 286 * @param args arguments to bind 287 */ 288 @Override 289 public ScriptFunction makeBoundFunction(final Object thiz, final Object[] args) { 290 Object[] allArgs = args; 291 292 if (allArgs == null) { 293 allArgs = ScriptRuntime.EMPTY_ARRAY; 294 } 295 296 final Object boundThiz = convertThisObject(thiz); 297 final MethodHandle boundMethod = MH.insertArguments(BOUND_FUNCTION, 0, this, boundThiz, allArgs); 298 final ScriptFunction boundFunc = makeFunction("", boundMethod, null, true); 299 300 MethodHandle consHandle = this.getConstructHandle(); 301 302 if (consHandle != null) { 303 consHandle = MH.insertArguments(BOUND_CONSTRUCTOR, 0, this, allArgs); 304 } 305 306 boundFunc.setConstructHandle(consHandle); 307 int newArity = this.getArity(); 308 if (newArity != -1) { 309 newArity -= Math.min(newArity, allArgs.length); 310 } 311 boundFunc.setArity(newArity); 312 313 return boundFunc; 314 } 315 316 @SuppressWarnings("unused") 317 private static Object boundFunction(final ScriptFunction wrapped, final Object boundThiz, final Object[] boundArgs, final Object thiz, final Object[] args) { 318 final Object[] allArgs = new Object[boundArgs.length + args.length]; 319 320 System.arraycopy(boundArgs, 0, allArgs, 0, boundArgs.length); 321 System.arraycopy(args, 0, allArgs, boundArgs.length, args.length); 322 323 return ScriptRuntime.apply(wrapped, boundThiz, allArgs); 324 } 325 326 @SuppressWarnings("unused") 327 private static Object boundConstructor(final ScriptFunction wrapped, final Object[] boundArgs, final Object thiz, final Object[] args) { 328 final Object[] allArgs = new Object[boundArgs.length + args.length]; 329 System.arraycopy(boundArgs, 0, allArgs, 0, boundArgs.length); 330 System.arraycopy(args, 0, allArgs, boundArgs.length, args.length); 331 332 return ScriptRuntime.construct(wrapped, allArgs); 333 } 334 335 // return Object.prototype - used by "allocate" 336 @Override 337 protected final ScriptObject getObjectPrototype() { 338 return Global.objectPrototype(); 339 } 340 341 // Internals below.. 342 private void init() { 343 this.setProto(Global.instance().getFunctionPrototype()); 344 this.setPrototype(new PrototypeObject(this)); 345 346 if (isStrict()) { 347 final ScriptFunction func = getTypeErrorThrower(); 348 // We have to fill user accessor functions late as these are stored 349 // in this object rather than in the PropertyMap of this object. 350 setUserAccessors("arguments", func, func); 351 setUserAccessors("caller", func, func); 352 } 353 } 354 355 private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 356 try { 357 return MethodHandles.lookup().findStatic(ScriptFunctionImpl.class, name, MH.type(rtype, types)); 358 } catch (final NoSuchMethodException | IllegalAccessException e) { 359 throw new MethodHandleFactory.LookupException(e); 360 } 361 } 362 }