Wed, 03 Jul 2013 13:03:36 +0200
8019585: Sometimes a var declaration using itself in its init wasn't declared as canBeUndefined, causing erroneous bytecode
Reviewed-by: sundar, attila
jlaskey@3 | 1 | /* |
jlaskey@7 | 2 | * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
jlaskey@3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
jlaskey@3 | 4 | * |
jlaskey@3 | 5 | * This code is free software; you can redistribute it and/or modify it |
jlaskey@3 | 6 | * under the terms of the GNU General Public License version 2 only, as |
jlaskey@3 | 7 | * published by the Free Software Foundation. Oracle designates this |
jlaskey@3 | 8 | * particular file as subject to the "Classpath" exception as provided |
jlaskey@3 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
jlaskey@3 | 10 | * |
jlaskey@3 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
jlaskey@3 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
jlaskey@3 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
jlaskey@3 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
jlaskey@3 | 15 | * accompanied this code). |
jlaskey@3 | 16 | * |
jlaskey@3 | 17 | * You should have received a copy of the GNU General Public License version |
jlaskey@3 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
jlaskey@3 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
jlaskey@3 | 20 | * |
jlaskey@3 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
jlaskey@3 | 22 | * or visit www.oracle.com if you need additional information or have any |
jlaskey@3 | 23 | * questions. |
jlaskey@3 | 24 | */ |
jlaskey@3 | 25 | |
jlaskey@3 | 26 | package jdk.nashorn.internal.objects; |
jlaskey@3 | 27 | |
jlaskey@3 | 28 | import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
jlaskey@3 | 29 | import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; |
jlaskey@3 | 30 | |
jlaskey@3 | 31 | import java.util.List; |
lagergren@405 | 32 | |
sundar@350 | 33 | import jdk.nashorn.api.scripting.ScriptObjectMirror; |
jlaskey@3 | 34 | import jdk.nashorn.internal.objects.annotations.Attribute; |
jlaskey@3 | 35 | import jdk.nashorn.internal.objects.annotations.Constructor; |
jlaskey@3 | 36 | import jdk.nashorn.internal.objects.annotations.Function; |
jlaskey@3 | 37 | import jdk.nashorn.internal.objects.annotations.ScriptClass; |
sundar@316 | 38 | import jdk.nashorn.internal.parser.Parser; |
sundar@316 | 39 | import jdk.nashorn.internal.runtime.Context; |
jlaskey@3 | 40 | import jdk.nashorn.internal.runtime.JSType; |
sundar@316 | 41 | import jdk.nashorn.internal.runtime.ParserException; |
hannesw@380 | 42 | import jdk.nashorn.internal.runtime.PropertyMap; |
jlaskey@3 | 43 | import jdk.nashorn.internal.runtime.ScriptFunction; |
jlaskey@3 | 44 | import jdk.nashorn.internal.runtime.ScriptObject; |
jlaskey@3 | 45 | import jdk.nashorn.internal.runtime.ScriptRuntime; |
sundar@316 | 46 | import jdk.nashorn.internal.runtime.Source; |
jlaskey@3 | 47 | |
jlaskey@3 | 48 | /** |
jlaskey@3 | 49 | * ECMA 15.3 Function Objects |
jlaskey@3 | 50 | * |
jlaskey@3 | 51 | * Note: instances of this class are never created. This class is not even a |
jlaskey@3 | 52 | * subclass of ScriptObject. But, we use this class to generate prototype and |
jlaskey@3 | 53 | * constructor for "Function". |
jlaskey@3 | 54 | */ |
jlaskey@3 | 55 | @ScriptClass("Function") |
jlaskey@3 | 56 | public final class NativeFunction { |
hannesw@380 | 57 | |
hannesw@380 | 58 | // initialized by nasgen |
lagergren@405 | 59 | @SuppressWarnings("unused") |
hannesw@380 | 60 | private static PropertyMap $nasgenmap$; |
hannesw@380 | 61 | |
jlaskey@3 | 62 | // do *not* create me! |
jlaskey@3 | 63 | private NativeFunction() { |
jlaskey@3 | 64 | } |
jlaskey@3 | 65 | |
jlaskey@3 | 66 | /** |
jlaskey@3 | 67 | * ECMA 15.3.4.2 Function.prototype.toString ( ) |
jlaskey@3 | 68 | * |
jlaskey@3 | 69 | * @param self self reference |
jlaskey@3 | 70 | * @return string representation of Function |
jlaskey@3 | 71 | */ |
jlaskey@3 | 72 | @Function(attributes = Attribute.NOT_ENUMERABLE) |
jlaskey@3 | 73 | public static Object toString(final Object self) { |
jlaskey@3 | 74 | if (!(self instanceof ScriptFunction)) { |
lagergren@112 | 75 | throw typeError("not.a.function", ScriptRuntime.safeToString(self)); |
jlaskey@3 | 76 | } |
jlaskey@3 | 77 | return ((ScriptFunction)self).toSource(); |
jlaskey@3 | 78 | } |
jlaskey@3 | 79 | |
jlaskey@3 | 80 | /** |
jlaskey@3 | 81 | * ECMA 15.3.4.3 Function.prototype.apply (thisArg, argArray) |
jlaskey@3 | 82 | * |
jlaskey@3 | 83 | * @param self self reference |
jlaskey@3 | 84 | * @param thiz {@code this} arg for apply |
jlaskey@3 | 85 | * @param array array of argument for apply |
jlaskey@3 | 86 | * @return result of apply |
jlaskey@3 | 87 | */ |
jlaskey@3 | 88 | @Function(attributes = Attribute.NOT_ENUMERABLE) |
jlaskey@3 | 89 | public static Object apply(final Object self, final Object thiz, final Object array) { |
jlaskey@3 | 90 | if (!(self instanceof ScriptFunction)) { |
lagergren@112 | 91 | throw typeError("not.a.function", ScriptRuntime.safeToString(self)); |
jlaskey@3 | 92 | } |
jlaskey@3 | 93 | |
jlaskey@3 | 94 | Object[] args = null; |
jlaskey@3 | 95 | |
sundar@203 | 96 | if (array instanceof ScriptObject) { |
jlaskey@3 | 97 | // look for array-like object |
jlaskey@3 | 98 | final ScriptObject sobj = (ScriptObject)array; |
jlaskey@3 | 99 | final Object len = sobj.getLength(); |
sundar@203 | 100 | final int n = (int)JSType.toUint32(len); |
jlaskey@3 | 101 | |
sundar@203 | 102 | args = new Object[n]; |
jlaskey@3 | 103 | for (int i = 0; i < args.length; i++) { |
jlaskey@3 | 104 | args[i] = sobj.get(i); |
jlaskey@3 | 105 | } |
jlaskey@3 | 106 | } else if (array instanceof Object[]) { |
jlaskey@3 | 107 | args = (Object[])array; |
jlaskey@3 | 108 | } else if (array instanceof List) { |
jlaskey@3 | 109 | final List<?> list = (List<?>)array; |
jlaskey@3 | 110 | list.toArray(args = new Object[list.size()]); |
jlaskey@3 | 111 | } else if (array == null || array == UNDEFINED) { |
jlaskey@3 | 112 | args = ScriptRuntime.EMPTY_ARRAY; |
sundar@350 | 113 | } else if (array instanceof ScriptObjectMirror) { |
sundar@350 | 114 | // look for array-like ScriptObjectMirror object |
sundar@350 | 115 | final ScriptObjectMirror mirror = (ScriptObjectMirror)array; |
sundar@350 | 116 | final Object len = mirror.containsKey("length")? mirror.getMember("length") : Integer.valueOf(0); |
sundar@350 | 117 | final int n = (int)JSType.toUint32(len); |
sundar@350 | 118 | |
sundar@350 | 119 | args = new Object[n]; |
sundar@350 | 120 | for (int i = 0; i < args.length; i++) { |
sundar@350 | 121 | args[i] = mirror.containsKey(i)? mirror.getSlot(i) : UNDEFINED; |
sundar@350 | 122 | } |
jlaskey@3 | 123 | } else { |
lagergren@112 | 124 | throw typeError("function.apply.expects.array"); |
jlaskey@3 | 125 | } |
jlaskey@3 | 126 | |
hannesw@63 | 127 | return ScriptRuntime.apply((ScriptFunction)self, thiz, args); |
jlaskey@3 | 128 | } |
jlaskey@3 | 129 | |
jlaskey@3 | 130 | /** |
jlaskey@3 | 131 | * ECMA 15.3.4.4 Function.prototype.call (thisArg [ , arg1 [ , arg2, ... ] ] ) |
jlaskey@3 | 132 | * |
jlaskey@3 | 133 | * @param self self reference |
jlaskey@3 | 134 | * @param args arguments for call |
jlaskey@3 | 135 | * @return result of call |
jlaskey@3 | 136 | */ |
jlaskey@3 | 137 | @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) |
jlaskey@3 | 138 | public static Object call(final Object self, final Object... args) { |
jlaskey@3 | 139 | if (!(self instanceof ScriptFunction)) { |
lagergren@112 | 140 | throw typeError("not.a.function", ScriptRuntime.safeToString(self)); |
jlaskey@3 | 141 | } |
jlaskey@3 | 142 | |
jlaskey@3 | 143 | Object thiz = (args.length == 0) ? UNDEFINED : args[0]; |
jlaskey@3 | 144 | Object[] arguments; |
jlaskey@3 | 145 | |
jlaskey@3 | 146 | if (args.length > 1) { |
jlaskey@3 | 147 | arguments = new Object[args.length - 1]; |
jlaskey@3 | 148 | System.arraycopy(args, 1, arguments, 0, arguments.length); |
jlaskey@3 | 149 | } else { |
jlaskey@3 | 150 | arguments = ScriptRuntime.EMPTY_ARRAY; |
jlaskey@3 | 151 | } |
jlaskey@3 | 152 | |
hannesw@63 | 153 | return ScriptRuntime.apply((ScriptFunction)self, thiz, arguments); |
jlaskey@3 | 154 | } |
jlaskey@3 | 155 | |
jlaskey@3 | 156 | /** |
jlaskey@3 | 157 | * ECMA 15.3.4.5 Function.prototype.bind (thisArg [, arg1 [, arg2, ...]]) |
jlaskey@3 | 158 | * |
jlaskey@3 | 159 | * @param self self reference |
jlaskey@3 | 160 | * @param args arguments for bind |
jlaskey@3 | 161 | * @return function with bound arguments |
jlaskey@3 | 162 | */ |
jlaskey@3 | 163 | @Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1) |
jlaskey@3 | 164 | public static Object bind(final Object self, final Object... args) { |
jlaskey@3 | 165 | if (!(self instanceof ScriptFunction)) { |
lagergren@112 | 166 | throw typeError("not.a.function", ScriptRuntime.safeToString(self)); |
jlaskey@3 | 167 | } |
jlaskey@3 | 168 | |
hannesw@63 | 169 | final Object thiz = (args.length == 0) ? UNDEFINED : args[0]; |
jlaskey@3 | 170 | |
jlaskey@3 | 171 | Object[] arguments; |
jlaskey@3 | 172 | if (args.length > 1) { |
jlaskey@3 | 173 | arguments = new Object[args.length - 1]; |
jlaskey@3 | 174 | System.arraycopy(args, 1, arguments, 0, arguments.length); |
jlaskey@3 | 175 | } else { |
jlaskey@3 | 176 | arguments = ScriptRuntime.EMPTY_ARRAY; |
jlaskey@3 | 177 | } |
jlaskey@3 | 178 | |
sundar@82 | 179 | return ((ScriptFunctionImpl)self).makeBoundFunction(thiz, arguments); |
jlaskey@3 | 180 | } |
jlaskey@3 | 181 | |
jlaskey@3 | 182 | /** |
jlaskey@3 | 183 | * Nashorn extension: Function.prototype.toSource |
jlaskey@3 | 184 | * |
jlaskey@3 | 185 | * @param self self reference |
jlaskey@3 | 186 | * @return source for function |
jlaskey@3 | 187 | */ |
jlaskey@3 | 188 | @Function(attributes = Attribute.NOT_ENUMERABLE) |
jlaskey@3 | 189 | public static Object toSource(final Object self) { |
jlaskey@3 | 190 | if (!(self instanceof ScriptFunction)) { |
lagergren@112 | 191 | throw typeError("not.a.function", ScriptRuntime.safeToString(self)); |
jlaskey@3 | 192 | } |
jlaskey@3 | 193 | return ((ScriptFunction)self).toSource(); |
jlaskey@3 | 194 | } |
jlaskey@3 | 195 | |
jlaskey@3 | 196 | /** |
jlaskey@3 | 197 | * ECMA 15.3.2.1 new Function (p1, p2, ... , pn, body) |
jlaskey@3 | 198 | * |
jlaskey@3 | 199 | * Constructor |
jlaskey@3 | 200 | * |
jlaskey@3 | 201 | * @param newObj is the new operator used for constructing this function |
jlaskey@3 | 202 | * @param self self reference |
jlaskey@3 | 203 | * @param args arguments |
jlaskey@3 | 204 | * @return new NativeFunction |
jlaskey@3 | 205 | */ |
jlaskey@3 | 206 | @Constructor(arity = 1) |
jlaskey@3 | 207 | public static Object function(final boolean newObj, final Object self, final Object... args) { |
jlaskey@3 | 208 | final StringBuilder sb = new StringBuilder(); |
jlaskey@3 | 209 | |
jlaskey@3 | 210 | sb.append("(function ("); |
jlaskey@3 | 211 | if (args.length > 0) { |
sundar@316 | 212 | final StringBuilder paramListBuf = new StringBuilder(); |
jlaskey@3 | 213 | for (int i = 0; i < args.length - 1; i++) { |
sundar@316 | 214 | paramListBuf.append(JSType.toString(args[i])); |
jlaskey@3 | 215 | if (i < args.length - 2) { |
sundar@316 | 216 | paramListBuf.append(","); |
jlaskey@3 | 217 | } |
jlaskey@3 | 218 | } |
sundar@316 | 219 | |
sundar@316 | 220 | final String paramList = paramListBuf.toString(); |
sundar@316 | 221 | if (! paramList.isEmpty()) { |
sundar@316 | 222 | checkFunctionParameters(paramList); |
sundar@316 | 223 | sb.append(paramList); |
sundar@316 | 224 | } |
jlaskey@3 | 225 | } |
jlaskey@3 | 226 | sb.append(") {\n"); |
jlaskey@3 | 227 | if (args.length > 0) { |
sundar@316 | 228 | final String funcBody = JSType.toString(args[args.length - 1]); |
sundar@316 | 229 | checkFunctionBody(funcBody); |
sundar@316 | 230 | sb.append(funcBody); |
jlaskey@3 | 231 | sb.append('\n'); |
jlaskey@3 | 232 | } |
jlaskey@3 | 233 | sb.append("})"); |
jlaskey@3 | 234 | |
jlaskey@3 | 235 | final Global global = Global.instance(); |
jlaskey@3 | 236 | |
sundar@344 | 237 | return Global.directEval(global, sb.toString(), global, "<function>", global.isStrictContext()); |
jlaskey@3 | 238 | } |
sundar@316 | 239 | |
sundar@316 | 240 | private static void checkFunctionParameters(final String params) { |
sundar@316 | 241 | final Source src = new Source("<function>", params); |
sundar@316 | 242 | final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager()); |
sundar@316 | 243 | try { |
sundar@316 | 244 | parser.parseFormalParameterList(); |
sundar@316 | 245 | } catch (final ParserException pe) { |
sundar@316 | 246 | pe.throwAsEcmaException(); |
sundar@316 | 247 | } |
sundar@316 | 248 | } |
sundar@316 | 249 | |
sundar@316 | 250 | private static void checkFunctionBody(final String funcBody) { |
sundar@316 | 251 | final Source src = new Source("<function>", funcBody); |
sundar@316 | 252 | final Parser parser = new Parser(Global.getEnv(), src, new Context.ThrowErrorManager()); |
sundar@316 | 253 | try { |
sundar@316 | 254 | parser.parseFunctionBody(); |
sundar@316 | 255 | } catch (final ParserException pe) { |
sundar@316 | 256 | pe.throwAsEcmaException(); |
sundar@316 | 257 | } |
sundar@316 | 258 | } |
jlaskey@3 | 259 | } |