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 }