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.runtime.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.linker.Lookup;
  43 import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
  44 
  45 /**
  46  * Concrete implementation of ScriptFunction. This sets correct map for the
  47  * function objects -- to expose properties like "prototype", "length" etc.
  48  */
  49 public class ScriptFunctionImpl extends ScriptFunction {














  50 
  51     private static final MethodHandle BOUND_FUNCTION    = findOwnMH("boundFunction",    Object.class, ScriptFunction.class, Object.class, Object[].class, Object.class, Object[].class);
  52     private static final MethodHandle BOUND_CONSTRUCTOR = findOwnMH("boundConstructor", Object.class, ScriptFunction.class, Object[].class, Object.class, Object[].class);
  53 
  54     private static final PropertyMap nasgenmap$;
  55 


  56     /**
  57      * Constructor called by Nasgen generated code, no membercount, use the default map.
  58      * Creates builtin functions only.


  59      *
  60      * @param name name of function
  61      * @param invokeHandle handle for invocation
  62      * @param specs specialized versions of this method, if available, null otherwise
  63      */
  64     ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
  65         super(name, invokeHandle, nasgenmap$, null, specs, false, true);
  66         init();
  67     }
  68 
  69     /**
  70      * Constructor called by Nasgen generated code, no membercount, use the map passed as argument.
  71      * Creates builtin functions only.


  72      *
  73      * @param name name of function
  74      * @param invokeHandle handle for invocation
  75      * @param map initial property map
  76      * @param specs specialized versions of this method, if available, null otherwise
  77      */
  78     ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
  79         super(name, invokeHandle, map.addAll(nasgenmap$), null, specs, false, true);

  80         init();
  81     }
  82 
  83     /**
  84      * Constructor called by Global.newScriptFunction (runtime).


  85      *
  86      * @param name name of function
  87      * @param methodHandle handle for invocation
  88      * @param scope scope object

  89      * @param specs specialized versions of this method, if available, null otherwise
  90      * @param strict are we in strict mode
  91      * @param builtin is this a built-in function
  92      */
  93     ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final boolean strict, final boolean builtin) {
  94         super(name, methodHandle, getMap(strict), scope, specs, strict, builtin);



  95         init();
  96     }
  97 
  98     /**
  99      * Constructor called by (compiler) generated code for {@link ScriptObject}s.
 100      * Code is generated by {@link FunctionObjectCreator}


 101      *
 102      * @param data static function data


 103      * @param methodHandle handle for invocation
 104      * @param scope scope object


 105      * @param allocator instance constructor for function



 106      */
 107     public ScriptFunctionImpl(final ScriptFunctionData data, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle allocator) {
 108         super(data, getMap(data.isStrict()), scope);
 109         // Set method handles in script data
 110         if (data.getInvoker() == null) {
 111             data.setMethodHandles(methodHandle, allocator);








 112         }
 113         init();
 114     }
 115 
 116     static {
 117         PropertyMap map = PropertyMap.newMap(ScriptFunctionImpl.class);
 118         map = Lookup.newProperty(map, "prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE);
 119         map = Lookup.newProperty(map, "length",    Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null);
 120         map = Lookup.newProperty(map, "name",      Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null);
 121         nasgenmap$ = map;
 122     }
 123 
 124     // function object representing TypeErrorThrower
 125     private static ScriptFunction typeErrorThrower;
 126 
 127     static synchronized ScriptFunction getTypeErrorThrower() {
 128         if (typeErrorThrower == null) {
 129             //name handle
 130             final ScriptFunctionImpl func = new ScriptFunctionImpl("TypeErrorThrower", Lookup.TYPE_ERROR_THROWER_SETTER, null, null, false, false);
 131             // clear constructor handle...
 132             func.setConstructHandle(null);
 133             func.setPrototype(UNDEFINED);
 134             typeErrorThrower = func;
 135         }
 136 
 137         return typeErrorThrower;
 138     }
 139 
 140     // add a new property that throws TypeError on get as well as set
 141     static synchronized PropertyMap newThrowerProperty(final PropertyMap map, final String name, final int flags) {
 142         return map.newProperty(name, flags, Lookup.TYPE_ERROR_THROWER_GETTER, Lookup.TYPE_ERROR_THROWER_SETTER);
 143     }
 144 
 145     // property map for strict mode functions - lazily initialized
 146     private static PropertyMap strictmodemap$;
 147 
 148     // Choose the map based on strict mode!
 149     private static PropertyMap getMap(final boolean strict) {
 150         if (strict) {
 151             synchronized (ScriptFunctionImpl.class) {
 152                 if (strictmodemap$ == null) {
 153                     // In strict mode, the following properties should throw TypeError
 154                     strictmodemap$ = nasgenmap$;
 155                     strictmodemap$ = newThrowerProperty(strictmodemap$, "arguments", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
 156                     strictmodemap$ = newThrowerProperty(strictmodemap$, "caller",    Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE);
 157                 }
 158             }
 159             return strictmodemap$;
 160         }
 161 
 162         return nasgenmap$;
 163     }
 164 
 165     // Instance of this class is used as global anonymous function which
 166     // serves as Function.prototype object.
 167     private static class AnonymousFunction extends ScriptFunctionImpl {
 168         private static final PropertyMap nasgenmap$$ = PropertyMap.newMap(AnonymousFunction.class);
 169 
 170         AnonymousFunction() {
 171             super("", GlobalFunctions.ANONYMOUS, nasgenmap$$, null);
 172         }
 173     }
 174 
 175     static ScriptFunctionImpl newAnonymousFunction() {
 176         return new AnonymousFunction();
 177     }
 178 




















 179     /**
 180      * Factory method for non-constructor built-in functions
 181      *
 182      * @param name   function name
 183      * @param methodHandle handle for invocation
 184      * @param specs  specialized versions of function if available, null otherwise
 185      * @param strict are we in strict mode
 186      * @return new ScriptFunction
 187      */
 188     public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final boolean strict) {
 189         final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, strict, true);


 190         func.setConstructHandle(null);
 191         func.setPrototype(UNDEFINED);
 192 
 193         return func;
 194     }
 195 
 196     /**
 197      * Factory method for non-constructor built-in functions
 198      *
 199      * @param name   function name
 200      * @param methodHandle handle for invocation
 201      * @param specs  specialized versions of function if available, null otherwise
 202      * @return new ScriptFunction
 203      */
 204     public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
 205         return makeFunction(name, methodHandle, specs, false);
 206     }
 207 
 208     /**
 209      * Factory method for non-constructor built-in functions
 210      *
 211      * @param name   function name
 212      * @param methodHandle handle for invocation
 213      * @return new ScriptFunction
 214      */
 215     public static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) {
 216         return makeFunction(name, methodHandle, null);
 217     }
 218 
 219     /**
 220      * This method is used to create a bound function. See also
 221      * {@link NativeFunction#bind(Object, Object...)} method implementation.
 222      *
 223      * @param thiz this reference to bind
 224      * @param args arguments to bind
 225      */
 226     @Override
 227     public ScriptFunction makeBoundFunction(final Object thiz, final Object[] args) {
 228         Object[] allArgs = args;
 229 
 230         if (allArgs == null) {
 231             allArgs = ScriptRuntime.EMPTY_ARRAY;
 232         }
 233 
 234         final Object boundThiz = convertThisObject(thiz);
 235         final MethodHandle   boundMethod = MH.insertArguments(BOUND_FUNCTION, 0, this, boundThiz, allArgs);
 236         final ScriptFunction boundFunc   = makeFunction("", boundMethod, null, true);
 237 
 238         MethodHandle consHandle  = this.getConstructHandle();
 239 
 240         if (consHandle != null) {
 241             consHandle = MH.insertArguments(BOUND_CONSTRUCTOR, 0, this, allArgs);
 242         }
 243 
 244         boundFunc.setConstructHandle(consHandle);
 245         int newArity = this.getArity();
 246         if (newArity != -1) {
 247             newArity -= Math.min(newArity, allArgs.length);
 248         }
 249         boundFunc.setArity(newArity);
 250 
 251         return boundFunc;
 252     }
 253 
 254     @SuppressWarnings("unused")
 255     private static Object boundFunction(final ScriptFunction wrapped, final Object boundThiz, final Object[] boundArgs, final Object thiz, final Object[] args) {
 256         final Object[] allArgs = new Object[boundArgs.length + args.length];
 257 
 258         System.arraycopy(boundArgs, 0, allArgs, 0, boundArgs.length);
 259         System.arraycopy(args, 0, allArgs, boundArgs.length, args.length);
 260 
 261         return ScriptRuntime.apply(wrapped, boundThiz, allArgs);
 262     }
 263 
 264     @SuppressWarnings("unused")
 265     private static Object boundConstructor(final ScriptFunction wrapped, final Object[] boundArgs, final Object thiz, final Object[] args) {
 266         final Object[] allArgs = new Object[boundArgs.length + args.length];
 267         System.arraycopy(boundArgs, 0, allArgs, 0, boundArgs.length);
 268         System.arraycopy(args, 0, allArgs, boundArgs.length, args.length);
 269 
 270         return ScriptRuntime.construct(wrapped, allArgs);
 271     }
 272 
 273     // return Object.prototype - used by "allocate"
 274     @Override
 275     protected final ScriptObject getObjectPrototype() {
 276         return Global.objectPrototype();
 277     }
 278 
 279     // Internals below..
 280     private void init() {
 281         this.setProto(Global.instance().getFunctionPrototype());
 282         this.setPrototype(new PrototypeObject(this));
 283 
 284         if (isStrict()) {
 285             final ScriptFunction func = getTypeErrorThrower();
 286             // We have to fill user accessor functions late as these are stored
 287             // in this object rather than in the PropertyMap of this object.
 288             setUserAccessors("arguments", func, func);
 289             setUserAccessors("caller", func, func);
 290         }
 291     }
 292 
 293     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
 294         try {
 295             return MethodHandles.lookup().findStatic(ScriptFunctionImpl.class, name, MH.type(rtype, types));
 296         } catch (final NoSuchMethodException | IllegalAccessException e) {
 297             throw new MethodHandleFactory.LookupException(e);
 298         }
 299     }
 300 }
--- EOF ---