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