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.runtime.JSType;
  37 import jdk.nashorn.internal.runtime.ScriptFunction;
  38 import jdk.nashorn.internal.runtime.ScriptObject;
  39 import jdk.nashorn.internal.runtime.ScriptRuntime;
  40 
  41 /**
  42  * ECMA 15.3 Function Objects
  43  *
  44  * Note: instances of this class are never created. This class is not even a
  45  * subclass of ScriptObject. But, we use this class to generate prototype and
  46  * constructor for "Function".
  47  */
  48 @ScriptClass("Function")
  49 public final class NativeFunction {
  50     // do *not* create me!
  51     private NativeFunction() {
  52     }
  53 
  54     /**
  55      * ECMA 15.3.4.2 Function.prototype.toString ( )
  56      *
  57      * @param self self reference
  58      * @return string representation of Function
  59      */
  60     @Function(attributes = Attribute.NOT_ENUMERABLE)
  61     public static Object toString(final Object self) {
  62         if (!(self instanceof ScriptFunction)) {
  63             typeError("not.a.function", ScriptRuntime.safeToString(self));
  64             return UNDEFINED;
  65         }
  66         return ((ScriptFunction)self).toSource();
  67     }
  68 
  69     /**
  70      * ECMA 15.3.4.3 Function.prototype.apply (thisArg, argArray)
  71      *
  72      * @param self   self reference
  73      * @param thiz   {@code this} arg for apply
  74      * @param array  array of argument for apply
  75      * @return result of apply
  76      */
  77     @Function(attributes = Attribute.NOT_ENUMERABLE)
  78     public static Object apply(final Object self, final Object thiz, final Object array) {
  79         if (!(self instanceof ScriptFunction)) {
  80             typeError("not.a.function", ScriptRuntime.safeToString(self));
  81             return UNDEFINED;
  82         }
  83 
  84         Object[] args = null;
  85 
  86         if (ScriptObject.isArray(array)) {
  87             args = ((NativeArray)array).asObjectArray();
  88         } else if (array instanceof ScriptObject) {
  89             // look for array-like object
  90             final ScriptObject sobj = (ScriptObject)array;
  91             final Object       len  = sobj.getLength();
  92 
  93             if (len == UNDEFINED || len == null) {
  94                 typeError("function.apply.expects.array");
  95             }
  96 
  97             final int n = (int)JSType.toUint32(len);
  98             if (n != JSType.toNumber(len)) {
  99                 typeError("function.apply.expects.array");
 100             }
 101 
 102             args = new Object[(int)JSType.toUint32(len)];
 103             for (int i = 0; i < args.length; i++) {
 104                 args[i] = sobj.get(i);
 105             }
 106         } else if (array instanceof Object[]) {
 107             args = (Object[])array;
 108         } else if (array instanceof List) {
 109             final List<?> list = (List<?>)array;
 110             list.toArray(args = new Object[list.size()]);
 111         } else if (array == null || array == UNDEFINED) {
 112             args = ScriptRuntime.EMPTY_ARRAY;
 113         } else {
 114             typeError("function.apply.expects.array");
 115         }
 116 
 117         return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
 118     }
 119 
 120     /**
 121      * ECMA 15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] )
 122      *
 123      * @param self self reference
 124      * @param args arguments for call
 125      * @return result of call
 126      */
 127     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 128     public static Object call(final Object self, final Object... args) {
 129         if (!(self instanceof ScriptFunction)) {
 130             typeError("not.a.function", ScriptRuntime.safeToString(self));
 131             return UNDEFINED;
 132         }
 133 
 134         Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 135         Object[] arguments;
 136 
 137         if (args.length > 1) {
 138             arguments = new Object[args.length - 1];
 139             System.arraycopy(args, 1, arguments, 0, arguments.length);
 140         } else {
 141             arguments = ScriptRuntime.EMPTY_ARRAY;
 142         }
 143 
 144         return ScriptRuntime.apply((ScriptFunction)self, thiz, arguments);
 145     }
 146 
 147     /**
 148      * ECMA 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
 149      *
 150      * @param self self reference
 151      * @param args arguments for bind
 152      * @return function with bound arguments
 153      */
 154     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 155     public static Object bind(final Object self, final Object... args) {
 156         if (!(self instanceof ScriptFunction)) {
 157             typeError("not.a.function", ScriptRuntime.safeToString(self));
 158             return UNDEFINED;
 159         }
 160 
 161         final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 162 
 163         Object[] arguments;
 164         if (args.length > 1) {
 165             arguments = new Object[args.length - 1];
 166             System.arraycopy(args, 1, arguments, 0, arguments.length);
 167         } else {
 168             arguments = ScriptRuntime.EMPTY_ARRAY;
 169         }
 170 
 171         return ((ScriptFunction)self).makeBoundFunction(thiz, arguments);
 172     }
 173 
 174     /**
 175      * Nashorn extension: Function.prototype.toSource
 176      *
 177      * @param self self reference
 178      * @return source for function
 179      */
 180     @Function(attributes = Attribute.NOT_ENUMERABLE)
 181     public static Object toSource(final Object self) {
 182         if (!(self instanceof ScriptFunction)) {
 183             typeError("not.a.function", ScriptRuntime.safeToString(self));
 184             return UNDEFINED;
 185         }
 186         return ((ScriptFunction)self).toSource();
 187     }
 188 
 189     /**
 190      * ECMA 15.3.2.1 new Function (p1, p2, ... , pn, body)
 191      *
 192      * Constructor
 193      *
 194      * @param newObj is the new operator used for constructing this function
 195      * @param self   self reference
 196      * @param args   arguments
 197      * @return new NativeFunction
 198      */
 199     @Constructor(arity = 1)
 200     public static Object function(final boolean newObj, final Object self, final Object... args) {
 201         final StringBuilder sb = new StringBuilder();
 202 
 203         sb.append("(function (");
 204         if (args.length > 0) {
 205             for (int i = 0; i < args.length - 1; i++) {
 206                 sb.append(JSType.toString(args[i]));
 207                 if (i < args.length - 2) {
 208                     sb.append(",");
 209                 }
 210             }
 211         }
 212         sb.append(") {\n");
 213         if (args.length > 0) {
 214             sb.append(JSType.toString(args[args.length - 1]));
 215             sb.append('\n');
 216         }
 217         sb.append("})");
 218 
 219         final Global global = Global.instance();
 220 
 221         return Global.directEval(global, sb.toString(), global, "<function>", Global.isStrict());
 222     }
 223 }