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.ECMAErrors.typeError;
  29 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
  30 
  31 import java.util.List;

  32 import jdk.nashorn.internal.objects.annotations.Attribute;
  33 import jdk.nashorn.internal.objects.annotations.Constructor;
  34 import jdk.nashorn.internal.objects.annotations.Function;
  35 import jdk.nashorn.internal.objects.annotations.ScriptClass;
  36 import jdk.nashorn.internal.parser.Parser;
  37 import jdk.nashorn.internal.runtime.Context;
  38 import jdk.nashorn.internal.runtime.JSType;
  39 import jdk.nashorn.internal.runtime.ParserException;
  40 import jdk.nashorn.internal.runtime.ScriptFunction;
  41 import jdk.nashorn.internal.runtime.ScriptObject;
  42 import jdk.nashorn.internal.runtime.ScriptRuntime;
  43 import jdk.nashorn.internal.runtime.Source;
  44 
  45 /**
  46  * ECMA 15.3 Function Objects
  47  *
  48  * Note: instances of this class are never created. This class is not even a
  49  * subclass of ScriptObject. But, we use this class to generate prototype and
  50  * constructor for "Function".
  51  */
  52 @ScriptClass("Function")
  53 public final class NativeFunction {
  54     // do *not* create me!
  55     private NativeFunction() {
  56     }
  57 
  58     /**
  59      * ECMA 15.3.4.2 Function.prototype.toString ( )
  60      *
  61      * @param self self reference
  62      * @return string representation of Function
  63      */
  64     @Function(attributes = Attribute.NOT_ENUMERABLE)
  65     public static Object toString(final Object self) {
  66         if (!(self instanceof ScriptFunction)) {
  67             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
  68         }
  69         return ((ScriptFunction)self).toSource();
  70     }
  71 
  72     /**
  73      * ECMA 15.3.4.3 Function.prototype.apply (thisArg, argArray)
  74      *
  75      * @param self   self reference
  76      * @param thiz   {@code this} arg for apply
  77      * @param array  array of argument for apply
  78      * @return result of apply
  79      */
  80     @Function(attributes = Attribute.NOT_ENUMERABLE)
  81     public static Object apply(final Object self, final Object thiz, final Object array) {
  82         if (!(self instanceof ScriptFunction)) {
  83             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
  84         }
  85 
  86         Object[] args = null;
  87 
  88         if (array instanceof ScriptObject) {
  89             // look for array-like object
  90             final ScriptObject sobj = (ScriptObject)array;
  91             final Object       len  = sobj.getLength();
  92             final int n = (int)JSType.toUint32(len);
  93 
  94             args = new Object[n];
  95             for (int i = 0; i < args.length; i++) {
  96                 args[i] = sobj.get(i);
  97             }
  98         } else if (array instanceof Object[]) {
  99             args = (Object[])array;
 100         } else if (array instanceof List) {
 101             final List<?> list = (List<?>)array;
 102             list.toArray(args = new Object[list.size()]);
 103         } else if (array == null || array == UNDEFINED) {
 104             args = ScriptRuntime.EMPTY_ARRAY;










 105         } else {
 106             throw typeError("function.apply.expects.array");
 107         }
 108 
 109         return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
 110     }
 111 
 112     /**
 113      * ECMA 15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] )
 114      *
 115      * @param self self reference
 116      * @param args arguments for call
 117      * @return result of call
 118      */
 119     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 120     public static Object call(final Object self, final Object... args) {
 121         if (!(self instanceof ScriptFunction)) {
 122             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
 123         }
 124 
 125         Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 126         Object[] arguments;
 127 
 128         if (args.length > 1) {
 129             arguments = new Object[args.length - 1];
 130             System.arraycopy(args, 1, arguments, 0, arguments.length);
 131         } else {
 132             arguments = ScriptRuntime.EMPTY_ARRAY;
 133         }
 134 
 135         return ScriptRuntime.apply((ScriptFunction)self, thiz, arguments);
 136     }
 137 
 138     /**
 139      * ECMA 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
 140      *
 141      * @param self self reference
 142      * @param args arguments for bind
 143      * @return function with bound arguments
 144      */
 145     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 146     public static Object bind(final Object self, final Object... args) {
 147         if (!(self instanceof ScriptFunction)) {
 148             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
 149         }
 150 
 151         final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 152 
 153         Object[] arguments;
 154         if (args.length > 1) {
 155             arguments = new Object[args.length - 1];
 156             System.arraycopy(args, 1, arguments, 0, arguments.length);
 157         } else {
 158             arguments = ScriptRuntime.EMPTY_ARRAY;
 159         }
 160 
 161         return ((ScriptFunctionImpl)self).makeBoundFunction(thiz, arguments);
 162     }
 163 
 164     /**
 165      * Nashorn extension: Function.prototype.toSource
 166      *
 167      * @param self self reference
 168      * @return source for function
 169      */
 170     @Function(attributes = Attribute.NOT_ENUMERABLE)
 171     public static Object toSource(final Object self) {
 172         if (!(self instanceof ScriptFunction)) {
 173             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
 174         }
 175         return ((ScriptFunction)self).toSource();
 176     }
 177 
 178     /**
 179      * ECMA 15.3.2.1 new Function (p1, p2, ... , pn, body)
 180      *
 181      * Constructor
 182      *
 183      * @param newObj is the new operator used for constructing this function
 184      * @param self   self reference
 185      * @param args   arguments
 186      * @return new NativeFunction
 187      */
 188     @Constructor(arity = 1)
 189     public static Object function(final boolean newObj, final Object self, final Object... args) {
 190         final StringBuilder sb = new StringBuilder();
 191 
 192         sb.append("(function (");
 193         if (args.length > 0) {
 194             final StringBuilder paramListBuf = new StringBuilder();
 195             for (int i = 0; i < args.length - 1; i++) {
 196                 paramListBuf.append(JSType.toString(args[i]));
 197                 if (i < args.length - 2) {
 198                     paramListBuf.append(",");
 199                 }
 200             }
 201 
 202             final String paramList = paramListBuf.toString();
 203             if (! paramList.isEmpty()) {
 204                 checkFunctionParameters(paramList);
 205                 sb.append(paramList);
 206             }
 207         }
 208         sb.append(") {\n");
 209         if (args.length > 0) {
 210             final String funcBody = JSType.toString(args[args.length - 1]);
 211             checkFunctionBody(funcBody);
 212             sb.append(funcBody);
 213             sb.append('\n');
 214         }
 215         sb.append("})");
 216 
 217         final Global global = Global.instance();
 218 
 219         return Global.directEval(global, sb.toString(), global, "<function>", global.isStrictContext());
 220     }
 221 
 222     private static void checkFunctionParameters(final String params) {
 223         final Source src = new Source("<function>", params);
 224         final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
 225         try {
 226             parser.parseFormalParameterList();
 227         } catch (final ParserException pe) {
 228             pe.throwAsEcmaException();
 229         }
 230     }
 231 
 232     private static void checkFunctionBody(final String funcBody) {
 233         final Source src = new Source("<function>", funcBody);
 234         final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
 235         try {
 236             parser.parseFunctionBody();
 237         } catch (final ParserException pe) {
 238             pe.throwAsEcmaException();
 239         }
 240     }
 241 }
--- EOF ---