nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
changeset 32560 351e2f1e9f17
parent 32559 8a1504b43ad8
parent 32550 6521875cb63e
child 32561 b80d31bbf0bf
equal deleted inserted replaced
32559:8a1504b43ad8 32560:351e2f1e9f17
     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.lookup.Lookup.MH;
       
    29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
       
    30 
       
    31 import java.lang.invoke.MethodHandle;
       
    32 import java.util.ArrayList;
       
    33 import jdk.nashorn.internal.runtime.AccessorProperty;
       
    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.RecompilableScriptFunctionData;
       
    38 import jdk.nashorn.internal.runtime.ScriptFunction;
       
    39 import jdk.nashorn.internal.runtime.ScriptFunctionData;
       
    40 import jdk.nashorn.internal.runtime.ScriptObject;
       
    41 import jdk.nashorn.internal.runtime.Specialization;
       
    42 
       
    43 /**
       
    44  * Concrete implementation of ScriptFunction. This sets correct map for the
       
    45  * function objects -- to expose properties like "prototype", "length" etc.
       
    46  */
       
    47 public class ScriptFunctionImpl extends ScriptFunction {
       
    48 
       
    49     /** Reference to constructor prototype. */
       
    50     private Object prototype;
       
    51 
       
    52     // property map for strict mode functions
       
    53     private static final PropertyMap strictmodemap$;
       
    54     // property map for bound functions
       
    55     private static final PropertyMap boundfunctionmap$;
       
    56     // property map for non-strict, non-bound functions.
       
    57     private static final PropertyMap map$;
       
    58 
       
    59     // Marker object for lazily initialized prototype object
       
    60     private static final Object LAZY_PROTOTYPE = new Object();
       
    61 
       
    62     private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs, final Global global) {
       
    63         super(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
       
    64         init(global);
       
    65     }
       
    66 
       
    67     /**
       
    68      * Constructor called by Nasgen generated code, no membercount, use the default map.
       
    69      * Creates builtin functions only.
       
    70      *
       
    71      * @param name name of function
       
    72      * @param invokeHandle handle for invocation
       
    73      * @param specs specialized versions of this method, if available, null otherwise
       
    74      */
       
    75     ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs) {
       
    76         this(name, invokeHandle, specs, Global.instance());
       
    77     }
       
    78 
       
    79     private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs, final Global global) {
       
    80         super(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
       
    81         init(global);
       
    82     }
       
    83 
       
    84     /**
       
    85      * Constructor called by Nasgen generated code, no membercount, use the map passed as argument.
       
    86      * Creates builtin functions only.
       
    87      *
       
    88      * @param name name of function
       
    89      * @param invokeHandle handle for invocation
       
    90      * @param map initial property map
       
    91      * @param specs specialized versions of this method, if available, null otherwise
       
    92      */
       
    93     ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) {
       
    94         this(name, invokeHandle, map, specs, Global.instance());
       
    95     }
       
    96 
       
    97     private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags, final Global global) {
       
    98         super(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags);
       
    99         init(global);
       
   100     }
       
   101 
       
   102     /**
       
   103      * Constructor called by Global.newScriptFunction (runtime).
       
   104      *
       
   105      * @param name name of function
       
   106      * @param methodHandle handle for invocation
       
   107      * @param scope scope object
       
   108      * @param specs specialized versions of this method, if available, null otherwise
       
   109      * @param flags {@link ScriptFunctionData} flags
       
   110      */
       
   111     ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags) {
       
   112         this(name, methodHandle, scope, specs, flags, Global.instance());
       
   113     }
       
   114 
       
   115     private ScriptFunctionImpl(final RecompilableScriptFunctionData data, final ScriptObject scope, final Global global) {
       
   116         super(data, getMap(data.isStrict()), scope);
       
   117         init(global);
       
   118     }
       
   119 
       
   120     /**
       
   121      * Factory method called by compiler generated code for functions that need parent scope.
       
   122      *
       
   123      * @param constants the generated class' constant array
       
   124      * @param index the index of the {@code RecompilableScriptFunctionData} object in the constants array.
       
   125      * @param scope the parent scope object
       
   126      * @return a newly created function object
       
   127      */
       
   128     public static ScriptFunction create(final Object[] constants, final int index, final ScriptObject scope) {
       
   129         return new ScriptFunctionImpl((RecompilableScriptFunctionData)constants[index], scope, Global.instance());
       
   130     }
       
   131 
       
   132     /**
       
   133      * Factory method called by compiler generated code for functions that don't need parent scope.
       
   134      *
       
   135      * @param constants the generated class' constant array
       
   136      * @param index the index of the {@code RecompilableScriptFunctionData} object in the constants array.
       
   137      * @return a newly created function object
       
   138      */
       
   139     public static ScriptFunction create(final Object[] constants, final int index) {
       
   140         return create(constants, index, null);
       
   141     }
       
   142 
       
   143     /**
       
   144      * Only invoked internally from {@link BoundScriptFunctionImpl} constructor.
       
   145      * @param data the script function data for the bound function.
       
   146      * @param global the global object
       
   147      */
       
   148     ScriptFunctionImpl(final ScriptFunctionData data, final Global global) {
       
   149         super(data, boundfunctionmap$, null);
       
   150         init(global);
       
   151     }
       
   152 
       
   153     static {
       
   154         final ArrayList<Property> properties = new ArrayList<>(3);
       
   155         properties.add(AccessorProperty.create("prototype", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE, G$PROTOTYPE, S$PROTOTYPE));
       
   156         properties.add(AccessorProperty.create("length",  Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$LENGTH, null));
       
   157         properties.add(AccessorProperty.create("name", Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE | Property.NOT_WRITABLE, G$NAME, null));
       
   158         map$ = PropertyMap.newMap(properties);
       
   159         strictmodemap$ = createStrictModeMap(map$);
       
   160         boundfunctionmap$ = createBoundFunctionMap(strictmodemap$);
       
   161     }
       
   162 
       
   163     private static PropertyMap createStrictModeMap(final PropertyMap map) {
       
   164         final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE;
       
   165         PropertyMap newMap = map;
       
   166         // Need to add properties directly to map since slots are assigned speculatively by newUserAccessors.
       
   167         newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags));
       
   168         newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags));
       
   169         return newMap;
       
   170     }
       
   171 
       
   172     private static boolean isStrict(final int flags) {
       
   173         return (flags & ScriptFunctionData.IS_STRICT) != 0;
       
   174     }
       
   175 
       
   176     // Choose the map based on strict mode!
       
   177     private static PropertyMap getMap(final boolean strict) {
       
   178         return strict ? strictmodemap$ : map$;
       
   179     }
       
   180 
       
   181     private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) {
       
   182         // Bound function map is same as strict function map, but additionally lacks the "prototype" property, see
       
   183         // ECMAScript 5.1 section 15.3.4.5
       
   184         return strictModeMap.deleteProperty(strictModeMap.findProperty("prototype"));
       
   185     }
       
   186 
       
   187     // Instance of this class is used as global anonymous function which
       
   188     // serves as Function.prototype object.
       
   189     private static class AnonymousFunction extends ScriptFunctionImpl {
       
   190         private static final PropertyMap anonmap$ = PropertyMap.newMap();
       
   191 
       
   192         AnonymousFunction() {
       
   193             super("", GlobalFunctions.ANONYMOUS, anonmap$, null);
       
   194         }
       
   195     }
       
   196 
       
   197     static ScriptFunctionImpl newAnonymousFunction() {
       
   198         return new AnonymousFunction();
       
   199     }
       
   200 
       
   201     private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) {
       
   202         final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, flags);
       
   203         func.setPrototype(UNDEFINED);
       
   204         // Non-constructor built-in functions do not have "prototype" property
       
   205         func.deleteOwnProperty(func.getMap().findProperty("prototype"));
       
   206 
       
   207         return func;
       
   208     }
       
   209 
       
   210     /**
       
   211      * Factory method for non-constructor built-in functions
       
   212      *
       
   213      * @param name   function name
       
   214      * @param methodHandle handle for invocation
       
   215      * @param specs  specialized versions of function if available, null otherwise
       
   216      * @return new ScriptFunction
       
   217      */
       
   218     static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs) {
       
   219         return makeFunction(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN);
       
   220     }
       
   221 
       
   222     /**
       
   223      * Factory method for non-constructor built-in, strict functions
       
   224      *
       
   225      * @param name   function name
       
   226      * @param methodHandle handle for invocation
       
   227      * @return new ScriptFunction
       
   228      */
       
   229     static ScriptFunction makeStrictFunction(final String name, final MethodHandle methodHandle) {
       
   230         return makeFunction(name, methodHandle, null, ScriptFunctionData.IS_BUILTIN | ScriptFunctionData.IS_STRICT );
       
   231     }
       
   232 
       
   233     /**
       
   234      * Factory method for non-constructor built-in functions
       
   235      *
       
   236      * @param name   function name
       
   237      * @param methodHandle handle for invocation
       
   238      * @return new ScriptFunction
       
   239      */
       
   240     static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle) {
       
   241         return makeFunction(name, methodHandle, null);
       
   242     }
       
   243 
       
   244     @Override
       
   245     public ScriptFunction makeSynchronizedFunction(final Object sync) {
       
   246         final MethodHandle mh = MH.insertArguments(ScriptFunction.INVOKE_SYNC, 0, this, sync);
       
   247         return makeFunction(getName(), mh);
       
   248     }
       
   249 
       
   250     /**
       
   251      * Same as {@link ScriptFunction#makeBoundFunction(Object, Object[])}. The only reason we override it is so that we
       
   252      * can expose it.
       
   253      * @param self the self to bind to this function. Can be null (in which case, null is bound as this).
       
   254      * @param args additional arguments to bind to this function. Can be null or empty to not bind additional arguments.
       
   255      * @return a function with the specified self and parameters bound.
       
   256      */
       
   257     @Override
       
   258     public ScriptFunction makeBoundFunction(final Object self, final Object[] args) {
       
   259         return super.makeBoundFunction(self, args);
       
   260     }
       
   261 
       
   262     /**
       
   263      * This method is used to create a bound function based on this function.
       
   264      *
       
   265      * @param data the {@code ScriptFunctionData} specifying the functions immutable portion.
       
   266      * @return a function initialized from the specified data. Its parent scope will be set to null, therefore the
       
   267      * passed in data should not expect a callee.
       
   268      */
       
   269     @Override
       
   270     protected ScriptFunction makeBoundFunction(final ScriptFunctionData data) {
       
   271         return new BoundScriptFunctionImpl(data, getTargetFunction());
       
   272     }
       
   273 
       
   274     // return Object.prototype - used by "allocate"
       
   275     @Override
       
   276     protected final ScriptObject getObjectPrototype() {
       
   277         return Global.objectPrototype();
       
   278     }
       
   279 
       
   280     @Override
       
   281     public final Object getPrototype() {
       
   282         if (prototype == LAZY_PROTOTYPE) {
       
   283             prototype = new PrototypeObject(this);
       
   284         }
       
   285         return prototype;
       
   286     }
       
   287 
       
   288     @Override
       
   289     public final void setPrototype(final Object newProto) {
       
   290         if (newProto instanceof ScriptObject && newProto != this.prototype && allocatorMap != null) {
       
   291             // Replace our current allocator map with one that is associated with the new prototype.
       
   292             allocatorMap = allocatorMap.changeProto((ScriptObject)newProto);
       
   293         }
       
   294         this.prototype = newProto;
       
   295     }
       
   296 
       
   297     // Internals below..
       
   298     private void init(final Global global) {
       
   299         this.setInitialProto(global.getFunctionPrototype());
       
   300         this.prototype = LAZY_PROTOTYPE;
       
   301 
       
   302         // We have to fill user accessor functions late as these are stored
       
   303         // in this object rather than in the PropertyMap of this object.
       
   304         assert objectSpill == null;
       
   305         final ScriptFunction typeErrorThrower = global.getTypeErrorThrower();
       
   306         if (findProperty("arguments", true) != null) {
       
   307             initUserAccessors("arguments", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower);
       
   308         }
       
   309         if (findProperty("caller", true) != null) {
       
   310             initUserAccessors("caller", Property.NOT_CONFIGURABLE | Property.NOT_ENUMERABLE, typeErrorThrower, typeErrorThrower);
       
   311        }
       
   312     }
       
   313 }