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 }