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.api.scripting.ScriptObjectMirror;
  33 import jdk.nashorn.internal.objects.annotations.Attribute;
  34 import jdk.nashorn.internal.objects.annotations.Constructor;
  35 import jdk.nashorn.internal.objects.annotations.Function;
  36 import jdk.nashorn.internal.objects.annotations.ScriptClass;
  37 import jdk.nashorn.internal.parser.Parser;
  38 import jdk.nashorn.internal.runtime.Context;
  39 import jdk.nashorn.internal.runtime.JSType;
  40 import jdk.nashorn.internal.runtime.ParserException;
  41 import jdk.nashorn.internal.runtime.ScriptFunction;
  42 import jdk.nashorn.internal.runtime.ScriptObject;
  43 import jdk.nashorn.internal.runtime.ScriptRuntime;
  44 import jdk.nashorn.internal.runtime.Source;
  45 
  46 /**
  47  * ECMA 15.3 Function Objects
  48  *
  49  * Note: instances of this class are never created. This class is not even a
  50  * subclass of ScriptObject. But, we use this class to generate prototype and
  51  * constructor for "Function".
  52  */
  53 @ScriptClass("Function")
  54 public final class NativeFunction {
  55     // do *not* create me!
  56     private NativeFunction() {
  57     }
  58 
  59     /**
  60      * ECMA 15.3.4.2 Function.prototype.toString ( )
  61      *
  62      * @param self self reference
  63      * @return string representation of Function
  64      */
  65     @Function(attributes = Attribute.NOT_ENUMERABLE)
  66     public static Object toString(final Object self) {
  67         if (!(self instanceof ScriptFunction)) {
  68             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
  69         }
  70         return ((ScriptFunction)self).toSource();
  71     }
  72 
  73     /**
  74      * ECMA 15.3.4.3 Function.prototype.apply (thisArg, argArray)
  75      *
  76      * @param self   self reference
  77      * @param thiz   {@code this} arg for apply
  78      * @param array  array of argument for apply
  79      * @return result of apply
  80      */
  81     @Function(attributes = Attribute.NOT_ENUMERABLE)
  82     public static Object apply(final Object self, final Object thiz, final Object array) {
  83         if (!(self instanceof ScriptFunction)) {
  84             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
  85         }
  86 
  87         Object[] args = null;
  88 
  89         if (array instanceof ScriptObject) {
  90             // look for array-like object
  91             final ScriptObject sobj = (ScriptObject)array;
  92             final Object       len  = sobj.getLength();
  93             final int n = (int)JSType.toUint32(len);
  94 
  95             args = new Object[n];
  96             for (int i = 0; i < args.length; i++) {
  97                 args[i] = sobj.get(i);
  98             }
  99         } else if (array instanceof Object[]) {
 100             args = (Object[])array;
 101         } else if (array instanceof List) {
 102             final List<?> list = (List<?>)array;
 103             list.toArray(args = new Object[list.size()]);
 104         } else if (array == null || array == UNDEFINED) {
 105             args = ScriptRuntime.EMPTY_ARRAY;
 106         } else if (array instanceof ScriptObjectMirror) {
 107             // look for array-like ScriptObjectMirror object
 108             final ScriptObjectMirror mirror = (ScriptObjectMirror)array;
 109             final Object       len  = mirror.containsKey("length")? mirror.getMember("length") : Integer.valueOf(0);
 110             final int n = (int)JSType.toUint32(len);
 111 
 112             args = new Object[n];
 113             for (int i = 0; i < args.length; i++) {
 114                 args[i] = mirror.containsKey(i)? mirror.getSlot(i) : UNDEFINED;
 115             }
 116         } else {
 117             throw typeError("function.apply.expects.array");
 118         }
 119 
 120         return ScriptRuntime.apply((ScriptFunction)self, thiz, args);
 121     }
 122 
 123     /**
 124      * ECMA 15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] )
 125      *
 126      * @param self self reference
 127      * @param args arguments for call
 128      * @return result of call
 129      */
 130     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 131     public static Object call(final Object self, final Object... args) {
 132         if (!(self instanceof ScriptFunction)) {
 133             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
 134         }
 135 
 136         Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 137         Object[] arguments;
 138 
 139         if (args.length > 1) {
 140             arguments = new Object[args.length - 1];
 141             System.arraycopy(args, 1, arguments, 0, arguments.length);
 142         } else {
 143             arguments = ScriptRuntime.EMPTY_ARRAY;
 144         }
 145 
 146         return ScriptRuntime.apply((ScriptFunction)self, thiz, arguments);
 147     }
 148 
 149     /**
 150      * ECMA 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]])
 151      *
 152      * @param self self reference
 153      * @param args arguments for bind
 154      * @return function with bound arguments
 155      */
 156     @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
 157     public static Object bind(final Object self, final Object... args) {
 158         if (!(self instanceof ScriptFunction)) {
 159             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
 160         }
 161 
 162         final Object thiz = (args.length == 0) ? UNDEFINED : args[0];
 163 
 164         Object[] arguments;
 165         if (args.length > 1) {
 166             arguments = new Object[args.length - 1];
 167             System.arraycopy(args, 1, arguments, 0, arguments.length);
 168         } else {
 169             arguments = ScriptRuntime.EMPTY_ARRAY;
 170         }
 171 
 172         return ((ScriptFunctionImpl)self).makeBoundFunction(thiz, arguments);
 173     }
 174 
 175     /**
 176      * Nashorn extension: Function.prototype.toSource
 177      *
 178      * @param self self reference
 179      * @return source for function
 180      */
 181     @Function(attributes = Attribute.NOT_ENUMERABLE)
 182     public static Object toSource(final Object self) {
 183         if (!(self instanceof ScriptFunction)) {
 184             throw typeError("not.a.function", ScriptRuntime.safeToString(self));
 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             final StringBuilder paramListBuf = new StringBuilder();
 206             for (int i = 0; i < args.length - 1; i++) {
 207                 paramListBuf.append(JSType.toString(args[i]));
 208                 if (i < args.length - 2) {
 209                     paramListBuf.append(",");
 210                 }
 211             }
 212 
 213             final String paramList = paramListBuf.toString();
 214             if (! paramList.isEmpty()) {
 215                 checkFunctionParameters(paramList);
 216                 sb.append(paramList);
 217             }
 218         }
 219         sb.append(") {\n");
 220         if (args.length > 0) {
 221             final String funcBody = JSType.toString(args[args.length - 1]);
 222             checkFunctionBody(funcBody);
 223             sb.append(funcBody);
 224             sb.append('\n');
 225         }
 226         sb.append("})");
 227 
 228         final Global global = Global.instance();
 229 
 230         return Global.directEval(global, sb.toString(), global, "<function>", global.isStrictContext());
 231     }
 232 
 233     private static void checkFunctionParameters(final String params) {
 234         final Source src = new Source("<function>", params);
 235         final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
 236         try {
 237             parser.parseFormalParameterList();
 238         } catch (final ParserException pe) {
 239             pe.throwAsEcmaException();
 240         }
 241     }
 242 
 243     private static void checkFunctionBody(final String funcBody) {
 244         final Source src = new Source("<function>", funcBody);
 245         final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager());
 246         try {
 247             parser.parseFunctionBody();
 248         } catch (final ParserException pe) {
 249             pe.throwAsEcmaException();
 250         }
 251     }
 252 }
--- EOF ---