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 }
--- EOF ---