src/jdk/nashorn/internal/parser/Parser.java

Tue, 26 Aug 2014 11:31:31 +0200

author
attila
date
Tue, 26 Aug 2014 11:31:31 +0200
changeset 975
85e3f07fc5fc
parent 963
e2497b11a021
child 977
7cf80b2dc39b
permissions
-rw-r--r--

8055911: Don't use String.intern for IdentNode
Reviewed-by: lagergren, sundar

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.parser;
jlaskey@3 27
attila@962 28 import static jdk.nashorn.internal.codegen.CompilerConstants.ANON_FUNCTION_PREFIX;
jlaskey@3 29 import static jdk.nashorn.internal.codegen.CompilerConstants.EVAL;
attila@963 30 import static jdk.nashorn.internal.codegen.CompilerConstants.PROGRAM;
jlaskey@3 31 import static jdk.nashorn.internal.parser.TokenType.ASSIGN;
jlaskey@3 32 import static jdk.nashorn.internal.parser.TokenType.CASE;
jlaskey@3 33 import static jdk.nashorn.internal.parser.TokenType.CATCH;
jlaskey@3 34 import static jdk.nashorn.internal.parser.TokenType.COLON;
jlaskey@3 35 import static jdk.nashorn.internal.parser.TokenType.COMMARIGHT;
sundar@844 36 import static jdk.nashorn.internal.parser.TokenType.CONST;
jlaskey@3 37 import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
jlaskey@3 38 import static jdk.nashorn.internal.parser.TokenType.DECPREFIX;
jlaskey@3 39 import static jdk.nashorn.internal.parser.TokenType.ELSE;
jlaskey@3 40 import static jdk.nashorn.internal.parser.TokenType.EOF;
jlaskey@3 41 import static jdk.nashorn.internal.parser.TokenType.EOL;
jlaskey@3 42 import static jdk.nashorn.internal.parser.TokenType.FINALLY;
jlaskey@3 43 import static jdk.nashorn.internal.parser.TokenType.FUNCTION;
jlaskey@3 44 import static jdk.nashorn.internal.parser.TokenType.IDENT;
jlaskey@3 45 import static jdk.nashorn.internal.parser.TokenType.IF;
jlaskey@3 46 import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
jlaskey@3 47 import static jdk.nashorn.internal.parser.TokenType.LBRACE;
jlaskey@3 48 import static jdk.nashorn.internal.parser.TokenType.LPAREN;
jlaskey@3 49 import static jdk.nashorn.internal.parser.TokenType.RBRACE;
jlaskey@3 50 import static jdk.nashorn.internal.parser.TokenType.RBRACKET;
jlaskey@3 51 import static jdk.nashorn.internal.parser.TokenType.RPAREN;
jlaskey@3 52 import static jdk.nashorn.internal.parser.TokenType.SEMICOLON;
jlaskey@3 53 import static jdk.nashorn.internal.parser.TokenType.TERNARY;
jlaskey@3 54 import static jdk.nashorn.internal.parser.TokenType.WHILE;
jlaskey@3 55
attila@963 56 import java.util.ArrayDeque;
jlaskey@3 57 import java.util.ArrayList;
attila@430 58 import java.util.Collections;
attila@963 59 import java.util.Deque;
hannesw@440 60 import java.util.HashMap;
jlaskey@3 61 import java.util.HashSet;
attila@144 62 import java.util.Iterator;
jlaskey@3 63 import java.util.List;
jlaskey@3 64 import java.util.Map;
sundar@563 65 import jdk.internal.dynalink.support.NameCodec;
lagergren@89 66 import jdk.nashorn.internal.codegen.CompilerConstants;
lagergren@89 67 import jdk.nashorn.internal.codegen.Namespace;
jlaskey@3 68 import jdk.nashorn.internal.ir.AccessNode;
lagergren@253 69 import jdk.nashorn.internal.ir.BaseNode;
jlaskey@3 70 import jdk.nashorn.internal.ir.BinaryNode;
jlaskey@3 71 import jdk.nashorn.internal.ir.Block;
lagergren@211 72 import jdk.nashorn.internal.ir.BlockLexicalContext;
attila@430 73 import jdk.nashorn.internal.ir.BlockStatement;
jlaskey@3 74 import jdk.nashorn.internal.ir.BreakNode;
jlaskey@3 75 import jdk.nashorn.internal.ir.BreakableNode;
jlaskey@3 76 import jdk.nashorn.internal.ir.CallNode;
jlaskey@3 77 import jdk.nashorn.internal.ir.CaseNode;
jlaskey@3 78 import jdk.nashorn.internal.ir.CatchNode;
jlaskey@3 79 import jdk.nashorn.internal.ir.ContinueNode;
jlaskey@3 80 import jdk.nashorn.internal.ir.EmptyNode;
attila@430 81 import jdk.nashorn.internal.ir.Expression;
attila@430 82 import jdk.nashorn.internal.ir.ExpressionStatement;
jlaskey@3 83 import jdk.nashorn.internal.ir.ForNode;
jlaskey@3 84 import jdk.nashorn.internal.ir.FunctionNode;
attila@144 85 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
jlaskey@3 86 import jdk.nashorn.internal.ir.IdentNode;
jlaskey@3 87 import jdk.nashorn.internal.ir.IfNode;
jlaskey@3 88 import jdk.nashorn.internal.ir.IndexNode;
attila@963 89 import jdk.nashorn.internal.ir.JoinPredecessorExpression;
jlaskey@3 90 import jdk.nashorn.internal.ir.LabelNode;
attila@144 91 import jdk.nashorn.internal.ir.LexicalContext;
jlaskey@3 92 import jdk.nashorn.internal.ir.LiteralNode;
lagergren@211 93 import jdk.nashorn.internal.ir.LoopNode;
jlaskey@3 94 import jdk.nashorn.internal.ir.Node;
jlaskey@3 95 import jdk.nashorn.internal.ir.ObjectNode;
jlaskey@3 96 import jdk.nashorn.internal.ir.PropertyKey;
jlaskey@3 97 import jdk.nashorn.internal.ir.PropertyNode;
jlaskey@3 98 import jdk.nashorn.internal.ir.ReturnNode;
jlaskey@3 99 import jdk.nashorn.internal.ir.RuntimeNode;
lagergren@253 100 import jdk.nashorn.internal.ir.Statement;
jlaskey@3 101 import jdk.nashorn.internal.ir.SwitchNode;
jlaskey@3 102 import jdk.nashorn.internal.ir.TernaryNode;
jlaskey@3 103 import jdk.nashorn.internal.ir.ThrowNode;
jlaskey@3 104 import jdk.nashorn.internal.ir.TryNode;
jlaskey@3 105 import jdk.nashorn.internal.ir.UnaryNode;
jlaskey@3 106 import jdk.nashorn.internal.ir.VarNode;
jlaskey@3 107 import jdk.nashorn.internal.ir.WhileNode;
jlaskey@3 108 import jdk.nashorn.internal.ir.WithNode;
attila@963 109 import jdk.nashorn.internal.ir.debug.ASTWriter;
attila@963 110 import jdk.nashorn.internal.ir.debug.PrintVisitor;
attila@963 111 import jdk.nashorn.internal.runtime.Context;
lagergren@89 112 import jdk.nashorn.internal.runtime.ErrorManager;
jlaskey@3 113 import jdk.nashorn.internal.runtime.JSErrorType;
jlaskey@3 114 import jdk.nashorn.internal.runtime.ParserException;
attila@963 115 import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
sundar@118 116 import jdk.nashorn.internal.runtime.ScriptEnvironment;
attila@101 117 import jdk.nashorn.internal.runtime.ScriptingFunctions;
lagergren@89 118 import jdk.nashorn.internal.runtime.Source;
attila@963 119 import jdk.nashorn.internal.runtime.logging.DebugLogger;
attila@963 120 import jdk.nashorn.internal.runtime.logging.Loggable;
attila@963 121 import jdk.nashorn.internal.runtime.logging.Logger;
jlaskey@3 122
jlaskey@3 123 /**
jlaskey@3 124 * Builds the IR.
jlaskey@3 125 */
attila@963 126 @Logger(name="parser")
attila@963 127 public class Parser extends AbstractParser implements Loggable {
attila@555 128 private static final String ARGUMENTS_NAME = CompilerConstants.ARGUMENTS_VAR.symbolName();
attila@555 129
attila@963 130 /** Current env. */
sundar@118 131 private final ScriptEnvironment env;
jlaskey@3 132
jlaskey@67 133 /** Is scripting mode. */
jlaskey@67 134 private final boolean scripting;
jlaskey@67 135
lagergren@253 136 private List<Statement> functionDeclarations;
jlaskey@3 137
lagergren@211 138 private final BlockLexicalContext lc = new BlockLexicalContext();
attila@963 139 private final Deque<Object> defaultNames = new ArrayDeque<>();
lagergren@211 140
lagergren@89 141 /** Namespace for function names where not explicitly given */
lagergren@89 142 private final Namespace namespace;
lagergren@89 143
attila@963 144 private final DebugLogger log;
lagergren@108 145
sundar@433 146 /** to receive line information from Lexer when scanning multine literals. */
sundar@433 147 protected final Lexer.LineInfoReceiver lineInfoReceiver;
sundar@433 148
attila@963 149 private int nextFunctionId;
attila@963 150
lagergren@89 151 /**
lagergren@89 152 * Constructor
lagergren@89 153 *
sundar@118 154 * @param env script environment
lagergren@89 155 * @param source source to parse
lagergren@89 156 * @param errors error manager
lagergren@89 157 */
sundar@118 158 public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors) {
attila@963 159 this(env, source, errors, env._strict, null);
attila@963 160 }
attila@963 161
attila@963 162 /**
attila@963 163 * Constructor
attila@963 164 *
attila@963 165 * @param env script environment
attila@963 166 * @param source source to parse
attila@963 167 * @param errors error manager
attila@963 168 * @param strict strict
attila@963 169 * @param log debug logger if one is needed
attila@963 170 */
attila@963 171 public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final DebugLogger log) {
attila@963 172 this(env, source, errors, strict, FunctionNode.FIRST_FUNCTION_ID, 0, log);
lagergren@89 173 }
lagergren@89 174
jlaskey@3 175 /**
jlaskey@3 176 * Construct a parser.
lagergren@89 177 *
sundar@118 178 * @param env script environment
lagergren@89 179 * @param source source to parse
lagergren@89 180 * @param errors error manager
lagergren@89 181 * @param strict parser created with strict mode enabled.
attila@963 182 * @param nextFunctionId starting value for assigning new unique ids to function nodes
attila@963 183 * @param lineOffset line offset to start counting lines from
attila@963 184 * @param log debug logger if one is needed
jlaskey@3 185 */
attila@963 186 public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int nextFunctionId, final int lineOffset, final DebugLogger log) {
attila@963 187 super(source, errors, strict, lineOffset);
attila@963 188 this.env = env;
sundar@118 189 this.namespace = new Namespace(env.getNamespace());
attila@963 190 this.nextFunctionId = nextFunctionId;
sundar@118 191 this.scripting = env._scripting;
sundar@433 192 if (this.scripting) {
sundar@433 193 this.lineInfoReceiver = new Lexer.LineInfoReceiver() {
sundar@433 194 @Override
lagergren@505 195 public void lineInfo(final int receiverLine, final int receiverLinePosition) {
sundar@433 196 // update the parser maintained line information
lagergren@505 197 Parser.this.line = receiverLine;
lagergren@505 198 Parser.this.linePosition = receiverLinePosition;
sundar@433 199 }
sundar@433 200 };
sundar@433 201 } else {
sundar@433 202 // non-scripting mode script can't have multi-line literals
sundar@433 203 this.lineInfoReceiver = null;
sundar@433 204 }
attila@963 205
attila@963 206 this.log = log == null ? DebugLogger.DISABLED_LOGGER : log;
attila@963 207 }
attila@963 208
attila@963 209 @Override
attila@963 210 public DebugLogger getLogger() {
attila@963 211 return log;
attila@963 212 }
attila@963 213
attila@963 214 @Override
attila@963 215 public DebugLogger initLogger(final Context context) {
attila@963 216 return context.getLogger(this.getClass());
attila@963 217 }
attila@963 218
attila@963 219 /**
attila@963 220 * Sets the name for the first function. This is only used when reparsing anonymous functions to ensure they can
attila@963 221 * preserve their already assigned name, as that name doesn't appear in their source text.
attila@963 222 * @param name the name for the first parsed function.
attila@963 223 */
attila@963 224 public void setFunctionName(final String name) {
attila@975 225 defaultNames.push(createIdentNode(0, 0, name));
jlaskey@3 226 }
jlaskey@3 227
jlaskey@3 228 /**
lagergren@89 229 * Execute parse and return the resulting function node.
lagergren@89 230 * Errors will be thrown and the error manager will contain information
lagergren@89 231 * if parsing should fail
lagergren@89 232 *
lagergren@89 233 * This is the default parse call, which will name the function node
attila@963 234 * {code :program} {@link CompilerConstants#PROGRAM}
lagergren@89 235 *
lagergren@89 236 * @return function node resulting from successful parse
jlaskey@3 237 */
lagergren@89 238 public FunctionNode parse() {
attila@963 239 return parse(PROGRAM.symbolName(), 0, source.getLength(), false);
jlaskey@3 240 }
jlaskey@3 241
jlaskey@3 242 /**
lagergren@89 243 * Execute parse and return the resulting function node.
lagergren@89 244 * Errors will be thrown and the error manager will contain information
lagergren@89 245 * if parsing should fail
lagergren@89 246 *
attila@963 247 * This should be used to create one and only one function node
attila@963 248 *
lagergren@89 249 * @param scriptName name for the script, given to the parsed FunctionNode
attila@963 250 * @param startPos start position in source
attila@963 251 * @param len length of parse
attila@963 252 * @param allowPropertyFunction if true, "get" and "set" are allowed as first tokens of the program, followed by
attila@963 253 * a property getter or setter function. This is used when reparsing a function that can potentially be defined as a
attila@963 254 * property getter or setter in an object literal.
lagergren@89 255 *
lagergren@89 256 * @return function node resulting from successful parse
jlaskey@3 257 */
attila@963 258 public FunctionNode parse(final String scriptName, final int startPos, final int len, final boolean allowPropertyFunction) {
attila@963 259 final boolean isTimingEnabled = env.isTimingEnabled();
attila@963 260 final long t0 = isTimingEnabled ? System.currentTimeMillis() : 0L;
attila@963 261 log.info(this, " begin for '", scriptName, "'");
lagergren@108 262
jlaskey@3 263 try {
jlaskey@3 264 stream = new TokenStream();
attila@963 265 lexer = new Lexer(source, startPos, len, stream, scripting && !env._no_syntax_extensions);
attila@963 266 lexer.line = lexer.pendingLine = lineOffset + 1;
attila@963 267 line = lineOffset;
jlaskey@3 268
jlaskey@3 269 // Set up first token (skips opening EOL.)
jlaskey@3 270 k = -1;
jlaskey@3 271 next();
jlaskey@3 272 // Begin parse.
attila@963 273 return program(scriptName, allowPropertyFunction);
jlaskey@3 274 } catch (final Exception e) {
sundar@316 275 handleParseException(e);
sundar@316 276
sundar@316 277 return null;
sundar@316 278 } finally {
sundar@316 279 final String end = this + " end '" + scriptName + "'";
attila@963 280 if (isTimingEnabled) {
attila@963 281 env._timing.accumulateTime(toString(), System.currentTimeMillis() - t0);
attila@963 282 log.info(end, "' in ", System.currentTimeMillis() - t0, " ms");
sundar@316 283 } else {
attila@963 284 log.info(end);
jlaskey@3 285 }
sundar@316 286 }
sundar@316 287 }
sundar@316 288
sundar@316 289 /**
sundar@316 290 * Parse and return the list of function parameter list. A comma
sundar@316 291 * separated list of function parameter identifiers is expected to be parsed.
sundar@316 292 * Errors will be thrown and the error manager will contain information
sundar@316 293 * if parsing should fail. This method is used to check if parameter Strings
sundar@316 294 * passed to "Function" constructor is a valid or not.
sundar@316 295 *
sundar@316 296 * @return the list of IdentNodes representing the formal parameter list
sundar@316 297 */
sundar@316 298 public List<IdentNode> parseFormalParameterList() {
sundar@316 299 try {
sundar@316 300 stream = new TokenStream();
sundar@316 301 lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
sundar@316 302
sundar@316 303 // Set up first token (skips opening EOL.)
sundar@316 304 k = -1;
sundar@316 305 next();
sundar@316 306
sundar@316 307 return formalParameterList(TokenType.EOF);
sundar@316 308 } catch (final Exception e) {
sundar@316 309 handleParseException(e);
jlaskey@3 310 return null;
sundar@316 311 }
sundar@316 312 }
sundar@316 313
sundar@316 314 /**
sundar@316 315 * Execute parse and return the resulting function node.
sundar@316 316 * Errors will be thrown and the error manager will contain information
sundar@316 317 * if parsing should fail. This method is used to check if code String
sundar@316 318 * passed to "Function" constructor is a valid function body or not.
sundar@316 319 *
sundar@316 320 * @return function node resulting from successful parse
sundar@316 321 */
sundar@316 322 public FunctionNode parseFunctionBody() {
sundar@316 323 try {
sundar@316 324 stream = new TokenStream();
sundar@316 325 lexer = new Lexer(source, stream, scripting && !env._no_syntax_extensions);
attila@963 326 final int functionLine = line;
sundar@316 327
sundar@316 328 // Set up first token (skips opening EOL.)
sundar@316 329 k = -1;
sundar@316 330 next();
sundar@316 331
sundar@316 332 // Make a fake token for the function.
sundar@316 333 final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength());
sundar@316 334 // Set up the function to append elements.
sundar@316 335
sundar@316 336 FunctionNode function = newFunctionNode(
sundar@316 337 functionToken,
attila@963 338 new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName()),
sundar@316 339 new ArrayList<IdentNode>(),
attila@963 340 FunctionNode.Kind.NORMAL,
attila@963 341 functionLine);
sundar@316 342
sundar@316 343 functionDeclarations = new ArrayList<>();
attila@963 344 sourceElements(false);
sundar@316 345 addFunctionDeclarations(function);
sundar@316 346 functionDeclarations = null;
sundar@316 347
sundar@316 348 expect(EOF);
sundar@316 349
sundar@316 350 function.setFinish(source.getLength() - 1);
sundar@316 351 function = restoreFunctionNode(function, token); //commit code
sundar@316 352 function = function.setBody(lc, function.getBody().setNeedsScope(lc));
attila@963 353
attila@963 354 printAST(function);
sundar@316 355 return function;
sundar@316 356 } catch (final Exception e) {
sundar@316 357 handleParseException(e);
sundar@316 358 return null;
sundar@316 359 }
sundar@316 360 }
sundar@316 361
sundar@316 362 private void handleParseException(final Exception e) {
sundar@316 363 // Extract message from exception. The message will be in error
sundar@316 364 // message format.
sundar@316 365 String message = e.getMessage();
sundar@316 366
sundar@316 367 // If empty message.
sundar@316 368 if (message == null) {
sundar@316 369 message = e.toString();
sundar@316 370 }
sundar@316 371
sundar@316 372 // Issue message.
sundar@316 373 if (e instanceof ParserException) {
sundar@316 374 errors.error((ParserException)e);
sundar@316 375 } else {
sundar@316 376 errors.error(message);
sundar@316 377 }
sundar@316 378
sundar@316 379 if (env._dump_on_error) {
sundar@316 380 e.printStackTrace(env.getErr());
sundar@316 381 }
jlaskey@3 382 }
jlaskey@3 383
jlaskey@3 384 /**
jlaskey@3 385 * Skip to a good parsing recovery point.
jlaskey@3 386 */
jlaskey@3 387 private void recover(final Exception e) {
jlaskey@3 388 if (e != null) {
jlaskey@3 389 // Extract message from exception. The message will be in error
jlaskey@3 390 // message format.
jlaskey@3 391 String message = e.getMessage();
jlaskey@3 392
jlaskey@3 393 // If empty message.
jlaskey@3 394 if (message == null) {
jlaskey@3 395 message = e.toString();
jlaskey@3 396 }
jlaskey@3 397
jlaskey@3 398 // Issue message.
jlaskey@3 399 if (e instanceof ParserException) {
jlaskey@3 400 errors.error((ParserException)e);
jlaskey@3 401 } else {
jlaskey@3 402 errors.error(message);
jlaskey@3 403 }
jlaskey@3 404
sundar@118 405 if (env._dump_on_error) {
sundar@118 406 e.printStackTrace(env.getErr());
jlaskey@3 407 }
jlaskey@3 408 }
jlaskey@3 409
jlaskey@3 410 // Skip to a recovery point.
jlaskey@3 411 loop:
jlaskey@3 412 while (true) {
jlaskey@3 413 switch (type) {
jlaskey@3 414 case EOF:
jlaskey@3 415 // Can not go any further.
jlaskey@3 416 break loop;
jlaskey@3 417 case EOL:
jlaskey@3 418 case SEMICOLON:
jlaskey@3 419 case RBRACE:
jlaskey@3 420 // Good recovery points.
jlaskey@3 421 next();
jlaskey@3 422 break loop;
jlaskey@3 423 default:
jlaskey@3 424 // So we can recover after EOL.
jlaskey@3 425 nextOrEOL();
jlaskey@3 426 break;
jlaskey@3 427 }
jlaskey@3 428 }
jlaskey@3 429 }
jlaskey@3 430
jlaskey@3 431 /**
jlaskey@3 432 * Set up a new block.
jlaskey@3 433 *
jlaskey@3 434 * @return New block.
jlaskey@3 435 */
jlaskey@3 436 private Block newBlock() {
attila@430 437 return lc.push(new Block(token, Token.descPosition(token)));
jlaskey@3 438 }
jlaskey@3 439
jlaskey@3 440 /**
jlaskey@3 441 * Set up a new function block.
jlaskey@3 442 *
jlaskey@3 443 * @param ident Name of function.
jlaskey@3 444 * @return New block.
jlaskey@3 445 */
attila@963 446 private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine) {
jlaskey@3 447 // Build function name.
jlaskey@3 448 final StringBuilder sb = new StringBuilder();
jlaskey@3 449
lagergren@211 450 final FunctionNode parentFunction = lc.getCurrentFunction();
lagergren@211 451 if (parentFunction != null && !parentFunction.isProgram()) {
attila@144 452 sb.append(parentFunction.getName()).append('$');
jlaskey@3 453 }
jlaskey@3 454
sundar@750 455 assert ident.getName() != null;
sundar@750 456 sb.append(ident.getName());
sundar@750 457
lagergren@89 458 final String name = namespace.uniqueName(sb.toString());
attila@963 459 assert parentFunction != null || name.equals(PROGRAM.symbolName()) || name.startsWith(RecompilableScriptFunctionData.RECOMPILATION_PREFIX) : "name = " + name;
lagergren@211 460
lagergren@211 461 int flags = 0;
lagergren@211 462 if (isStrictMode) {
lagergren@211 463 flags |= FunctionNode.IS_STRICT;
lagergren@211 464 }
attila@963 465 if (parentFunction == null) {
attila@963 466 flags |= FunctionNode.IS_PROGRAM;
lagergren@247 467 }
jlaskey@3 468
jlaskey@3 469 // Start new block.
attila@962 470 final FunctionNode functionNode =
lagergren@211 471 new FunctionNode(
lagergren@211 472 source,
attila@963 473 nextFunctionId++,
attila@963 474 functionLine,
lagergren@211 475 token,
lagergren@211 476 Token.descPosition(token),
lagergren@211 477 startToken,
lagergren@211 478 namespace,
lagergren@211 479 ident,
lagergren@211 480 name,
lagergren@211 481 parameters,
lagergren@211 482 kind,
attila@963 483 flags);
lagergren@211 484
lagergren@211 485 lc.push(functionNode);
lagergren@211 486 // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the
lagergren@211 487 // FunctionNode.
lagergren@211 488 newBlock();
lagergren@247 489
lagergren@211 490 return functionNode;
jlaskey@3 491 }
jlaskey@3 492
jlaskey@3 493 /**
jlaskey@3 494 * Restore the current block.
jlaskey@3 495 */
lagergren@211 496 private Block restoreBlock(final Block block) {
lagergren@247 497 return lc.pop(block);
lagergren@211 498 }
lagergren@211 499
lagergren@247 500
lagergren@211 501 private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) {
lagergren@211 502 final Block newBody = restoreBlock(lc.getFunctionBody(functionNode));
lagergren@211 503
lagergren@247 504 return lc.pop(functionNode).
lagergren@247 505 setBody(lc, newBody).
lagergren@247 506 setLastToken(lc, lastToken).
attila@963 507 setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED);
attila@963 508 }
jlaskey@3 509
jlaskey@3 510 /**
jlaskey@3 511 * Get the statements in a block.
jlaskey@3 512 * @return Block statements.
jlaskey@3 513 */
jlaskey@3 514 private Block getBlock(final boolean needsBraces) {
jlaskey@3 515 // Set up new block. Captures LBRACE.
lagergren@211 516 Block newBlock = newBlock();
jlaskey@3 517 try {
attila@144 518 // Block opening brace.
attila@144 519 if (needsBraces) {
attila@144 520 expect(LBRACE);
attila@144 521 }
lagergren@211 522 // Accumulate block statements.
lagergren@211 523 statementList();
lagergren@211 524
jlaskey@3 525 } finally {
lagergren@211 526 newBlock = restoreBlock(newBlock);
jlaskey@3 527 }
jlaskey@3 528
lagergren@137 529 final int possibleEnd = Token.descPosition(token) + Token.descLength(token);
jlaskey@3 530
jlaskey@3 531 // Block closing brace.
jlaskey@3 532 if (needsBraces) {
jlaskey@3 533 expect(RBRACE);
jlaskey@3 534 }
jlaskey@3 535
jlaskey@3 536 newBlock.setFinish(possibleEnd);
jlaskey@3 537
jlaskey@3 538 return newBlock;
jlaskey@3 539 }
jlaskey@3 540
jlaskey@3 541 /**
jlaskey@3 542 * Get all the statements generated by a single statement.
jlaskey@3 543 * @return Statements.
jlaskey@3 544 */
jlaskey@3 545 private Block getStatement() {
jlaskey@3 546 if (type == LBRACE) {
jlaskey@3 547 return getBlock(true);
jlaskey@3 548 }
jlaskey@3 549 // Set up new block. Captures first token.
lagergren@211 550 Block newBlock = newBlock();
jlaskey@3 551 try {
jlaskey@3 552 statement();
jlaskey@3 553 } finally {
lagergren@211 554 newBlock = restoreBlock(newBlock);
jlaskey@3 555 }
jlaskey@3 556 return newBlock;
jlaskey@3 557 }
jlaskey@3 558
jlaskey@3 559 /**
jlaskey@3 560 * Detect calls to special functions.
jlaskey@3 561 * @param ident Called function.
jlaskey@3 562 */
jlaskey@3 563 private void detectSpecialFunction(final IdentNode ident) {
jlaskey@3 564 final String name = ident.getName();
jlaskey@3 565
lagergren@211 566 if (EVAL.symbolName().equals(name)) {
attila@233 567 markEval(lc);
jlaskey@3 568 }
jlaskey@3 569 }
jlaskey@3 570
jlaskey@3 571 /**
jlaskey@3 572 * Detect use of special properties.
jlaskey@3 573 * @param ident Referenced property.
jlaskey@3 574 */
jlaskey@3 575 private void detectSpecialProperty(final IdentNode ident) {
attila@555 576 if (isArguments(ident)) {
lagergren@211 577 lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS);
jlaskey@3 578 }
jlaskey@3 579 }
jlaskey@3 580
attila@555 581 private static boolean isArguments(final String name) {
attila@555 582 return ARGUMENTS_NAME.equals(name);
attila@555 583 }
attila@555 584
attila@555 585 private static boolean isArguments(final IdentNode ident) {
attila@555 586 return isArguments(ident.getName());
attila@555 587 }
attila@555 588
jlaskey@3 589 /**
jlaskey@3 590 * Tells whether a IdentNode can be used as L-value of an assignment
jlaskey@3 591 *
jlaskey@3 592 * @param ident IdentNode to be checked
jlaskey@3 593 * @return whether the ident can be used as L-value
jlaskey@3 594 */
jlaskey@3 595 private static boolean checkIdentLValue(final IdentNode ident) {
jlaskey@3 596 return Token.descType(ident.getToken()).getKind() != TokenKind.KEYWORD;
jlaskey@3 597 }
jlaskey@3 598
jlaskey@3 599 /**
jlaskey@3 600 * Verify an assignment expression.
jlaskey@3 601 * @param op Operation token.
jlaskey@3 602 * @param lhs Left hand side expression.
jlaskey@3 603 * @param rhs Right hand side expression.
jlaskey@3 604 * @return Verified expression.
jlaskey@3 605 */
attila@430 606 private Expression verifyAssignment(final long op, final Expression lhs, final Expression rhs) {
jlaskey@3 607 final TokenType opType = Token.descType(op);
jlaskey@3 608
jlaskey@3 609 switch (opType) {
jlaskey@3 610 case ASSIGN:
jlaskey@3 611 case ASSIGN_ADD:
jlaskey@3 612 case ASSIGN_BIT_AND:
jlaskey@3 613 case ASSIGN_BIT_OR:
jlaskey@3 614 case ASSIGN_BIT_XOR:
jlaskey@3 615 case ASSIGN_DIV:
jlaskey@3 616 case ASSIGN_MOD:
jlaskey@3 617 case ASSIGN_MUL:
jlaskey@3 618 case ASSIGN_SAR:
jlaskey@3 619 case ASSIGN_SHL:
jlaskey@3 620 case ASSIGN_SHR:
jlaskey@3 621 case ASSIGN_SUB:
jlaskey@3 622 if (!(lhs instanceof AccessNode ||
jlaskey@3 623 lhs instanceof IndexNode ||
jlaskey@3 624 lhs instanceof IdentNode)) {
sundar@394 625 return referenceError(lhs, rhs, env._early_lvalue_error);
jlaskey@3 626 }
jlaskey@3 627
jlaskey@3 628 if (lhs instanceof IdentNode) {
lagergren@137 629 if (!checkIdentLValue((IdentNode)lhs)) {
sundar@394 630 return referenceError(lhs, rhs, false);
jlaskey@3 631 }
jlaskey@3 632 verifyStrictIdent((IdentNode)lhs, "assignment");
jlaskey@3 633 }
jlaskey@3 634 break;
jlaskey@3 635
jlaskey@3 636 default:
jlaskey@3 637 break;
jlaskey@3 638 }
jlaskey@3 639
jlaskey@3 640 // Build up node.
attila@963 641 if(BinaryNode.isLogical(opType)) {
attila@963 642 return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs));
attila@963 643 }
lagergren@252 644 return new BinaryNode(op, lhs, rhs);
jlaskey@3 645 }
jlaskey@3 646
attila@963 647
jlaskey@3 648 /**
jlaskey@3 649 * Reduce increment/decrement to simpler operations.
jlaskey@3 650 * @param firstToken First token.
jlaskey@3 651 * @param tokenType Operation token (INCPREFIX/DEC.)
jlaskey@3 652 * @param expression Left hand side expression.
jlaskey@3 653 * @param isPostfix Prefix or postfix.
jlaskey@3 654 * @return Reduced expression.
jlaskey@3 655 */
attila@430 656 private static UnaryNode incDecExpression(final long firstToken, final TokenType tokenType, final Expression expression, final boolean isPostfix) {
jlaskey@3 657 if (isPostfix) {
lagergren@252 658 return new UnaryNode(Token.recast(firstToken, tokenType == DECPREFIX ? DECPOSTFIX : INCPOSTFIX), expression.getStart(), Token.descPosition(firstToken) + Token.descLength(firstToken), expression);
jlaskey@3 659 }
jlaskey@3 660
lagergren@252 661 return new UnaryNode(firstToken, expression);
jlaskey@3 662 }
jlaskey@3 663
jlaskey@3 664 /**
jlaskey@3 665 * -----------------------------------------------------------------------
jlaskey@3 666 *
jlaskey@3 667 * Grammar based on
jlaskey@3 668 *
jlaskey@3 669 * ECMAScript Language Specification
jlaskey@3 670 * ECMA-262 5th Edition / December 2009
jlaskey@3 671 *
jlaskey@3 672 * -----------------------------------------------------------------------
jlaskey@3 673 */
jlaskey@3 674
jlaskey@3 675 /**
jlaskey@3 676 * Program :
jlaskey@3 677 * SourceElements?
jlaskey@3 678 *
jlaskey@3 679 * See 14
jlaskey@3 680 *
jlaskey@3 681 * Parse the top level script.
jlaskey@3 682 */
attila@963 683 private FunctionNode program(final String scriptName, final boolean allowPropertyFunction) {
attila@963 684 // Make a pseudo-token for the script holding its start and length.
attila@963 685 final long functionToken = Token.toDesc(FUNCTION, Token.descPosition(Token.withDelimiter(token)), source.getLength());
attila@963 686 final int functionLine = line;
jlaskey@3 687 // Set up the script to append elements.
lagergren@137 688
lagergren@211 689 FunctionNode script = newFunctionNode(
lagergren@211 690 functionToken,
lagergren@252 691 new IdentNode(functionToken, Token.descPosition(functionToken), scriptName),
lagergren@211 692 new ArrayList<IdentNode>(),
attila@963 693 FunctionNode.Kind.SCRIPT,
attila@963 694 functionLine);
lagergren@211 695
attila@144 696 functionDeclarations = new ArrayList<>();
attila@963 697 sourceElements(allowPropertyFunction);
lagergren@211 698 addFunctionDeclarations(script);
attila@144 699 functionDeclarations = null;
lagergren@211 700
jlaskey@3 701 expect(EOF);
lagergren@211 702
jlaskey@3 703 script.setFinish(source.getLength() - 1);
jlaskey@3 704
lagergren@211 705 script = restoreFunctionNode(script, token); //commit code
lagergren@211 706 script = script.setBody(lc, script.getBody().setNeedsScope(lc));
lagergren@247 707
jlaskey@3 708 return script;
jlaskey@3 709 }
jlaskey@3 710
jlaskey@3 711 /**
jlaskey@3 712 * Directive value or null if statement is not a directive.
jlaskey@3 713 *
jlaskey@3 714 * @param stmt Statement to be checked
jlaskey@3 715 * @return Directive value if the given statement is a directive
jlaskey@3 716 */
jlaskey@3 717 private String getDirective(final Node stmt) {
attila@430 718 if (stmt instanceof ExpressionStatement) {
attila@430 719 final Node expr = ((ExpressionStatement)stmt).getExpression();
jlaskey@3 720 if (expr instanceof LiteralNode) {
jlaskey@3 721 final LiteralNode<?> lit = (LiteralNode<?>)expr;
jlaskey@3 722 final long litToken = lit.getToken();
jlaskey@3 723 final TokenType tt = Token.descType(litToken);
jlaskey@3 724 // A directive is either a string or an escape string
jlaskey@3 725 if (tt == TokenType.STRING || tt == TokenType.ESCSTRING) {
jlaskey@3 726 // Make sure that we don't unescape anything. Return as seen in source!
jlaskey@3 727 return source.getString(lit.getStart(), Token.descLength(litToken));
jlaskey@3 728 }
jlaskey@3 729 }
jlaskey@3 730 }
jlaskey@3 731
jlaskey@3 732 return null;
jlaskey@3 733 }
jlaskey@3 734
jlaskey@3 735 /**
jlaskey@3 736 * SourceElements :
jlaskey@3 737 * SourceElement
jlaskey@3 738 * SourceElements SourceElement
jlaskey@3 739 *
jlaskey@3 740 * See 14
jlaskey@3 741 *
jlaskey@3 742 * Parse the elements of the script or function.
jlaskey@3 743 */
attila@963 744 private void sourceElements(final boolean shouldAllowPropertyFunction) {
attila@963 745 List<Node> directiveStmts = null;
attila@963 746 boolean checkDirective = true;
attila@963 747 boolean allowPropertyFunction = shouldAllowPropertyFunction;
attila@963 748 final boolean oldStrictMode = isStrictMode;
attila@963 749
jlaskey@3 750
jlaskey@3 751 try {
jlaskey@3 752 // If is a script, then process until the end of the script.
jlaskey@3 753 while (type != EOF) {
jlaskey@3 754 // Break if the end of a code block.
jlaskey@3 755 if (type == RBRACE) {
jlaskey@3 756 break;
jlaskey@3 757 }
jlaskey@3 758
jlaskey@3 759 try {
jlaskey@3 760 // Get the next element.
attila@963 761 statement(true, allowPropertyFunction);
attila@963 762 allowPropertyFunction = false;
jlaskey@3 763
jlaskey@3 764 // check for directive prologues
jlaskey@3 765 if (checkDirective) {
jlaskey@3 766 // skip any debug statement like line number to get actual first line
lagergren@211 767 final Node lastStatement = lc.getLastStatement();
jlaskey@3 768
jlaskey@3 769 // get directive prologue, if any
jlaskey@3 770 final String directive = getDirective(lastStatement);
jlaskey@3 771
jlaskey@3 772 // If we have seen first non-directive statement,
jlaskey@3 773 // no more directive statements!!
jlaskey@3 774 checkDirective = directive != null;
jlaskey@3 775
jlaskey@3 776 if (checkDirective) {
jlaskey@3 777 if (!oldStrictMode) {
jlaskey@3 778 if (directiveStmts == null) {
jlaskey@3 779 directiveStmts = new ArrayList<>();
jlaskey@3 780 }
jlaskey@3 781 directiveStmts.add(lastStatement);
jlaskey@3 782 }
jlaskey@3 783
jlaskey@3 784 // handle use strict directive
jlaskey@3 785 if ("use strict".equals(directive)) {
jlaskey@3 786 isStrictMode = true;
lagergren@211 787 final FunctionNode function = lc.getCurrentFunction();
lagergren@211 788 lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_STRICT);
jlaskey@3 789
jlaskey@3 790 // We don't need to check these, if lexical environment is already strict
jlaskey@3 791 if (!oldStrictMode && directiveStmts != null) {
jlaskey@3 792 // check that directives preceding this one do not violate strictness
jlaskey@3 793 for (final Node statement : directiveStmts) {
jlaskey@3 794 // the get value will force unescape of preceeding
jlaskey@3 795 // escaped string directives
jlaskey@3 796 getValue(statement.getToken());
jlaskey@3 797 }
jlaskey@3 798
jlaskey@3 799 // verify that function name as well as parameter names
lagergren@57 800 // satisfy strict mode restrictions.
jlaskey@3 801 verifyStrictIdent(function.getIdent(), "function name");
jlaskey@3 802 for (final IdentNode param : function.getParameters()) {
jlaskey@3 803 verifyStrictIdent(param, "function parameter");
jlaskey@3 804 }
jlaskey@3 805 }
attila@963 806 } else if (Context.DEBUG) {
attila@963 807 final int flag = FunctionNode.getDirectiveFlag(directive);
attila@963 808 if (flag != 0) {
attila@963 809 final FunctionNode function = lc.getCurrentFunction();
attila@963 810 lc.setFlag(function, flag);
attila@963 811 }
jlaskey@3 812 }
jlaskey@3 813 }
jlaskey@3 814 }
jlaskey@3 815 } catch (final Exception e) {
lagergren@211 816 //recover parsing
jlaskey@3 817 recover(e);
jlaskey@3 818 }
jlaskey@3 819
jlaskey@3 820 // No backtracking from here on.
jlaskey@3 821 stream.commit(k);
jlaskey@3 822 }
jlaskey@3 823 } finally {
jlaskey@3 824 isStrictMode = oldStrictMode;
jlaskey@3 825 }
jlaskey@3 826 }
jlaskey@3 827
jlaskey@3 828 /**
jlaskey@3 829 * Statement :
jlaskey@3 830 * Block
jlaskey@3 831 * VariableStatement
jlaskey@3 832 * EmptyStatement
jlaskey@3 833 * ExpressionStatement
jlaskey@3 834 * IfStatement
jlaskey@3 835 * IterationStatement
jlaskey@3 836 * ContinueStatement
jlaskey@3 837 * BreakStatement
jlaskey@3 838 * ReturnStatement
jlaskey@3 839 * WithStatement
jlaskey@3 840 * LabelledStatement
jlaskey@3 841 * SwitchStatement
jlaskey@3 842 * ThrowStatement
jlaskey@3 843 * TryStatement
jlaskey@3 844 * DebuggerStatement
jlaskey@3 845 *
jlaskey@3 846 * see 12
jlaskey@3 847 *
jlaskey@3 848 * Parse any of the basic statement types.
jlaskey@3 849 */
jlaskey@3 850 private void statement() {
attila@963 851 statement(false, false);
jlaskey@3 852 }
jlaskey@3 853
jlaskey@3 854 /**
jlaskey@3 855 * @param topLevel does this statement occur at the "top level" of a script or a function?
jlaskey@3 856 */
attila@963 857 private void statement(final boolean topLevel, final boolean allowPropertyFunction) {
jlaskey@3 858 if (type == FUNCTION) {
jlaskey@3 859 // As per spec (ECMA section 12), function declarations as arbitrary statement
jlaskey@3 860 // is not "portable". Implementation can issue a warning or disallow the same.
attila@144 861 functionExpression(true, topLevel);
jlaskey@3 862 return;
jlaskey@3 863 }
jlaskey@3 864
jlaskey@3 865 switch (type) {
jlaskey@3 866 case LBRACE:
jlaskey@3 867 block();
jlaskey@3 868 break;
jlaskey@3 869 case VAR:
jlaskey@3 870 variableStatement(true);
jlaskey@3 871 break;
jlaskey@3 872 case SEMICOLON:
jlaskey@3 873 emptyStatement();
jlaskey@3 874 break;
jlaskey@3 875 case IF:
jlaskey@3 876 ifStatement();
jlaskey@3 877 break;
jlaskey@3 878 case FOR:
jlaskey@3 879 forStatement();
jlaskey@3 880 break;
jlaskey@3 881 case WHILE:
jlaskey@3 882 whileStatement();
jlaskey@3 883 break;
jlaskey@3 884 case DO:
jlaskey@3 885 doStatement();
jlaskey@3 886 break;
jlaskey@3 887 case CONTINUE:
jlaskey@3 888 continueStatement();
jlaskey@3 889 break;
jlaskey@3 890 case BREAK:
jlaskey@3 891 breakStatement();
jlaskey@3 892 break;
jlaskey@3 893 case RETURN:
jlaskey@3 894 returnStatement();
jlaskey@3 895 break;
jlaskey@3 896 case YIELD:
jlaskey@3 897 yieldStatement();
jlaskey@3 898 break;
jlaskey@3 899 case WITH:
jlaskey@3 900 withStatement();
jlaskey@3 901 break;
jlaskey@3 902 case SWITCH:
jlaskey@3 903 switchStatement();
jlaskey@3 904 break;
jlaskey@3 905 case THROW:
jlaskey@3 906 throwStatement();
jlaskey@3 907 break;
jlaskey@3 908 case TRY:
jlaskey@3 909 tryStatement();
jlaskey@3 910 break;
jlaskey@3 911 case DEBUGGER:
jlaskey@3 912 debuggerStatement();
jlaskey@3 913 break;
jlaskey@3 914 case RPAREN:
jlaskey@3 915 case RBRACKET:
jlaskey@3 916 case EOF:
jlaskey@3 917 expect(SEMICOLON);
jlaskey@3 918 break;
jlaskey@3 919 default:
sundar@844 920 if (env._const_as_var && type == CONST) {
sundar@844 921 variableStatement(true);
sundar@844 922 break;
sundar@844 923 }
sundar@844 924
jlaskey@3 925 if (type == IDENT || isNonStrictModeIdent()) {
jlaskey@3 926 if (T(k + 1) == COLON) {
jlaskey@3 927 labelStatement();
jlaskey@3 928 return;
jlaskey@3 929 }
attila@963 930 if(allowPropertyFunction) {
attila@963 931 final String ident = (String)getValue();
attila@963 932 final long propertyToken = token;
attila@963 933 final int propertyLine = line;
attila@963 934 if("get".equals(ident)) {
attila@963 935 next();
attila@963 936 addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine));
attila@963 937 return;
attila@963 938 } else if("set".equals(ident)) {
attila@963 939 next();
attila@963 940 addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine));
attila@963 941 return;
attila@963 942 }
attila@963 943 }
jlaskey@3 944 }
jlaskey@3 945
jlaskey@3 946 expressionStatement();
jlaskey@3 947 break;
jlaskey@3 948 }
jlaskey@3 949 }
jlaskey@3 950
attila@963 951 private void addPropertyFunctionStatement(final PropertyFunction propertyFunction) {
attila@963 952 final FunctionNode fn = propertyFunction.functionNode;
attila@963 953 functionDeclarations.add(new ExpressionStatement(fn.getLineNumber(), fn.getToken(), finish, fn));
attila@963 954 }
attila@963 955
jlaskey@3 956 /**
jlaskey@3 957 * block :
jlaskey@3 958 * { StatementList? }
jlaskey@3 959 *
jlaskey@3 960 * see 12.1
jlaskey@3 961 *
jlaskey@3 962 * Parse a statement block.
jlaskey@3 963 */
jlaskey@3 964 private void block() {
attila@430 965 appendStatement(new BlockStatement(line, getBlock(true)));
jlaskey@3 966 }
jlaskey@3 967
jlaskey@3 968 /**
jlaskey@3 969 * StatementList :
jlaskey@3 970 * Statement
jlaskey@3 971 * StatementList Statement
jlaskey@3 972 *
jlaskey@3 973 * See 12.1
jlaskey@3 974 *
jlaskey@3 975 * Parse a list of statements.
jlaskey@3 976 */
jlaskey@3 977 private void statementList() {
jlaskey@3 978 // Accumulate statements until end of list. */
jlaskey@3 979 loop:
jlaskey@3 980 while (type != EOF) {
jlaskey@3 981 switch (type) {
jlaskey@3 982 case EOF:
jlaskey@3 983 case CASE:
jlaskey@3 984 case DEFAULT:
jlaskey@3 985 case RBRACE:
jlaskey@3 986 break loop;
jlaskey@3 987 default:
jlaskey@3 988 break;
jlaskey@3 989 }
jlaskey@3 990
jlaskey@3 991 // Get next statement.
jlaskey@3 992 statement();
jlaskey@3 993 }
jlaskey@3 994 }
jlaskey@3 995
jlaskey@3 996 /**
jlaskey@3 997 * Make sure that in strict mode, the identifier name used is allowed.
jlaskey@3 998 *
jlaskey@3 999 * @param ident Identifier that is verified
jlaskey@3 1000 * @param contextString String used in error message to give context to the user
jlaskey@3 1001 */
jlaskey@3 1002 private void verifyStrictIdent(final IdentNode ident, final String contextString) {
jlaskey@3 1003 if (isStrictMode) {
jlaskey@3 1004 switch (ident.getName()) {
jlaskey@3 1005 case "eval":
jlaskey@3 1006 case "arguments":
lagergren@211 1007 throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
jlaskey@3 1008 default:
jlaskey@3 1009 break;
jlaskey@3 1010 }
sundar@594 1011
sundar@594 1012 if (ident.isFutureStrictName()) {
sundar@594 1013 throw error(AbstractParser.message("strict.name", ident.getName(), contextString), ident.getToken());
sundar@594 1014 }
jlaskey@3 1015 }
jlaskey@3 1016 }
jlaskey@3 1017
jlaskey@3 1018 /**
jlaskey@3 1019 * VariableStatement :
jlaskey@3 1020 * var VariableDeclarationList ;
jlaskey@3 1021 *
jlaskey@3 1022 * VariableDeclarationList :
jlaskey@3 1023 * VariableDeclaration
jlaskey@3 1024 * VariableDeclarationList , VariableDeclaration
jlaskey@3 1025 *
jlaskey@3 1026 * VariableDeclaration :
jlaskey@3 1027 * Identifier Initializer?
jlaskey@3 1028 *
jlaskey@3 1029 * Initializer :
jlaskey@3 1030 * = AssignmentExpression
jlaskey@3 1031 *
jlaskey@3 1032 * See 12.2
jlaskey@3 1033 *
jlaskey@3 1034 * Parse a VAR statement.
jlaskey@3 1035 * @param isStatement True if a statement (not used in a FOR.)
jlaskey@3 1036 */
jlaskey@3 1037 private List<VarNode> variableStatement(final boolean isStatement) {
jlaskey@3 1038 // VAR tested in caller.
jlaskey@3 1039 next();
jlaskey@3 1040
jlaskey@3 1041 final List<VarNode> vars = new ArrayList<>();
jlaskey@3 1042
jlaskey@3 1043 while (true) {
jlaskey@3 1044 // Get starting token.
lagergren@253 1045 final int varLine = line;
jlaskey@3 1046 final long varToken = token;
jlaskey@3 1047 // Get name of var.
jlaskey@3 1048 final IdentNode name = getIdent();
jlaskey@3 1049 verifyStrictIdent(name, "variable name");
jlaskey@3 1050
jlaskey@3 1051 // Assume no init.
attila@430 1052 Expression init = null;
jlaskey@3 1053
jlaskey@3 1054 // Look for initializer assignment.
jlaskey@3 1055 if (type == ASSIGN) {
jlaskey@3 1056 next();
jlaskey@3 1057
jlaskey@3 1058 // Get initializer expression. Suppress IN if not statement.
attila@963 1059 defaultNames.push(name);
attila@963 1060 try {
attila@963 1061 init = assignmentExpression(!isStatement);
attila@963 1062 } finally {
attila@963 1063 defaultNames.pop();
attila@963 1064 }
jlaskey@3 1065 }
jlaskey@3 1066
jlaskey@3 1067 // Allocate var node.
lagergren@253 1068 final VarNode var = new VarNode(varLine, varToken, finish, name, init);
jlaskey@3 1069 vars.add(var);
lagergren@211 1070 appendStatement(var);
jlaskey@3 1071
jlaskey@3 1072 if (type != COMMARIGHT) {
jlaskey@3 1073 break;
jlaskey@3 1074 }
jlaskey@3 1075 next();
jlaskey@3 1076 }
jlaskey@3 1077
jlaskey@3 1078 // If is a statement then handle end of line.
jlaskey@3 1079 if (isStatement) {
attila@962 1080 final boolean semicolon = type == SEMICOLON;
jlaskey@3 1081 endOfLine();
jlaskey@9 1082 if (semicolon) {
lagergren@211 1083 lc.getCurrentBlock().setFinish(finish);
jlaskey@9 1084 }
jlaskey@3 1085 }
jlaskey@3 1086
jlaskey@3 1087 return vars;
jlaskey@3 1088 }
jlaskey@3 1089
jlaskey@3 1090 /**
jlaskey@3 1091 * EmptyStatement :
jlaskey@3 1092 * ;
jlaskey@3 1093 *
jlaskey@3 1094 * See 12.3
jlaskey@3 1095 *
jlaskey@3 1096 * Parse an empty statement.
jlaskey@3 1097 */
jlaskey@3 1098 private void emptyStatement() {
sundar@118 1099 if (env._empty_statements) {
lagergren@253 1100 appendStatement(new EmptyNode(line, token, Token.descPosition(token) + Token.descLength(token)));
jlaskey@3 1101 }
jlaskey@3 1102
jlaskey@3 1103 // SEMICOLON checked in caller.
jlaskey@3 1104 next();
jlaskey@3 1105 }
jlaskey@3 1106
jlaskey@3 1107 /**
jlaskey@3 1108 * ExpressionStatement :
jlaskey@3 1109 * Expression ; // [lookahead ~( or function )]
jlaskey@3 1110 *
jlaskey@3 1111 * See 12.4
jlaskey@3 1112 *
jlaskey@3 1113 * Parse an expression used in a statement block.
jlaskey@3 1114 */
jlaskey@3 1115 private void expressionStatement() {
jlaskey@3 1116 // Lookahead checked in caller.
lagergren@253 1117 final int expressionLine = line;
jlaskey@3 1118 final long expressionToken = token;
jlaskey@3 1119
jlaskey@3 1120 // Get expression and add as statement.
attila@430 1121 final Expression expression = expression();
attila@430 1122
attila@430 1123 ExpressionStatement expressionStatement = null;
jlaskey@3 1124 if (expression != null) {
attila@430 1125 expressionStatement = new ExpressionStatement(expressionLine, expressionToken, finish, expression);
attila@430 1126 appendStatement(expressionStatement);
jlaskey@3 1127 } else {
jlaskey@3 1128 expect(null);
jlaskey@3 1129 }
jlaskey@3 1130
jlaskey@3 1131 endOfLine();
jlaskey@3 1132
attila@430 1133 if (expressionStatement != null) {
attila@430 1134 expressionStatement.setFinish(finish);
lagergren@211 1135 lc.getCurrentBlock().setFinish(finish);
jlaskey@3 1136 }
jlaskey@3 1137 }
jlaskey@3 1138
jlaskey@3 1139 /**
jlaskey@3 1140 * IfStatement :
jlaskey@3 1141 * if ( Expression ) Statement else Statement
jlaskey@3 1142 * if ( Expression ) Statement
jlaskey@3 1143 *
jlaskey@3 1144 * See 12.5
jlaskey@3 1145 *
jlaskey@3 1146 * Parse an IF statement.
jlaskey@3 1147 */
jlaskey@3 1148 private void ifStatement() {
jlaskey@3 1149 // Capture IF token.
lagergren@253 1150 final int ifLine = line;
jlaskey@3 1151 final long ifToken = token;
jlaskey@3 1152 // IF tested in caller.
jlaskey@3 1153 next();
jlaskey@3 1154
jlaskey@3 1155 expect(LPAREN);
attila@430 1156 final Expression test = expression();
jlaskey@3 1157 expect(RPAREN);
jlaskey@3 1158 final Block pass = getStatement();
jlaskey@3 1159
jlaskey@3 1160 Block fail = null;
jlaskey@3 1161 if (type == ELSE) {
jlaskey@3 1162 next();
jlaskey@3 1163 fail = getStatement();
jlaskey@3 1164 }
jlaskey@3 1165
lagergren@253 1166 appendStatement(new IfNode(ifLine, ifToken, fail != null ? fail.getFinish() : pass.getFinish(), test, pass, fail));
jlaskey@3 1167 }
jlaskey@3 1168
jlaskey@3 1169 /**
jlaskey@3 1170 * ... IterationStatement:
jlaskey@3 1171 * ...
jlaskey@3 1172 * for ( Expression[NoIn]?; Expression? ; Expression? ) Statement
jlaskey@3 1173 * for ( var VariableDeclarationList[NoIn]; Expression? ; Expression? ) Statement
jlaskey@3 1174 * for ( LeftHandSideExpression in Expression ) Statement
jlaskey@3 1175 * for ( var VariableDeclaration[NoIn] in Expression ) Statement
jlaskey@3 1176 *
jlaskey@3 1177 * See 12.6
jlaskey@3 1178 *
jlaskey@3 1179 * Parse a FOR statement.
jlaskey@3 1180 */
jlaskey@3 1181 private void forStatement() {
jlaskey@3 1182 // Create FOR node, capturing FOR token.
attila@963 1183 ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR);
jlaskey@3 1184
lagergren@211 1185 lc.push(forNode);
jlaskey@3 1186
jlaskey@3 1187 try {
jlaskey@3 1188 // FOR tested in caller.
jlaskey@3 1189 next();
jlaskey@3 1190
jlaskey@3 1191 // Nashorn extension: for each expression.
jlaskey@3 1192 // iterate property values rather than property names.
sundar@118 1193 if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) {
lagergren@211 1194 forNode = forNode.setIsForEach(lc);
jlaskey@3 1195 next();
jlaskey@3 1196 }
jlaskey@3 1197
jlaskey@3 1198 expect(LPAREN);
jlaskey@3 1199
lagergren@211 1200 List<VarNode> vars = null;
lagergren@211 1201
lagergren@211 1202 switch (type) {
lagergren@211 1203 case VAR:
lagergren@211 1204 // Var statements captured in for outer block.
lagergren@211 1205 vars = variableStatement(false);
lagergren@211 1206 break;
lagergren@211 1207 case SEMICOLON:
lagergren@211 1208 break;
lagergren@211 1209 default:
sundar@844 1210 if (env._const_as_var && type == CONST) {
sundar@844 1211 // Var statements captured in for outer block.
sundar@844 1212 vars = variableStatement(false);
sundar@844 1213 break;
sundar@844 1214 }
sundar@844 1215
attila@430 1216 final Expression expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true);
lagergren@211 1217 forNode = forNode.setInit(lc, expression);
lagergren@211 1218 break;
lagergren@211 1219 }
lagergren@211 1220
lagergren@211 1221 switch (type) {
lagergren@211 1222 case SEMICOLON:
lagergren@211 1223 // for (init; test; modify)
sundar@406 1224
sundar@406 1225 // for each (init; test; modify) is invalid
sundar@406 1226 if (forNode.isForEach()) {
sundar@406 1227 throw error(AbstractParser.message("for.each.without.in"), token);
sundar@406 1228 }
sundar@406 1229
lagergren@211 1230 expect(SEMICOLON);
lagergren@211 1231 if (type != SEMICOLON) {
attila@963 1232 forNode = forNode.setTest(lc, joinPredecessorExpression());
lagergren@211 1233 }
lagergren@211 1234 expect(SEMICOLON);
lagergren@211 1235 if (type != RPAREN) {
attila@963 1236 forNode = forNode.setModify(lc, joinPredecessorExpression());
lagergren@211 1237 }
lagergren@211 1238 break;
lagergren@211 1239
lagergren@211 1240 case IN:
attila@963 1241 forNode = forNode.setIsForIn(lc).setTest(lc, new JoinPredecessorExpression());
lagergren@211 1242 if (vars != null) {
lagergren@211 1243 // for (var i in obj)
lagergren@211 1244 if (vars.size() == 1) {
lagergren@211 1245 forNode = forNode.setInit(lc, new IdentNode(vars.get(0).getName()));
lagergren@211 1246 } else {
lagergren@211 1247 // for (var i, j in obj) is invalid
lagergren@211 1248 throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken());
lagergren@211 1249 }
lagergren@211 1250
lagergren@211 1251 } else {
lagergren@211 1252 // for (expr in obj)
lagergren@211 1253 final Node init = forNode.getInit();
lagergren@211 1254 assert init != null : "for..in init expression can not be null here";
lagergren@211 1255
lagergren@211 1256 // check if initial expression is a valid L-value
lagergren@211 1257 if (!(init instanceof AccessNode ||
lagergren@211 1258 init instanceof IndexNode ||
lagergren@211 1259 init instanceof IdentNode)) {
lagergren@211 1260 throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
lagergren@211 1261 }
lagergren@211 1262
lagergren@211 1263 if (init instanceof IdentNode) {
lagergren@211 1264 if (!checkIdentLValue((IdentNode)init)) {
lagergren@211 1265 throw error(AbstractParser.message("not.lvalue.for.in.loop"), init.getToken());
lagergren@211 1266 }
lagergren@211 1267 verifyStrictIdent((IdentNode)init, "for-in iterator");
lagergren@211 1268 }
lagergren@211 1269 }
lagergren@211 1270
lagergren@211 1271 next();
lagergren@211 1272
lagergren@211 1273 // Get the collection expression.
attila@963 1274 forNode = forNode.setModify(lc, joinPredecessorExpression());
lagergren@211 1275 break;
lagergren@211 1276
lagergren@211 1277 default:
lagergren@211 1278 expect(SEMICOLON);
lagergren@211 1279 break;
lagergren@211 1280 }
jlaskey@3 1281
jlaskey@3 1282 expect(RPAREN);
jlaskey@3 1283
jlaskey@3 1284 // Set the for body.
jlaskey@3 1285 final Block body = getStatement();
lagergren@211 1286 forNode = forNode.setBody(lc, body);
jlaskey@3 1287 forNode.setFinish(body.getFinish());
jlaskey@3 1288
lagergren@211 1289 appendStatement(forNode);
jlaskey@3 1290 } finally {
lagergren@211 1291 lc.pop(forNode);
jlaskey@3 1292 }
jlaskey@3 1293 }
jlaskey@3 1294
jlaskey@3 1295 /**
jlaskey@3 1296 * ... IterationStatement :
jlaskey@3 1297 * ...
jlaskey@3 1298 * Expression[NoIn]?; Expression? ; Expression?
jlaskey@3 1299 * var VariableDeclarationList[NoIn]; Expression? ; Expression?
jlaskey@3 1300 * LeftHandSideExpression in Expression
jlaskey@3 1301 * var VariableDeclaration[NoIn] in Expression
jlaskey@3 1302 *
jlaskey@3 1303 * See 12.6
jlaskey@3 1304 *
jlaskey@3 1305 * Parse the control section of a FOR statement. Also used for
jlaskey@3 1306 * comprehensions.
jlaskey@3 1307 * @param forNode Owning FOR.
jlaskey@3 1308 */
lagergren@211 1309
jlaskey@3 1310
jlaskey@3 1311 /**
jlaskey@3 1312 * ...IterationStatement :
jlaskey@3 1313 * ...
jlaskey@3 1314 * while ( Expression ) Statement
jlaskey@3 1315 * ...
jlaskey@3 1316 *
jlaskey@3 1317 * See 12.6
jlaskey@3 1318 *
jlaskey@3 1319 * Parse while statement.
jlaskey@3 1320 */
jlaskey@3 1321 private void whileStatement() {
jlaskey@3 1322 // Capture WHILE token.
jlaskey@3 1323 final long whileToken = token;
jlaskey@3 1324 // WHILE tested in caller.
jlaskey@3 1325 next();
jlaskey@3 1326
jlaskey@3 1327 // Construct WHILE node.
lagergren@679 1328 WhileNode whileNode = new WhileNode(line, whileToken, Token.descPosition(whileToken), false);
lagergren@211 1329 lc.push(whileNode);
jlaskey@3 1330
jlaskey@3 1331 try {
jlaskey@3 1332 expect(LPAREN);
lagergren@679 1333 final int whileLine = line;
attila@963 1334 final JoinPredecessorExpression test = joinPredecessorExpression();
jlaskey@3 1335 expect(RPAREN);
lagergren@679 1336 final Block body = getStatement();
lagergren@679 1337 appendStatement(whileNode =
lagergren@679 1338 new WhileNode(whileLine, whileToken, finish, false).
lagergren@679 1339 setTest(lc, test).
lagergren@679 1340 setBody(lc, body));
jlaskey@3 1341 } finally {
lagergren@211 1342 lc.pop(whileNode);
jlaskey@3 1343 }
jlaskey@3 1344 }
jlaskey@3 1345
jlaskey@3 1346 /**
jlaskey@3 1347 * ...IterationStatement :
jlaskey@3 1348 * ...
jlaskey@3 1349 * do Statement while( Expression ) ;
jlaskey@3 1350 * ...
jlaskey@3 1351 *
jlaskey@3 1352 * See 12.6
jlaskey@3 1353 *
jlaskey@3 1354 * Parse DO WHILE statement.
jlaskey@3 1355 */
jlaskey@3 1356 private void doStatement() {
jlaskey@3 1357 // Capture DO token.
jlaskey@3 1358 final long doToken = token;
jlaskey@3 1359 // DO tested in the caller.
jlaskey@3 1360 next();
jlaskey@3 1361
lagergren@679 1362 WhileNode doWhileNode = new WhileNode(-1, doToken, Token.descPosition(doToken), true);
lagergren@211 1363 lc.push(doWhileNode);
jlaskey@3 1364
jlaskey@3 1365 try {
jlaskey@3 1366 // Get DO body.
lagergren@679 1367 final Block body = getStatement();
jlaskey@3 1368
jlaskey@3 1369 expect(WHILE);
jlaskey@3 1370 expect(LPAREN);
lagergren@679 1371 final int doLine = line;
attila@963 1372 final JoinPredecessorExpression test = joinPredecessorExpression();
jlaskey@3 1373 expect(RPAREN);
jlaskey@3 1374
jlaskey@3 1375 if (type == SEMICOLON) {
jlaskey@3 1376 endOfLine();
jlaskey@3 1377 }
jlaskey@3 1378 doWhileNode.setFinish(finish);
lagergren@679 1379
lagergren@679 1380 //line number is last
lagergren@679 1381 appendStatement(doWhileNode =
lagergren@679 1382 new WhileNode(doLine, doToken, finish, true).
lagergren@679 1383 setBody(lc, body).
lagergren@679 1384 setTest(lc, test));
jlaskey@3 1385 } finally {
lagergren@211 1386 lc.pop(doWhileNode);
jlaskey@3 1387 }
jlaskey@3 1388 }
jlaskey@3 1389
jlaskey@3 1390 /**
jlaskey@3 1391 * ContinueStatement :
jlaskey@3 1392 * continue Identifier? ; // [no LineTerminator here]
jlaskey@3 1393 *
jlaskey@3 1394 * See 12.7
jlaskey@3 1395 *
jlaskey@3 1396 * Parse CONTINUE statement.
jlaskey@3 1397 */
jlaskey@3 1398 private void continueStatement() {
jlaskey@3 1399 // Capture CONTINUE token.
lagergren@253 1400 final int continueLine = line;
jlaskey@3 1401 final long continueToken = token;
jlaskey@3 1402 // CONTINUE tested in caller.
jlaskey@3 1403 nextOrEOL();
jlaskey@3 1404
jlaskey@3 1405 LabelNode labelNode = null;
jlaskey@3 1406
jlaskey@3 1407 // SEMICOLON or label.
jlaskey@3 1408 switch (type) {
jlaskey@3 1409 case RBRACE:
jlaskey@3 1410 case SEMICOLON:
jlaskey@3 1411 case EOL:
sundar@387 1412 case EOF:
jlaskey@3 1413 break;
jlaskey@3 1414
jlaskey@3 1415 default:
jlaskey@3 1416 final IdentNode ident = getIdent();
lagergren@211 1417 labelNode = lc.findLabel(ident.getName());
jlaskey@3 1418
jlaskey@3 1419 if (labelNode == null) {
lagergren@211 1420 throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
jlaskey@3 1421 }
jlaskey@3 1422
jlaskey@3 1423 break;
jlaskey@3 1424 }
jlaskey@3 1425
attila@963 1426 final String labelName = labelNode == null ? null : labelNode.getLabelName();
attila@963 1427 final LoopNode targetNode = lc.getContinueTo(labelName);
jlaskey@3 1428
jlaskey@3 1429 if (targetNode == null) {
lagergren@211 1430 throw error(AbstractParser.message("illegal.continue.stmt"), continueToken);
jlaskey@3 1431 }
jlaskey@3 1432
jlaskey@3 1433 endOfLine();
jlaskey@3 1434
jlaskey@3 1435 // Construct and add CONTINUE node.
attila@963 1436 appendStatement(new ContinueNode(continueLine, continueToken, finish, labelName));
jlaskey@3 1437 }
jlaskey@3 1438
jlaskey@3 1439 /**
jlaskey@3 1440 * BreakStatement :
jlaskey@3 1441 * break Identifier? ; // [no LineTerminator here]
jlaskey@3 1442 *
jlaskey@3 1443 * See 12.8
jlaskey@3 1444 *
jlaskey@3 1445 */
jlaskey@3 1446 private void breakStatement() {
jlaskey@3 1447 // Capture BREAK token.
lagergren@253 1448 final int breakLine = line;
jlaskey@3 1449 final long breakToken = token;
jlaskey@3 1450 // BREAK tested in caller.
jlaskey@3 1451 nextOrEOL();
jlaskey@3 1452
jlaskey@3 1453 LabelNode labelNode = null;
jlaskey@3 1454
jlaskey@3 1455 // SEMICOLON or label.
jlaskey@3 1456 switch (type) {
jlaskey@3 1457 case RBRACE:
jlaskey@3 1458 case SEMICOLON:
jlaskey@3 1459 case EOL:
sundar@387 1460 case EOF:
jlaskey@3 1461 break;
jlaskey@3 1462
jlaskey@3 1463 default:
jlaskey@3 1464 final IdentNode ident = getIdent();
lagergren@211 1465 labelNode = lc.findLabel(ident.getName());
jlaskey@3 1466
jlaskey@3 1467 if (labelNode == null) {
lagergren@211 1468 throw error(AbstractParser.message("undefined.label", ident.getName()), ident.getToken());
jlaskey@3 1469 }
jlaskey@3 1470
jlaskey@3 1471 break;
jlaskey@3 1472 }
jlaskey@3 1473
lagergren@211 1474 //either an explicit label - then get its node or just a "break" - get first breakable
lagergren@211 1475 //targetNode is what we are breaking out from.
attila@963 1476 final String labelName = labelNode == null ? null : labelNode.getLabelName();
attila@963 1477 final BreakableNode targetNode = lc.getBreakable(labelName);
jlaskey@3 1478 if (targetNode == null) {
lagergren@211 1479 throw error(AbstractParser.message("illegal.break.stmt"), breakToken);
jlaskey@3 1480 }
jlaskey@3 1481
jlaskey@3 1482 endOfLine();
jlaskey@3 1483
jlaskey@3 1484 // Construct and add BREAK node.
attila@963 1485 appendStatement(new BreakNode(breakLine, breakToken, finish, labelName));
jlaskey@3 1486 }
jlaskey@3 1487
jlaskey@3 1488 /**
jlaskey@3 1489 * ReturnStatement :
jlaskey@3 1490 * return Expression? ; // [no LineTerminator here]
jlaskey@3 1491 *
jlaskey@3 1492 * See 12.9
jlaskey@3 1493 *
jlaskey@3 1494 * Parse RETURN statement.
jlaskey@3 1495 */
jlaskey@3 1496 private void returnStatement() {
jlaskey@3 1497 // check for return outside function
lagergren@211 1498 if (lc.getCurrentFunction().getKind() == FunctionNode.Kind.SCRIPT) {
lagergren@211 1499 throw error(AbstractParser.message("invalid.return"));
jlaskey@3 1500 }
jlaskey@3 1501
jlaskey@3 1502 // Capture RETURN token.
lagergren@253 1503 final int returnLine = line;
jlaskey@3 1504 final long returnToken = token;
jlaskey@3 1505 // RETURN tested in caller.
jlaskey@3 1506 nextOrEOL();
jlaskey@3 1507
attila@430 1508 Expression expression = null;
jlaskey@3 1509
jlaskey@3 1510 // SEMICOLON or expression.
jlaskey@3 1511 switch (type) {
jlaskey@3 1512 case RBRACE:
jlaskey@3 1513 case SEMICOLON:
jlaskey@3 1514 case EOL:
sundar@387 1515 case EOF:
jlaskey@3 1516 break;
jlaskey@3 1517
jlaskey@3 1518 default:
jlaskey@3 1519 expression = expression();
jlaskey@3 1520 break;
jlaskey@3 1521 }
jlaskey@3 1522
jlaskey@3 1523 endOfLine();
jlaskey@3 1524
jlaskey@3 1525 // Construct and add RETURN node.
lagergren@253 1526 appendStatement(new ReturnNode(returnLine, returnToken, finish, expression));
jlaskey@3 1527 }
jlaskey@3 1528
jlaskey@3 1529 /**
jlaskey@3 1530 * YieldStatement :
jlaskey@3 1531 * yield Expression? ; // [no LineTerminator here]
jlaskey@3 1532 *
jlaskey@3 1533 * JavaScript 1.8
jlaskey@3 1534 *
jlaskey@3 1535 * Parse YIELD statement.
jlaskey@3 1536 */
jlaskey@3 1537 private void yieldStatement() {
jlaskey@3 1538 // Capture YIELD token.
lagergren@253 1539 final int yieldLine = line;
jlaskey@3 1540 final long yieldToken = token;
jlaskey@3 1541 // YIELD tested in caller.
jlaskey@3 1542 nextOrEOL();
jlaskey@3 1543
attila@430 1544 Expression expression = null;
jlaskey@3 1545
jlaskey@3 1546 // SEMICOLON or expression.
jlaskey@3 1547 switch (type) {
jlaskey@3 1548 case RBRACE:
jlaskey@3 1549 case SEMICOLON:
jlaskey@3 1550 case EOL:
sundar@387 1551 case EOF:
jlaskey@3 1552 break;
jlaskey@3 1553
jlaskey@3 1554 default:
jlaskey@3 1555 expression = expression();
jlaskey@3 1556 break;
jlaskey@3 1557 }
jlaskey@3 1558
jlaskey@3 1559 endOfLine();
jlaskey@3 1560
jlaskey@3 1561 // Construct and add YIELD node.
lagergren@253 1562 appendStatement(new ReturnNode(yieldLine, yieldToken, finish, expression));
jlaskey@3 1563 }
jlaskey@3 1564
jlaskey@3 1565 /**
jlaskey@3 1566 * WithStatement :
jlaskey@3 1567 * with ( Expression ) Statement
jlaskey@3 1568 *
jlaskey@3 1569 * See 12.10
jlaskey@3 1570 *
jlaskey@3 1571 * Parse WITH statement.
jlaskey@3 1572 */
jlaskey@3 1573 private void withStatement() {
jlaskey@3 1574 // Capture WITH token.
lagergren@253 1575 final int withLine = line;
jlaskey@3 1576 final long withToken = token;
jlaskey@3 1577 // WITH tested in caller.
jlaskey@3 1578 next();
jlaskey@3 1579
jlaskey@3 1580 // ECMA 12.10.1 strict mode restrictions
jlaskey@3 1581 if (isStrictMode) {
lagergren@211 1582 throw error(AbstractParser.message("strict.no.with"), withToken);
jlaskey@3 1583 }
jlaskey@3 1584
jlaskey@3 1585 // Get WITH expression.
lagergren@253 1586 WithNode withNode = new WithNode(withLine, withToken, finish);
lagergren@211 1587
lagergren@211 1588 try {
lagergren@211 1589 lc.push(withNode);
lagergren@211 1590 expect(LPAREN);
lagergren@211 1591 withNode = withNode.setExpression(lc, expression());
lagergren@211 1592 expect(RPAREN);
lagergren@211 1593 withNode = withNode.setBody(lc, getStatement());
lagergren@211 1594 } finally {
lagergren@211 1595 lc.pop(withNode);
attila@144 1596 }
jlaskey@3 1597
lagergren@211 1598 appendStatement(withNode);
jlaskey@3 1599 }
jlaskey@3 1600
jlaskey@3 1601 /**
jlaskey@3 1602 * SwitchStatement :
jlaskey@3 1603 * switch ( Expression ) CaseBlock
jlaskey@3 1604 *
jlaskey@3 1605 * CaseBlock :
jlaskey@3 1606 * { CaseClauses? }
jlaskey@3 1607 * { CaseClauses? DefaultClause CaseClauses }
jlaskey@3 1608 *
jlaskey@3 1609 * CaseClauses :
jlaskey@3 1610 * CaseClause
jlaskey@3 1611 * CaseClauses CaseClause
jlaskey@3 1612 *
jlaskey@3 1613 * CaseClause :
jlaskey@3 1614 * case Expression : StatementList?
jlaskey@3 1615 *
jlaskey@3 1616 * DefaultClause :
jlaskey@3 1617 * default : StatementList?
jlaskey@3 1618 *
jlaskey@3 1619 * See 12.11
jlaskey@3 1620 *
jlaskey@3 1621 * Parse SWITCH statement.
jlaskey@3 1622 */
jlaskey@3 1623 private void switchStatement() {
lagergren@253 1624 final int switchLine = line;
jlaskey@3 1625 final long switchToken = token;
jlaskey@3 1626 // SWITCH tested in caller.
jlaskey@3 1627 next();
jlaskey@3 1628
jlaskey@3 1629 // Create and add switch statement.
lagergren@253 1630 SwitchNode switchNode = new SwitchNode(switchLine, switchToken, Token.descPosition(switchToken), null, new ArrayList<CaseNode>(), null);
lagergren@211 1631 lc.push(switchNode);
jlaskey@3 1632
jlaskey@3 1633 try {
jlaskey@3 1634 expect(LPAREN);
lagergren@211 1635 switchNode = switchNode.setExpression(lc, expression());
jlaskey@3 1636 expect(RPAREN);
jlaskey@3 1637
jlaskey@3 1638 expect(LBRACE);
jlaskey@3 1639
jlaskey@3 1640 // Prepare to accumulate cases.
jlaskey@3 1641 final List<CaseNode> cases = new ArrayList<>();
jlaskey@3 1642 CaseNode defaultCase = null;
jlaskey@3 1643
jlaskey@3 1644 while (type != RBRACE) {
jlaskey@3 1645 // Prepare for next case.
attila@430 1646 Expression caseExpression = null;
jlaskey@3 1647 final long caseToken = token;
jlaskey@3 1648
jlaskey@3 1649 switch (type) {
jlaskey@3 1650 case CASE:
jlaskey@3 1651 next();
jlaskey@3 1652 caseExpression = expression();
jlaskey@3 1653 break;
jlaskey@3 1654
jlaskey@3 1655 case DEFAULT:
jlaskey@3 1656 if (defaultCase != null) {
lagergren@211 1657 throw error(AbstractParser.message("duplicate.default.in.switch"));
jlaskey@3 1658 }
jlaskey@3 1659 next();
jlaskey@3 1660 break;
jlaskey@3 1661
jlaskey@3 1662 default:
jlaskey@3 1663 // Force an error.
jlaskey@3 1664 expect(CASE);
jlaskey@3 1665 break;
jlaskey@3 1666 }
jlaskey@3 1667
jlaskey@3 1668 expect(COLON);
jlaskey@3 1669
jlaskey@3 1670 // Get CASE body.
jlaskey@3 1671 final Block statements = getBlock(false);
lagergren@252 1672 final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements);
jlaskey@9 1673 statements.setFinish(finish);
jlaskey@3 1674
jlaskey@3 1675 if (caseExpression == null) {
jlaskey@3 1676 defaultCase = caseNode;
jlaskey@3 1677 }
jlaskey@3 1678
jlaskey@3 1679 cases.add(caseNode);
jlaskey@3 1680 }
jlaskey@3 1681
lagergren@211 1682 switchNode = switchNode.setCases(lc, cases, defaultCase);
jlaskey@3 1683 next();
jlaskey@3 1684 switchNode.setFinish(finish);
jlaskey@3 1685
lagergren@211 1686 appendStatement(switchNode);
jlaskey@3 1687 } finally {
lagergren@211 1688 lc.pop(switchNode);
jlaskey@3 1689 }
jlaskey@3 1690 }
jlaskey@3 1691
jlaskey@3 1692 /**
jlaskey@3 1693 * LabelledStatement :
jlaskey@3 1694 * Identifier : Statement
jlaskey@3 1695 *
jlaskey@3 1696 * See 12.12
jlaskey@3 1697 *
jlaskey@3 1698 * Parse label statement.
jlaskey@3 1699 */
jlaskey@3 1700 private void labelStatement() {
jlaskey@3 1701 // Capture label token.
jlaskey@3 1702 final long labelToken = token;
jlaskey@3 1703 // Get label ident.
jlaskey@3 1704 final IdentNode ident = getIdent();
jlaskey@3 1705
jlaskey@3 1706 expect(COLON);
jlaskey@3 1707
lagergren@211 1708 if (lc.findLabel(ident.getName()) != null) {
lagergren@211 1709 throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken);
jlaskey@3 1710 }
jlaskey@3 1711
attila@963 1712 LabelNode labelNode = new LabelNode(line, labelToken, finish, ident.getName(), null);
jlaskey@3 1713 try {
lagergren@211 1714 lc.push(labelNode);
lagergren@211 1715 labelNode = labelNode.setBody(lc, getStatement());
jlaskey@3 1716 labelNode.setFinish(finish);
lagergren@211 1717 appendStatement(labelNode);
jlaskey@3 1718 } finally {
lagergren@211 1719 assert lc.peek() instanceof LabelNode;
lagergren@211 1720 lc.pop(labelNode);
jlaskey@3 1721 }
jlaskey@3 1722 }
jlaskey@3 1723
jlaskey@3 1724 /**
jlaskey@3 1725 * ThrowStatement :
jlaskey@3 1726 * throw Expression ; // [no LineTerminator here]
jlaskey@3 1727 *
jlaskey@3 1728 * See 12.13
jlaskey@3 1729 *
jlaskey@3 1730 * Parse throw statement.
jlaskey@3 1731 */
jlaskey@3 1732 private void throwStatement() {
jlaskey@3 1733 // Capture THROW token.
lagergren@253 1734 final int throwLine = line;
jlaskey@3 1735 final long throwToken = token;
jlaskey@3 1736 // THROW tested in caller.
jlaskey@3 1737 nextOrEOL();
jlaskey@3 1738
attila@430 1739 Expression expression = null;
jlaskey@3 1740
jlaskey@3 1741 // SEMICOLON or expression.
jlaskey@3 1742 switch (type) {
jlaskey@3 1743 case RBRACE:
jlaskey@3 1744 case SEMICOLON:
jlaskey@3 1745 case EOL:
jlaskey@3 1746 break;
jlaskey@3 1747
jlaskey@3 1748 default:
jlaskey@3 1749 expression = expression();
jlaskey@3 1750 break;
jlaskey@3 1751 }
jlaskey@3 1752
jlaskey@3 1753 if (expression == null) {
lagergren@211 1754 throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
jlaskey@3 1755 }
jlaskey@3 1756
jlaskey@3 1757 endOfLine();
jlaskey@3 1758
attila@963 1759 appendStatement(new ThrowNode(throwLine, throwToken, finish, expression, false));
jlaskey@3 1760 }
jlaskey@3 1761
jlaskey@3 1762 /**
jlaskey@3 1763 * TryStatement :
jlaskey@3 1764 * try Block Catch
jlaskey@3 1765 * try Block Finally
jlaskey@3 1766 * try Block Catch Finally
jlaskey@3 1767 *
jlaskey@3 1768 * Catch :
jlaskey@3 1769 * catch( Identifier if Expression ) Block
jlaskey@3 1770 * catch( Identifier ) Block
jlaskey@3 1771 *
jlaskey@3 1772 * Finally :
jlaskey@3 1773 * finally Block
jlaskey@3 1774 *
jlaskey@3 1775 * See 12.14
jlaskey@3 1776 *
jlaskey@3 1777 * Parse TRY statement.
jlaskey@3 1778 */
jlaskey@3 1779 private void tryStatement() {
jlaskey@3 1780 // Capture TRY token.
lagergren@253 1781 final int tryLine = line;
jlaskey@3 1782 final long tryToken = token;
jlaskey@3 1783 // TRY tested in caller.
jlaskey@3 1784 next();
jlaskey@3 1785
hannesw@95 1786 // Container block needed to act as target for labeled break statements
attila@430 1787 final int startLine = line;
lagergren@211 1788 Block outer = newBlock();
hannesw@95 1789
jlaskey@3 1790 // Create try.
jlaskey@3 1791
jlaskey@3 1792 try {
lagergren@211 1793 final Block tryBody = getBlock(true);
jlaskey@3 1794 final List<Block> catchBlocks = new ArrayList<>();
jlaskey@3 1795
jlaskey@3 1796 while (type == CATCH) {
lagergren@253 1797 final int catchLine = line;
jlaskey@3 1798 final long catchToken = token;
jlaskey@3 1799 next();
jlaskey@3 1800 expect(LPAREN);
jlaskey@3 1801 final IdentNode exception = getIdent();
jlaskey@3 1802
jlaskey@3 1803 // ECMA 12.4.1 strict mode restrictions
jlaskey@3 1804 verifyStrictIdent(exception, "catch argument");
jlaskey@3 1805
sundar@777 1806 // Nashorn extension: catch clause can have optional
sundar@777 1807 // condition. So, a single try can have more than one
sundar@777 1808 // catch clause each with it's own condition.
attila@430 1809 final Expression ifExpression;
sundar@777 1810 if (!env._no_syntax_extensions && type == IF) {
jlaskey@3 1811 next();
jlaskey@3 1812 // Get the exception condition.
jlaskey@3 1813 ifExpression = expression();
attila@430 1814 } else {
attila@430 1815 ifExpression = null;
jlaskey@3 1816 }
jlaskey@3 1817
jlaskey@3 1818 expect(RPAREN);
jlaskey@3 1819
lagergren@211 1820 Block catchBlock = newBlock();
jlaskey@3 1821 try {
jlaskey@3 1822 // Get CATCH body.
jlaskey@3 1823 final Block catchBody = getBlock(true);
attila@963 1824 final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, false);
lagergren@211 1825 appendStatement(catchNode);
lagergren@211 1826 } finally {
lagergren@211 1827 catchBlock = restoreBlock(catchBlock);
jlaskey@3 1828 catchBlocks.add(catchBlock);
jlaskey@3 1829 }
jlaskey@3 1830
jlaskey@3 1831 // If unconditional catch then should to be the end.
jlaskey@3 1832 if (ifExpression == null) {
jlaskey@3 1833 break;
jlaskey@3 1834 }
jlaskey@3 1835 }
jlaskey@3 1836
jlaskey@3 1837 // Prepare to capture finally statement.
jlaskey@3 1838 Block finallyStatements = null;
jlaskey@3 1839
jlaskey@3 1840 if (type == FINALLY) {
jlaskey@3 1841 next();
jlaskey@3 1842 finallyStatements = getBlock(true);
jlaskey@3 1843 }
jlaskey@3 1844
jlaskey@3 1845 // Need at least one catch or a finally.
jlaskey@3 1846 if (catchBlocks.isEmpty() && finallyStatements == null) {
lagergren@211 1847 throw error(AbstractParser.message("missing.catch.or.finally"), tryToken);
jlaskey@3 1848 }
jlaskey@3 1849
lagergren@253 1850 final TryNode tryNode = new TryNode(tryLine, tryToken, Token.descPosition(tryToken), tryBody, catchBlocks, finallyStatements);
lagergren@211 1851 // Add try.
lagergren@211 1852 assert lc.peek() == outer;
lagergren@211 1853 appendStatement(tryNode);
lagergren@211 1854
jlaskey@3 1855 tryNode.setFinish(finish);
hannesw@95 1856 outer.setFinish(finish);
jlaskey@3 1857
jlaskey@3 1858 } finally {
lagergren@211 1859 outer = restoreBlock(outer);
jlaskey@3 1860 }
hannesw@95 1861
attila@430 1862 appendStatement(new BlockStatement(startLine, outer));
jlaskey@3 1863 }
jlaskey@3 1864
jlaskey@3 1865 /**
jlaskey@3 1866 * DebuggerStatement :
jlaskey@3 1867 * debugger ;
jlaskey@3 1868 *
jlaskey@3 1869 * See 12.15
jlaskey@3 1870 *
jlaskey@3 1871 * Parse debugger statement.
jlaskey@3 1872 */
jlaskey@3 1873 private void debuggerStatement() {
jlaskey@3 1874 // Capture DEBUGGER token.
lagergren@253 1875 final int debuggerLine = line;
jlaskey@3 1876 final long debuggerToken = token;
jlaskey@3 1877 // DEBUGGER tested in caller.
jlaskey@3 1878 next();
jlaskey@3 1879 endOfLine();
attila@430 1880 appendStatement(new ExpressionStatement(debuggerLine, debuggerToken, finish, new RuntimeNode(debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList<Expression>())));
jlaskey@3 1881 }
jlaskey@3 1882
jlaskey@3 1883 /**
jlaskey@3 1884 * PrimaryExpression :
jlaskey@3 1885 * this
jlaskey@3 1886 * Identifier
jlaskey@3 1887 * Literal
jlaskey@3 1888 * ArrayLiteral
jlaskey@3 1889 * ObjectLiteral
jlaskey@3 1890 * ( Expression )
jlaskey@3 1891 *
jlaskey@3 1892 * See 11.1
jlaskey@3 1893 *
jlaskey@3 1894 * Parse primary expression.
jlaskey@3 1895 * @return Expression node.
jlaskey@3 1896 */
jlaskey@3 1897 @SuppressWarnings("fallthrough")
attila@430 1898 private Expression primaryExpression() {
jlaskey@3 1899 // Capture first token.
lagergren@253 1900 final int primaryLine = line;
jlaskey@3 1901 final long primaryToken = token;
jlaskey@3 1902
jlaskey@3 1903 switch (type) {
jlaskey@3 1904 case THIS:
jlaskey@3 1905 final String name = type.getName();
jlaskey@3 1906 next();
hannesw@769 1907 lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_THIS);
lagergren@252 1908 return new IdentNode(primaryToken, finish, name);
jlaskey@3 1909 case IDENT:
jlaskey@3 1910 final IdentNode ident = getIdent();
jlaskey@3 1911 if (ident == null) {
jlaskey@3 1912 break;
jlaskey@3 1913 }
jlaskey@3 1914 detectSpecialProperty(ident);
jlaskey@3 1915 return ident;
jlaskey@3 1916 case OCTAL:
jlaskey@3 1917 if (isStrictMode) {
lagergren@211 1918 throw error(AbstractParser.message("strict.no.octal"), token);
jlaskey@3 1919 }
jlaskey@3 1920 case STRING:
jlaskey@3 1921 case ESCSTRING:
jlaskey@3 1922 case DECIMAL:
jlaskey@3 1923 case HEXADECIMAL:
jlaskey@3 1924 case FLOATING:
jlaskey@3 1925 case REGEX:
jlaskey@3 1926 case XML:
jlaskey@3 1927 return getLiteral();
jlaskey@67 1928 case EXECSTRING:
lagergren@253 1929 return execString(primaryLine, primaryToken);
jlaskey@3 1930 case FALSE:
jlaskey@3 1931 next();
lagergren@252 1932 return LiteralNode.newInstance(primaryToken, finish, false);
jlaskey@3 1933 case TRUE:
jlaskey@3 1934 next();
lagergren@252 1935 return LiteralNode.newInstance(primaryToken, finish, true);
jlaskey@3 1936 case NULL:
jlaskey@3 1937 next();
lagergren@252 1938 return LiteralNode.newInstance(primaryToken, finish);
jlaskey@3 1939 case LBRACKET:
jlaskey@3 1940 return arrayLiteral();
jlaskey@3 1941 case LBRACE:
jlaskey@3 1942 return objectLiteral();
jlaskey@3 1943 case LPAREN:
jlaskey@3 1944 next();
jlaskey@3 1945
attila@430 1946 final Expression expression = expression();
jlaskey@3 1947
jlaskey@3 1948 expect(RPAREN);
jlaskey@3 1949
jlaskey@3 1950 return expression;
jlaskey@3 1951
jlaskey@3 1952 default:
jlaskey@3 1953 // In this context some operator tokens mark the start of a literal.
sundar@433 1954 if (lexer.scanLiteral(primaryToken, type, lineInfoReceiver)) {
jlaskey@3 1955 next();
jlaskey@3 1956 return getLiteral();
jlaskey@3 1957 }
jlaskey@3 1958 if (isNonStrictModeIdent()) {
jlaskey@3 1959 return getIdent();
jlaskey@3 1960 }
jlaskey@3 1961 break;
jlaskey@3 1962 }
jlaskey@3 1963
jlaskey@3 1964 return null;
jlaskey@3 1965 }
jlaskey@3 1966
jlaskey@67 1967 /**
jlaskey@67 1968 * Convert execString to a call to $EXEC.
jlaskey@67 1969 *
jlaskey@67 1970 * @param primaryToken Original string token.
jlaskey@67 1971 * @return callNode to $EXEC.
jlaskey@67 1972 */
attila@430 1973 CallNode execString(final int primaryLine, final long primaryToken) {
jlaskey@67 1974 // Synthesize an ident to call $EXEC.
lagergren@252 1975 final IdentNode execIdent = new IdentNode(primaryToken, finish, ScriptingFunctions.EXEC_NAME);
jlaskey@67 1976 // Skip over EXECSTRING.
jlaskey@67 1977 next();
jlaskey@67 1978 // Set up argument list for call.
jlaskey@67 1979 // Skip beginning of edit string expression.
jlaskey@67 1980 expect(LBRACE);
jlaskey@67 1981 // Add the following expression to arguments.
attila@430 1982 final List<Expression> arguments = Collections.singletonList(expression());
jlaskey@67 1983 // Skip ending of edit string expression.
jlaskey@67 1984 expect(RBRACE);
jlaskey@67 1985
attila@963 1986 return new CallNode(primaryLine, primaryToken, finish, execIdent, arguments, false);
jlaskey@67 1987 }
jlaskey@3 1988
jlaskey@3 1989 /**
jlaskey@3 1990 * ArrayLiteral :
jlaskey@3 1991 * [ Elision? ]
jlaskey@3 1992 * [ ElementList ]
jlaskey@3 1993 * [ ElementList , Elision? ]
jlaskey@3 1994 * [ expression for (LeftHandExpression in expression) ( (if ( Expression ) )? ]
jlaskey@3 1995 *
jlaskey@3 1996 * ElementList : Elision? AssignmentExpression
jlaskey@3 1997 * ElementList , Elision? AssignmentExpression
jlaskey@3 1998 *
jlaskey@3 1999 * Elision :
jlaskey@3 2000 * ,
jlaskey@3 2001 * Elision ,
jlaskey@3 2002 *
jlaskey@3 2003 * See 12.1.4
jlaskey@3 2004 * JavaScript 1.8
jlaskey@3 2005 *
jlaskey@3 2006 * Parse array literal.
jlaskey@3 2007 * @return Expression node.
jlaskey@3 2008 */
attila@430 2009 private LiteralNode<Expression[]> arrayLiteral() {
jlaskey@3 2010 // Capture LBRACKET token.
jlaskey@3 2011 final long arrayToken = token;
jlaskey@3 2012 // LBRACKET tested in caller.
jlaskey@3 2013 next();
jlaskey@3 2014
jlaskey@3 2015 // Prepare to accummulating elements.
attila@430 2016 final List<Expression> elements = new ArrayList<>();
jlaskey@3 2017 // Track elisions.
jlaskey@3 2018 boolean elision = true;
jlaskey@3 2019 loop:
jlaskey@3 2020 while (true) {
jlaskey@3 2021 switch (type) {
jlaskey@3 2022 case RBRACKET:
jlaskey@3 2023 next();
jlaskey@3 2024
jlaskey@3 2025 break loop;
jlaskey@3 2026
jlaskey@3 2027 case COMMARIGHT:
jlaskey@3 2028 next();
jlaskey@3 2029
jlaskey@3 2030 // If no prior expression
jlaskey@3 2031 if (elision) {
jlaskey@3 2032 elements.add(null);
jlaskey@3 2033 }
jlaskey@3 2034
jlaskey@3 2035 elision = true;
jlaskey@3 2036
jlaskey@3 2037 break;
jlaskey@3 2038
jlaskey@3 2039 default:
lagergren@137 2040 if (!elision) {
lagergren@211 2041 throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
jlaskey@3 2042 }
jlaskey@3 2043 // Add expression element.
attila@430 2044 final Expression expression = assignmentExpression(false);
jlaskey@3 2045
jlaskey@3 2046 if (expression != null) {
jlaskey@3 2047 elements.add(expression);
jlaskey@3 2048 } else {
jlaskey@3 2049 expect(RBRACKET);
jlaskey@3 2050 }
jlaskey@3 2051
jlaskey@3 2052 elision = false;
jlaskey@3 2053 break;
jlaskey@3 2054 }
jlaskey@3 2055 }
jlaskey@3 2056
lagergren@252 2057 return LiteralNode.newInstance(arrayToken, finish, elements);
jlaskey@3 2058 }
jlaskey@3 2059
jlaskey@3 2060 /**
jlaskey@3 2061 * ObjectLiteral :
jlaskey@3 2062 * { }
jlaskey@3 2063 * { PropertyNameAndValueList } { PropertyNameAndValueList , }
jlaskey@3 2064 *
jlaskey@3 2065 * PropertyNameAndValueList :
jlaskey@3 2066 * PropertyAssignment
jlaskey@3 2067 * PropertyNameAndValueList , PropertyAssignment
jlaskey@3 2068 *
jlaskey@3 2069 * See 11.1.5
jlaskey@3 2070 *
jlaskey@3 2071 * Parse an object literal.
jlaskey@3 2072 * @return Expression node.
jlaskey@3 2073 */
attila@430 2074 private ObjectNode objectLiteral() {
jlaskey@3 2075 // Capture LBRACE token.
jlaskey@3 2076 final long objectToken = token;
jlaskey@3 2077 // LBRACE tested in caller.
jlaskey@3 2078 next();
jlaskey@3 2079
jlaskey@3 2080 // Object context.
jlaskey@3 2081 // Prepare to accumulate elements.
hannesw@440 2082 final List<PropertyNode> elements = new ArrayList<>();
hannesw@440 2083 final Map<String, Integer> map = new HashMap<>();
jlaskey@3 2084
lagergren@137 2085 // Create a block for the object literal.
hannesw@440 2086 boolean commaSeen = true;
jlaskey@3 2087 loop:
hannesw@440 2088 while (true) {
hannesw@440 2089 switch (type) {
jlaskey@3 2090 case RBRACE:
jlaskey@3 2091 next();
jlaskey@3 2092 break loop;
jlaskey@3 2093
jlaskey@3 2094 case COMMARIGHT:
sundar@391 2095 if (commaSeen) {
sundar@391 2096 throw error(AbstractParser.message("expected.property.id", type.getNameOrType()));
sundar@391 2097 }
jlaskey@3 2098 next();
jlaskey@3 2099 commaSeen = true;
jlaskey@3 2100 break;
jlaskey@3 2101
jlaskey@3 2102 default:
lagergren@137 2103 if (!commaSeen) {
lagergren@211 2104 throw error(AbstractParser.message("expected.comma", type.getNameOrType()));
lagergren@211 2105 }
lagergren@211 2106
lagergren@211 2107 commaSeen = false;
lagergren@211 2108 // Get and add the next property.
lagergren@211 2109 final PropertyNode property = propertyAssignment();
lagergren@211 2110 final String key = property.getKeyName();
hannesw@440 2111 final Integer existing = map.get(key);
hannesw@440 2112
hannesw@440 2113 if (existing == null) {
hannesw@440 2114 map.put(key, elements.size());
hannesw@440 2115 elements.add(property);
lagergren@211 2116 break;
lagergren@211 2117 }
lagergren@211 2118
hannesw@440 2119 final PropertyNode existingProperty = elements.get(existing);
hannesw@440 2120
lagergren@137 2121 // ECMA section 11.1.5 Object Initialiser
lagergren@137 2122 // point # 4 on property assignment production
attila@430 2123 final Expression value = property.getValue();
lagergren@211 2124 final FunctionNode getter = property.getGetter();
lagergren@211 2125 final FunctionNode setter = property.getSetter();
lagergren@211 2126
attila@430 2127 final Expression prevValue = existingProperty.getValue();
lagergren@211 2128 final FunctionNode prevGetter = existingProperty.getGetter();
lagergren@211 2129 final FunctionNode prevSetter = existingProperty.getSetter();
lagergren@137 2130
lagergren@137 2131 // ECMA 11.1.5 strict mode restrictions
hannesw@440 2132 if (isStrictMode && value != null && prevValue != null) {
hannesw@440 2133 throw error(AbstractParser.message("property.redefinition", key), property.getToken());
hannesw@440 2134 }
hannesw@440 2135
hannesw@440 2136 final boolean isPrevAccessor = prevGetter != null || prevSetter != null;
hannesw@440 2137 final boolean isAccessor = getter != null || setter != null;
hannesw@440 2138
hannesw@440 2139 // data property redefined as accessor property
hannesw@440 2140 if (prevValue != null && isAccessor) {
hannesw@440 2141 throw error(AbstractParser.message("property.redefinition", key), property.getToken());
hannesw@440 2142 }
hannesw@440 2143
hannesw@440 2144 // accessor property redefined as data
hannesw@440 2145 if (isPrevAccessor && value != null) {
hannesw@440 2146 throw error(AbstractParser.message("property.redefinition", key), property.getToken());
hannesw@440 2147 }
hannesw@440 2148
hannesw@440 2149 if (isAccessor && isPrevAccessor) {
hannesw@440 2150 if (getter != null && prevGetter != null ||
hannesw@440 2151 setter != null && prevSetter != null) {
hannesw@440 2152 throw error(AbstractParser.message("property.redefinition", key), property.getToken());
jlaskey@3 2153 }
lagergren@137 2154 }
lagergren@137 2155
lagergren@137 2156 if (value != null) {
hannesw@440 2157 elements.add(property);
hannesw@440 2158 } else if (getter != null) {
hannesw@440 2159 elements.set(existing, existingProperty.setGetter(getter));
hannesw@440 2160 } else if (setter != null) {
hannesw@440 2161 elements.set(existing, existingProperty.setSetter(setter));
lagergren@137 2162 }
lagergren@211 2163 break;
jlaskey@3 2164 }
jlaskey@3 2165 }
jlaskey@3 2166
hannesw@440 2167 return new ObjectNode(objectToken, finish, elements);
jlaskey@3 2168 }
jlaskey@3 2169
jlaskey@3 2170 /**
jlaskey@3 2171 * PropertyName :
jlaskey@3 2172 * IdentifierName
jlaskey@3 2173 * StringLiteral
jlaskey@3 2174 * NumericLiteral
jlaskey@3 2175 *
jlaskey@3 2176 * See 11.1.5
jlaskey@3 2177 *
jlaskey@3 2178 * @return PropertyName node
jlaskey@3 2179 */
jlaskey@3 2180 @SuppressWarnings("fallthrough")
jlaskey@3 2181 private PropertyKey propertyName() {
jlaskey@3 2182 switch (type) {
jlaskey@3 2183 case IDENT:
attila@430 2184 return getIdent().setIsPropertyName();
jlaskey@3 2185 case OCTAL:
jlaskey@3 2186 if (isStrictMode) {
lagergren@211 2187 throw error(AbstractParser.message("strict.no.octal"), token);
jlaskey@3 2188 }
jlaskey@3 2189 case STRING:
jlaskey@3 2190 case ESCSTRING:
jlaskey@3 2191 case DECIMAL:
jlaskey@3 2192 case HEXADECIMAL:
jlaskey@3 2193 case FLOATING:
jlaskey@3 2194 return getLiteral();
jlaskey@3 2195 default:
sundar@540 2196 return getIdentifierName().setIsPropertyName();
jlaskey@3 2197 }
jlaskey@3 2198 }
jlaskey@3 2199
jlaskey@3 2200 /**
jlaskey@3 2201 * PropertyAssignment :
jlaskey@3 2202 * PropertyName : AssignmentExpression
jlaskey@3 2203 * get PropertyName ( ) { FunctionBody }
jlaskey@3 2204 * set PropertyName ( PropertySetParameterList ) { FunctionBody }
jlaskey@3 2205 *
jlaskey@3 2206 * PropertySetParameterList :
jlaskey@3 2207 * Identifier
jlaskey@3 2208 *
jlaskey@3 2209 * PropertyName :
jlaskey@3 2210 * IdentifierName
jlaskey@3 2211 * StringLiteral
jlaskey@3 2212 * NumericLiteral
jlaskey@3 2213 *
jlaskey@3 2214 * See 11.1.5
jlaskey@3 2215 *
jlaskey@3 2216 * Parse an object literal property.
jlaskey@3 2217 * @return Property or reference node.
jlaskey@3 2218 */
jlaskey@3 2219 private PropertyNode propertyAssignment() {
jlaskey@3 2220 // Capture firstToken.
jlaskey@3 2221 final long propertyToken = token;
attila@963 2222 final int functionLine = line;
attila@963 2223
jlaskey@3 2224 PropertyKey propertyName;
jlaskey@3 2225
jlaskey@3 2226 if (type == IDENT) {
jlaskey@3 2227 // Get IDENT.
jlaskey@3 2228 final String ident = (String)expectValue(IDENT);
jlaskey@3 2229
jlaskey@3 2230 if (type != COLON) {
sundar@615 2231 final long getSetToken = propertyToken;
jlaskey@3 2232
jlaskey@3 2233 switch (ident) {
jlaskey@3 2234 case "get":
attila@963 2235 final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine);
attila@963 2236 return new PropertyNode(propertyToken, finish, getter.ident, null, getter.functionNode, null);
jlaskey@3 2237
jlaskey@3 2238 case "set":
attila@963 2239 final PropertyFunction setter = propertySetterFunction(getSetToken, functionLine);
attila@963 2240 return new PropertyNode(propertyToken, finish, setter.ident, null, null, setter.functionNode);
jlaskey@3 2241 default:
jlaskey@3 2242 break;
jlaskey@3 2243 }
jlaskey@3 2244 }
jlaskey@3 2245
attila@975 2246 propertyName = createIdentNode(propertyToken, finish, ident).setIsPropertyName();
jlaskey@3 2247 } else {
jlaskey@3 2248 propertyName = propertyName();
jlaskey@3 2249 }
jlaskey@3 2250
jlaskey@3 2251 expect(COLON);
jlaskey@3 2252
attila@963 2253 defaultNames.push(propertyName);
attila@963 2254 try {
attila@963 2255 return new PropertyNode(propertyToken, finish, propertyName, assignmentExpression(false), null, null);
attila@963 2256 } finally {
attila@963 2257 defaultNames.pop();
attila@963 2258 }
attila@963 2259 }
attila@963 2260
attila@963 2261 private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine) {
attila@963 2262 final PropertyKey getIdent = propertyName();
attila@963 2263 final String getterName = getIdent.getPropertyName();
attila@975 2264 final IdentNode getNameNode = createIdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName));
attila@963 2265 expect(LPAREN);
attila@963 2266 expect(RPAREN);
attila@963 2267 final FunctionNode functionNode = functionBody(getSetToken, getNameNode, new ArrayList<IdentNode>(), FunctionNode.Kind.GETTER, functionLine);
attila@963 2268
attila@963 2269 return new PropertyFunction(getIdent, functionNode);
attila@963 2270 }
attila@963 2271
attila@963 2272 private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) {
attila@963 2273 final PropertyKey setIdent = propertyName();
attila@963 2274 final String setterName = setIdent.getPropertyName();
attila@975 2275 final IdentNode setNameNode = createIdentNode(((Node)setIdent).getToken(), finish, NameCodec.encode("set " + setterName));
attila@963 2276 expect(LPAREN);
attila@963 2277 // be sloppy and allow missing setter parameter even though
attila@963 2278 // spec does not permit it!
attila@963 2279 final IdentNode argIdent;
attila@963 2280 if (type == IDENT || isNonStrictModeIdent()) {
attila@963 2281 argIdent = getIdent();
attila@963 2282 verifyStrictIdent(argIdent, "setter argument");
attila@963 2283 } else {
attila@963 2284 argIdent = null;
attila@963 2285 }
attila@963 2286 expect(RPAREN);
attila@963 2287 final List<IdentNode> parameters = new ArrayList<>();
attila@963 2288 if (argIdent != null) {
attila@963 2289 parameters.add(argIdent);
attila@963 2290 }
attila@963 2291 final FunctionNode functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER, functionLine);
attila@963 2292
attila@963 2293 return new PropertyFunction(setIdent, functionNode);
attila@963 2294 }
attila@963 2295
attila@963 2296 private static class PropertyFunction {
attila@963 2297 final PropertyKey ident;
attila@963 2298 final FunctionNode functionNode;
attila@963 2299
attila@963 2300 PropertyFunction(final PropertyKey ident, final FunctionNode function) {
attila@963 2301 this.ident = ident;
attila@963 2302 this.functionNode = function;
attila@963 2303 }
lagergren@211 2304 }
lagergren@211 2305
jlaskey@3 2306 /**
jlaskey@3 2307 * LeftHandSideExpression :
jlaskey@3 2308 * NewExpression
jlaskey@3 2309 * CallExpression
jlaskey@3 2310 *
jlaskey@3 2311 * CallExpression :
jlaskey@3 2312 * MemberExpression Arguments
jlaskey@3 2313 * CallExpression Arguments
jlaskey@3 2314 * CallExpression [ Expression ]
jlaskey@3 2315 * CallExpression . IdentifierName
jlaskey@3 2316 *
jlaskey@3 2317 * See 11.2
jlaskey@3 2318 *
jlaskey@3 2319 * Parse left hand side expression.
jlaskey@3 2320 * @return Expression node.
jlaskey@3 2321 */
attila@430 2322 private Expression leftHandSideExpression() {
lagergren@253 2323 int callLine = line;
jlaskey@3 2324 long callToken = token;
jlaskey@3 2325
attila@430 2326 Expression lhs = memberExpression();
jlaskey@3 2327
jlaskey@3 2328 if (type == LPAREN) {
attila@430 2329 final List<Expression> arguments = optimizeList(argumentList());
jlaskey@3 2330
jlaskey@3 2331 // Catch special functions.
jlaskey@3 2332 if (lhs instanceof IdentNode) {
jlaskey@3 2333 detectSpecialFunction((IdentNode)lhs);
jlaskey@3 2334 }
jlaskey@3 2335
attila@963 2336 lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
jlaskey@3 2337 }
jlaskey@3 2338
jlaskey@3 2339 loop:
jlaskey@3 2340 while (true) {
jlaskey@3 2341 // Capture token.
lagergren@253 2342 callLine = line;
jlaskey@3 2343 callToken = token;
jlaskey@3 2344
jlaskey@3 2345 switch (type) {
jlaskey@3 2346 case LPAREN:
jlaskey@3 2347 // Get NEW or FUNCTION arguments.
attila@430 2348 final List<Expression> arguments = optimizeList(argumentList());
jlaskey@3 2349
jlaskey@3 2350 // Create call node.
attila@963 2351 lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
jlaskey@3 2352
jlaskey@3 2353 break;
jlaskey@3 2354
jlaskey@3 2355 case LBRACKET:
jlaskey@3 2356 next();
jlaskey@3 2357
jlaskey@3 2358 // Get array index.
attila@430 2359 final Expression rhs = expression();
jlaskey@3 2360
jlaskey@3 2361 expect(RBRACKET);
jlaskey@3 2362
jlaskey@3 2363 // Create indexing node.
lagergren@252 2364 lhs = new IndexNode(callToken, finish, lhs, rhs);
jlaskey@3 2365
jlaskey@3 2366 break;
jlaskey@3 2367
jlaskey@3 2368 case PERIOD:
jlaskey@3 2369 next();
jlaskey@3 2370
jlaskey@3 2371 final IdentNode property = getIdentifierName();
jlaskey@3 2372
jlaskey@3 2373 // Create property access node.
attila@963 2374 lhs = new AccessNode(callToken, finish, lhs, property.getName());
jlaskey@3 2375
jlaskey@3 2376 break;
jlaskey@3 2377
jlaskey@3 2378 default:
jlaskey@3 2379 break loop;
jlaskey@3 2380 }
jlaskey@3 2381 }
jlaskey@3 2382
jlaskey@3 2383 return lhs;
jlaskey@3 2384 }
jlaskey@3 2385
jlaskey@3 2386 /**
jlaskey@3 2387 * NewExpression :
jlaskey@3 2388 * MemberExpression
jlaskey@3 2389 * new NewExpression
jlaskey@3 2390 *
jlaskey@3 2391 * See 11.2
jlaskey@3 2392 *
jlaskey@3 2393 * Parse new expression.
jlaskey@3 2394 * @return Expression node.
jlaskey@3 2395 */
attila@430 2396 private Expression newExpression() {
jlaskey@3 2397 final long newToken = token;
jlaskey@3 2398 // NEW is tested in caller.
jlaskey@3 2399 next();
jlaskey@3 2400
jlaskey@3 2401 // Get function base.
lagergren@253 2402 final int callLine = line;
attila@430 2403 final Expression constructor = memberExpression();
jlaskey@3 2404 if (constructor == null) {
jlaskey@3 2405 return null;
jlaskey@3 2406 }
jlaskey@3 2407 // Get arguments.
attila@430 2408 ArrayList<Expression> arguments;
jlaskey@3 2409
jlaskey@3 2410 // Allow for missing arguments.
jlaskey@3 2411 if (type == LPAREN) {
jlaskey@3 2412 arguments = argumentList();
jlaskey@3 2413 } else {
jlaskey@3 2414 arguments = new ArrayList<>();
jlaskey@3 2415 }
jlaskey@3 2416
sundar@52 2417 // Nashorn extension: This is to support the following interface implementation
sundar@52 2418 // syntax:
jlaskey@3 2419 //
jlaskey@3 2420 // var r = new java.lang.Runnable() {
jlaskey@3 2421 // run: function() { println("run"); }
jlaskey@3 2422 // };
jlaskey@3 2423 //
jlaskey@3 2424 // The object literal following the "new Constructor()" expresssion
jlaskey@3 2425 // is passed as an additional (last) argument to the constructor.
sundar@118 2426 if (!env._no_syntax_extensions && type == LBRACE) {
jlaskey@3 2427 arguments.add(objectLiteral());
jlaskey@3 2428 }
jlaskey@3 2429
attila@963 2430 final CallNode callNode = new CallNode(callLine, constructor.getToken(), finish, constructor, optimizeList(arguments), true);
lagergren@252 2431
lagergren@252 2432 return new UnaryNode(newToken, callNode);
jlaskey@3 2433 }
jlaskey@3 2434
jlaskey@3 2435 /**
jlaskey@3 2436 * MemberExpression :
jlaskey@3 2437 * PrimaryExpression
jlaskey@3 2438 * FunctionExpression
jlaskey@3 2439 * MemberExpression [ Expression ]
jlaskey@3 2440 * MemberExpression . IdentifierName
jlaskey@3 2441 * new MemberExpression Arguments
jlaskey@3 2442 *
jlaskey@3 2443 * See 11.2
jlaskey@3 2444 *
jlaskey@3 2445 * Parse member expression.
jlaskey@3 2446 * @return Expression node.
jlaskey@3 2447 */
attila@430 2448 private Expression memberExpression() {
jlaskey@3 2449 // Prepare to build operation.
attila@430 2450 Expression lhs;
jlaskey@3 2451
jlaskey@3 2452 switch (type) {
jlaskey@3 2453 case NEW:
attila@963 2454 // Get new expression.
jlaskey@3 2455 lhs = newExpression();
jlaskey@3 2456 break;
jlaskey@3 2457
jlaskey@3 2458 case FUNCTION:
jlaskey@3 2459 // Get function expression.
attila@144 2460 lhs = functionExpression(false, false);
jlaskey@3 2461 break;
jlaskey@3 2462
jlaskey@3 2463 default:
jlaskey@3 2464 // Get primary expression.
jlaskey@3 2465 lhs = primaryExpression();
jlaskey@3 2466 break;
jlaskey@3 2467 }
jlaskey@3 2468
jlaskey@3 2469 loop:
jlaskey@3 2470 while (true) {
jlaskey@3 2471 // Capture token.
jlaskey@3 2472 final long callToken = token;
jlaskey@3 2473
jlaskey@3 2474 switch (type) {
jlaskey@3 2475 case LBRACKET:
jlaskey@3 2476 next();
jlaskey@3 2477
jlaskey@3 2478 // Get array index.
attila@430 2479 final Expression index = expression();
jlaskey@3 2480
jlaskey@3 2481 expect(RBRACKET);
jlaskey@3 2482
jlaskey@3 2483 // Create indexing node.
lagergren@252 2484 lhs = new IndexNode(callToken, finish, lhs, index);
jlaskey@3 2485
jlaskey@3 2486 break;
jlaskey@3 2487
jlaskey@3 2488 case PERIOD:
jlaskey@3 2489 if (lhs == null) {
lagergren@211 2490 throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
jlaskey@3 2491 }
jlaskey@3 2492
jlaskey@3 2493 next();
jlaskey@3 2494
jlaskey@3 2495 final IdentNode property = getIdentifierName();
jlaskey@3 2496
jlaskey@3 2497 // Create property access node.
attila@963 2498 lhs = new AccessNode(callToken, finish, lhs, property.getName());
jlaskey@3 2499
jlaskey@3 2500 break;
jlaskey@3 2501
jlaskey@3 2502 default:
jlaskey@3 2503 break loop;
jlaskey@3 2504 }
jlaskey@3 2505 }
jlaskey@3 2506
jlaskey@3 2507 return lhs;
jlaskey@3 2508 }
jlaskey@3 2509
jlaskey@3 2510 /**
jlaskey@3 2511 * Arguments :
jlaskey@3 2512 * ( )
jlaskey@3 2513 * ( ArgumentList )
jlaskey@3 2514 *
jlaskey@3 2515 * ArgumentList :
jlaskey@3 2516 * AssignmentExpression
jlaskey@3 2517 * ArgumentList , AssignmentExpression
jlaskey@3 2518 *
jlaskey@3 2519 * See 11.2
jlaskey@3 2520 *
jlaskey@3 2521 * Parse function call arguments.
jlaskey@3 2522 * @return Argument list.
jlaskey@3 2523 */
attila@430 2524 private ArrayList<Expression> argumentList() {
jlaskey@3 2525 // Prepare to accumulate list of arguments.
attila@430 2526 final ArrayList<Expression> nodeList = new ArrayList<>();
jlaskey@3 2527 // LPAREN tested in caller.
jlaskey@3 2528 next();
jlaskey@3 2529
jlaskey@3 2530 // Track commas.
jlaskey@3 2531 boolean first = true;
jlaskey@3 2532
jlaskey@3 2533 while (type != RPAREN) {
jlaskey@3 2534 // Comma prior to every argument except the first.
jlaskey@3 2535 if (!first) {
jlaskey@3 2536 expect(COMMARIGHT);
jlaskey@3 2537 } else {
jlaskey@3 2538 first = false;
jlaskey@3 2539 }
jlaskey@3 2540
jlaskey@3 2541 // Get argument expression.
jlaskey@3 2542 nodeList.add(assignmentExpression(false));
jlaskey@3 2543 }
jlaskey@3 2544
jlaskey@3 2545 expect(RPAREN);
jlaskey@3 2546 return nodeList;
attila@430 2547 }
attila@430 2548
attila@962 2549 private static <T> List<T> optimizeList(final ArrayList<T> list) {
attila@430 2550 switch(list.size()) {
attila@430 2551 case 0: {
attila@430 2552 return Collections.emptyList();
attila@430 2553 }
attila@430 2554 case 1: {
attila@430 2555 return Collections.singletonList(list.get(0));
attila@430 2556 }
attila@430 2557 default: {
attila@430 2558 list.trimToSize();
attila@430 2559 return list;
attila@430 2560 }
attila@430 2561 }
attila@430 2562 }
jlaskey@3 2563
jlaskey@3 2564 /**
jlaskey@3 2565 * FunctionDeclaration :
jlaskey@3 2566 * function Identifier ( FormalParameterList? ) { FunctionBody }
jlaskey@3 2567 *
jlaskey@3 2568 * FunctionExpression :
jlaskey@3 2569 * function Identifier? ( FormalParameterList? ) { FunctionBody }
jlaskey@3 2570 *
jlaskey@3 2571 * See 13
jlaskey@3 2572 *
jlaskey@3 2573 * Parse function declaration.
jlaskey@3 2574 * @param isStatement True if for is a statement.
jlaskey@3 2575 *
jlaskey@3 2576 * @return Expression node.
jlaskey@3 2577 */
attila@430 2578 private Expression functionExpression(final boolean isStatement, final boolean topLevel) {
jlaskey@3 2579 final long functionToken = token;
lagergren@253 2580 final int functionLine = line;
jlaskey@3 2581 // FUNCTION is tested in caller.
jlaskey@3 2582 next();
jlaskey@3 2583
jlaskey@3 2584 IdentNode name = null;
jlaskey@3 2585
jlaskey@3 2586 if (type == IDENT || isNonStrictModeIdent()) {
jlaskey@3 2587 name = getIdent();
jlaskey@3 2588 verifyStrictIdent(name, "function name");
sundar@52 2589 } else if (isStatement) {
sundar@52 2590 // Nashorn extension: anonymous function statements
sundar@351 2591 if (env._no_syntax_extensions) {
sundar@52 2592 expect(IDENT);
sundar@52 2593 }
jlaskey@3 2594 }
jlaskey@3 2595
jlaskey@3 2596 // name is null, generate anonymous name
jlaskey@3 2597 boolean isAnonymous = false;
jlaskey@3 2598 if (name == null) {
attila@963 2599 final String tmpName = getDefaultValidFunctionName(functionLine);
lagergren@252 2600 name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName);
jlaskey@3 2601 isAnonymous = true;
jlaskey@3 2602 }
jlaskey@3 2603
jlaskey@3 2604 expect(LPAREN);
jlaskey@3 2605 final List<IdentNode> parameters = formalParameterList();
jlaskey@3 2606 expect(RPAREN);
jlaskey@3 2607
attila@963 2608 FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine);
jlaskey@3 2609
attila@144 2610 if (isStatement) {
lagergren@211 2611 if (topLevel) {
lagergren@211 2612 functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED);
attila@235 2613 } else if (isStrictMode) {
attila@235 2614 throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken);
attila@235 2615 } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) {
attila@235 2616 throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here"), functionToken);
attila@235 2617 } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.WARNING) {
attila@235 2618 warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken);
attila@144 2619 }
attila@555 2620 if (isArguments(name)) {
attila@227 2621 lc.setFlag(lc.getCurrentFunction(), FunctionNode.DEFINES_ARGUMENTS);
attila@62 2622 }
jlaskey@3 2623 }
jlaskey@3 2624
jlaskey@3 2625 if (isAnonymous) {
lagergren@211 2626 functionNode = functionNode.setFlag(lc, FunctionNode.IS_ANONYMOUS);
jlaskey@3 2627 }
jlaskey@3 2628
jlaskey@3 2629 final int arity = parameters.size();
jlaskey@3 2630
lagergren@211 2631 final boolean strict = functionNode.isStrict();
jlaskey@3 2632 if (arity > 1) {
jlaskey@3 2633 final HashSet<String> parametersSet = new HashSet<>(arity);
jlaskey@3 2634
jlaskey@3 2635 for (int i = arity - 1; i >= 0; i--) {
jlaskey@3 2636 final IdentNode parameter = parameters.get(i);
jlaskey@3 2637 String parameterName = parameter.getName();
jlaskey@3 2638
attila@555 2639 if (isArguments(parameterName)) {
lagergren@211 2640 functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
jlaskey@3 2641 }
jlaskey@3 2642
jlaskey@3 2643 if (parametersSet.contains(parameterName)) {
jlaskey@3 2644 // redefinition of parameter name
jlaskey@3 2645 if (strict) {
lagergren@211 2646 throw error(AbstractParser.message("strict.param.redefinition", parameterName), parameter.getToken());
jlaskey@3 2647 }
lagergren@211 2648 // rename in non-strict mode
lagergren@211 2649 parameterName = functionNode.uniqueName(parameterName);
lagergren@211 2650 final long parameterToken = parameter.getToken();
lagergren@252 2651 parameters.set(i, new IdentNode(parameterToken, Token.descPosition(parameterToken), functionNode.uniqueName(parameterName)));
jlaskey@3 2652 }
jlaskey@3 2653
jlaskey@3 2654 parametersSet.add(parameterName);
jlaskey@3 2655 }
jlaskey@3 2656 } else if (arity == 1) {
attila@555 2657 if (isArguments(parameters.get(0))) {
lagergren@211 2658 functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS);
jlaskey@3 2659 }
jlaskey@3 2660 }
jlaskey@3 2661
jlaskey@3 2662 if (isStatement) {
lagergren@253 2663 final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, VarNode.IS_STATEMENT);
lagergren@211 2664 if (topLevel) {
attila@144 2665 functionDeclarations.add(varNode);
sundar@47 2666 } else {
lagergren@211 2667 appendStatement(varNode);
sundar@47 2668 }
jlaskey@3 2669 }
jlaskey@3 2670
attila@144 2671 return functionNode;
jlaskey@3 2672 }
jlaskey@3 2673
attila@963 2674 private String getDefaultValidFunctionName(final int functionLine) {
attila@963 2675 final String defaultFunctionName = getDefaultFunctionName();
attila@963 2676 return isValidIdentifier(defaultFunctionName) ? defaultFunctionName : ANON_FUNCTION_PREFIX.symbolName() + functionLine;
attila@963 2677 }
attila@963 2678
attila@963 2679 private static boolean isValidIdentifier(final String name) {
attila@963 2680 if(name == null || name.isEmpty()) {
attila@963 2681 return false;
attila@963 2682 }
attila@963 2683 if(!Character.isJavaIdentifierStart(name.charAt(0))) {
attila@963 2684 return false;
attila@963 2685 }
attila@963 2686 for(int i = 1; i < name.length(); ++i) {
attila@963 2687 if(!Character.isJavaIdentifierPart(name.charAt(i))) {
attila@963 2688 return false;
attila@963 2689 }
attila@963 2690 }
attila@963 2691 return true;
attila@963 2692 }
attila@963 2693
attila@963 2694 private String getDefaultFunctionName() {
attila@963 2695 if(!defaultNames.isEmpty()) {
attila@963 2696 final Object nameExpr = defaultNames.peek();
attila@963 2697 if(nameExpr instanceof PropertyKey) {
attila@963 2698 markDefaultNameUsed();
attila@963 2699 return ((PropertyKey)nameExpr).getPropertyName();
attila@963 2700 } else if(nameExpr instanceof AccessNode) {
attila@963 2701 markDefaultNameUsed();
attila@963 2702 return ((AccessNode)nameExpr).getProperty();
attila@963 2703 }
attila@963 2704 }
attila@963 2705 return null;
attila@963 2706 }
attila@963 2707
attila@963 2708 private void markDefaultNameUsed() {
attila@963 2709 defaultNames.pop();
attila@963 2710 // Can be any value as long as getDefaultFunctionName doesn't recognize it as something it can extract a value
attila@963 2711 // from. Can't be null
attila@963 2712 defaultNames.push("");
attila@963 2713 }
attila@963 2714
jlaskey@3 2715 /**
jlaskey@3 2716 * FormalParameterList :
jlaskey@3 2717 * Identifier
jlaskey@3 2718 * FormalParameterList , Identifier
jlaskey@3 2719 *
jlaskey@3 2720 * See 13
jlaskey@3 2721 *
jlaskey@3 2722 * Parse function parameter list.
jlaskey@3 2723 * @return List of parameter nodes.
jlaskey@3 2724 */
jlaskey@3 2725 private List<IdentNode> formalParameterList() {
sundar@316 2726 return formalParameterList(RPAREN);
sundar@316 2727 }
sundar@316 2728
sundar@316 2729 /**
sundar@316 2730 * Same as the other method of the same name - except that the end
sundar@316 2731 * token type expected is passed as argument to this method.
sundar@316 2732 *
sundar@316 2733 * FormalParameterList :
sundar@316 2734 * Identifier
sundar@316 2735 * FormalParameterList , Identifier
sundar@316 2736 *
sundar@316 2737 * See 13
sundar@316 2738 *
sundar@316 2739 * Parse function parameter list.
sundar@316 2740 * @return List of parameter nodes.
sundar@316 2741 */
sundar@316 2742 private List<IdentNode> formalParameterList(final TokenType endType) {
jlaskey@3 2743 // Prepare to gather parameters.
attila@963 2744 final ArrayList<IdentNode> parameters = new ArrayList<>();
jlaskey@3 2745 // Track commas.
jlaskey@3 2746 boolean first = true;
jlaskey@3 2747
sundar@316 2748 while (type != endType) {
jlaskey@3 2749 // Comma prior to every argument except the first.
jlaskey@3 2750 if (!first) {
jlaskey@3 2751 expect(COMMARIGHT);
jlaskey@3 2752 } else {
jlaskey@3 2753 first = false;
jlaskey@3 2754 }
jlaskey@3 2755
jlaskey@3 2756 // Get and add parameter.
jlaskey@3 2757 final IdentNode ident = getIdent();
jlaskey@3 2758
jlaskey@3 2759 // ECMA 13.1 strict mode restrictions
jlaskey@3 2760 verifyStrictIdent(ident, "function parameter");
jlaskey@3 2761
jlaskey@3 2762 parameters.add(ident);
jlaskey@3 2763 }
jlaskey@3 2764
attila@963 2765 parameters.trimToSize();
jlaskey@3 2766 return parameters;
jlaskey@3 2767 }
jlaskey@3 2768
jlaskey@3 2769 /**
jlaskey@3 2770 * FunctionBody :
jlaskey@3 2771 * SourceElements?
jlaskey@3 2772 *
jlaskey@3 2773 * See 13
jlaskey@3 2774 *
jlaskey@3 2775 * Parse function body.
jlaskey@3 2776 * @return function node (body.)
jlaskey@3 2777 */
attila@963 2778 private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List<IdentNode> parameters, final FunctionNode.Kind kind, final int functionLine) {
jlaskey@3 2779 FunctionNode functionNode = null;
lagergren@211 2780 long lastToken = 0L;
jlaskey@3 2781
jlaskey@3 2782 try {
jlaskey@3 2783 // Create a new function block.
attila@963 2784 functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine);
jlaskey@3 2785
jlaskey@3 2786 // Nashorn extension: expression closures
sundar@118 2787 if (!env._no_syntax_extensions && type != LBRACE) {
jlaskey@3 2788 /*
jlaskey@3 2789 * Example:
jlaskey@3 2790 *
jlaskey@3 2791 * function square(x) x * x;
jlaskey@3 2792 * print(square(3));
jlaskey@3 2793 */
jlaskey@3 2794
jlaskey@3 2795 // just expression as function body
attila@430 2796 final Expression expr = assignmentExpression(true);
attila@963 2797 lastToken = previousToken;
lagergren@211 2798 assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode);
attila@963 2799 // EOL uses length field to store the line number
attila@963 2800 final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken));
attila@963 2801 final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr);
lagergren@211 2802 appendStatement(returnNode);
attila@963 2803 functionNode.setFinish(lastFinish);
jlaskey@3 2804
jlaskey@3 2805 } else {
jlaskey@3 2806 expect(LBRACE);
jlaskey@3 2807
jlaskey@3 2808 // Gather the function elements.
lagergren@253 2809 final List<Statement> prevFunctionDecls = functionDeclarations;
attila@144 2810 functionDeclarations = new ArrayList<>();
attila@144 2811 try {
attila@963 2812 sourceElements(false);
lagergren@211 2813 addFunctionDeclarations(functionNode);
attila@144 2814 } finally {
attila@144 2815 functionDeclarations = prevFunctionDecls;
attila@144 2816 }
jlaskey@3 2817
lagergren@211 2818 lastToken = token;
jlaskey@3 2819 expect(RBRACE);
jlaskey@3 2820 functionNode.setFinish(finish);
jlaskey@3 2821 }
jlaskey@3 2822 } finally {
lagergren@211 2823 functionNode = restoreFunctionNode(functionNode, lastToken);
jlaskey@3 2824 }
attila@963 2825 printAST(functionNode);
jlaskey@3 2826 return functionNode;
jlaskey@3 2827 }
jlaskey@3 2828
attila@963 2829 private void printAST(final FunctionNode functionNode) {
attila@963 2830 if (functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
attila@963 2831 env.getErr().println(new ASTWriter(functionNode));
attila@963 2832 }
attila@963 2833
attila@963 2834 if (functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
attila@963 2835 env.getErr().println(new PrintVisitor(functionNode, true, false));
attila@963 2836 }
attila@963 2837 }
attila@963 2838
lagergren@211 2839 private void addFunctionDeclarations(final FunctionNode functionNode) {
lagergren@211 2840 assert lc.peek() == lc.getFunctionBody(functionNode);
lagergren@211 2841 VarNode lastDecl = null;
lagergren@211 2842 for (int i = functionDeclarations.size() - 1; i >= 0; i--) {
lagergren@253 2843 Statement decl = functionDeclarations.get(i);
lagergren@211 2844 if (lastDecl == null && decl instanceof VarNode) {
lagergren@211 2845 decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION);
lagergren@211 2846 lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS);
lagergren@211 2847 }
lagergren@211 2848 prependStatement(decl);
lagergren@211 2849 }
lagergren@211 2850 }
lagergren@211 2851
attila@430 2852 private RuntimeNode referenceError(final Expression lhs, final Expression rhs, final boolean earlyError) {
sundar@394 2853 if (earlyError) {
sundar@394 2854 throw error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken());
sundar@394 2855 }
attila@430 2856 final ArrayList<Expression> args = new ArrayList<>();
jlaskey@3 2857 args.add(lhs);
jlaskey@3 2858 if (rhs == null) {
lagergren@252 2859 args.add(LiteralNode.newInstance(lhs.getToken(), lhs.getFinish()));
jlaskey@3 2860 } else {
jlaskey@3 2861 args.add(rhs);
jlaskey@3 2862 }
lagergren@252 2863 args.add(LiteralNode.newInstance(lhs.getToken(), lhs.getFinish(), lhs.toString()));
lagergren@252 2864 return new RuntimeNode(lhs.getToken(), lhs.getFinish(), RuntimeNode.Request.REFERENCE_ERROR, args);
jlaskey@3 2865 }
jlaskey@3 2866
jlaskey@3 2867 /*
jlaskey@3 2868 * parse LHS [a, b, ..., c].
jlaskey@3 2869 *
jlaskey@3 2870 * JavaScript 1.8.
jlaskey@3 2871 */
jlaskey@3 2872 //private Node destructureExpression() {
jlaskey@3 2873 // return null;
jlaskey@3 2874 //}
jlaskey@3 2875
jlaskey@3 2876 /**
jlaskey@3 2877 * PostfixExpression :
jlaskey@3 2878 * LeftHandSideExpression
jlaskey@3 2879 * LeftHandSideExpression ++ // [no LineTerminator here]
jlaskey@3 2880 * LeftHandSideExpression -- // [no LineTerminator here]
jlaskey@3 2881 *
jlaskey@3 2882 * See 11.3
jlaskey@3 2883 *
jlaskey@3 2884 * UnaryExpression :
jlaskey@3 2885 * PostfixExpression
jlaskey@3 2886 * delete UnaryExpression
jlaskey@3 2887 * Node UnaryExpression
jlaskey@3 2888 * typeof UnaryExpression
jlaskey@3 2889 * ++ UnaryExpression
jlaskey@3 2890 * -- UnaryExpression
jlaskey@3 2891 * + UnaryExpression
jlaskey@3 2892 * - UnaryExpression
jlaskey@3 2893 * ~ UnaryExpression
jlaskey@3 2894 * ! UnaryExpression
jlaskey@3 2895 *
jlaskey@3 2896 * See 11.4
jlaskey@3 2897 *
jlaskey@3 2898 * Parse unary expression.
jlaskey@3 2899 * @return Expression node.
jlaskey@3 2900 */
attila@430 2901 private Expression unaryExpression() {
lagergren@253 2902 final int unaryLine = line;
jlaskey@3 2903 final long unaryToken = token;
jlaskey@3 2904
jlaskey@3 2905 switch (type) {
lagergren@253 2906 case DELETE: {
lagergren@253 2907 next();
attila@430 2908 final Expression expr = unaryExpression();
lagergren@253 2909 if (expr instanceof BaseNode || expr instanceof IdentNode) {
lagergren@253 2910 return new UnaryNode(unaryToken, expr);
lagergren@253 2911 }
attila@430 2912 appendStatement(new ExpressionStatement(unaryLine, unaryToken, finish, expr));
lagergren@253 2913 return LiteralNode.newInstance(unaryToken, finish, true);
lagergren@253 2914 }
jlaskey@3 2915 case VOID:
jlaskey@3 2916 case TYPEOF:
jlaskey@3 2917 case ADD:
jlaskey@3 2918 case SUB:
jlaskey@3 2919 case BIT_NOT:
jlaskey@3 2920 case NOT:
jlaskey@3 2921 next();
attila@430 2922 final Expression expr = unaryExpression();
lagergren@252 2923 return new UnaryNode(unaryToken, expr);
jlaskey@3 2924
jlaskey@3 2925 case INCPREFIX:
jlaskey@3 2926 case DECPREFIX:
jlaskey@3 2927 final TokenType opType = type;
jlaskey@3 2928 next();
jlaskey@3 2929
attila@430 2930 final Expression lhs = leftHandSideExpression();
jlaskey@3 2931 // ++, -- without operand..
jlaskey@3 2932 if (lhs == null) {
sundar@394 2933 throw error(AbstractParser.message("expected.lvalue", type.getNameOrType()));
jlaskey@3 2934 }
sundar@394 2935
jlaskey@3 2936 if (!(lhs instanceof AccessNode ||
jlaskey@3 2937 lhs instanceof IndexNode ||
jlaskey@3 2938 lhs instanceof IdentNode)) {
sundar@394 2939 return referenceError(lhs, null, env._early_lvalue_error);
jlaskey@3 2940 }
jlaskey@3 2941
jlaskey@3 2942 if (lhs instanceof IdentNode) {
lagergren@137 2943 if (!checkIdentLValue((IdentNode)lhs)) {
sundar@394 2944 return referenceError(lhs, null, false);
jlaskey@3 2945 }
jlaskey@3 2946 verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator");
jlaskey@3 2947 }
jlaskey@3 2948
jlaskey@3 2949 return incDecExpression(unaryToken, opType, lhs, false);
jlaskey@3 2950
jlaskey@3 2951 default:
jlaskey@3 2952 break;
jlaskey@3 2953 }
jlaskey@3 2954
attila@430 2955 Expression expression = leftHandSideExpression();
jlaskey@3 2956
jlaskey@3 2957 if (last != EOL) {
jlaskey@3 2958 switch (type) {
jlaskey@3 2959 case INCPREFIX:
jlaskey@3 2960 case DECPREFIX:
jlaskey@3 2961 final TokenType opType = type;
attila@430 2962 final Expression lhs = expression;
sundar@394 2963 // ++, -- without operand..
sundar@394 2964 if (lhs == null) {
sundar@394 2965 throw error(AbstractParser.message("expected.lvalue", type.getNameOrType()));
sundar@394 2966 }
sundar@394 2967
jlaskey@3 2968 if (!(lhs instanceof AccessNode ||
jlaskey@3 2969 lhs instanceof IndexNode ||
jlaskey@3 2970 lhs instanceof IdentNode)) {
jlaskey@3 2971 next();
sundar@394 2972 return referenceError(lhs, null, env._early_lvalue_error);
jlaskey@3 2973 }
jlaskey@3 2974 if (lhs instanceof IdentNode) {
lagergren@137 2975 if (!checkIdentLValue((IdentNode)lhs)) {
jlaskey@3 2976 next();
sundar@394 2977 return referenceError(lhs, null, false);
jlaskey@3 2978 }
jlaskey@3 2979 verifyStrictIdent((IdentNode)lhs, "operand for " + opType.getName() + " operator");
jlaskey@3 2980 }
jlaskey@3 2981 expression = incDecExpression(token, type, expression, true);
jlaskey@3 2982 next();
jlaskey@3 2983 break;
jlaskey@3 2984 default:
jlaskey@3 2985 break;
jlaskey@3 2986 }
jlaskey@3 2987 }
jlaskey@3 2988
jlaskey@3 2989 if (expression == null) {
lagergren@211 2990 throw error(AbstractParser.message("expected.operand", type.getNameOrType()));
jlaskey@3 2991 }
jlaskey@3 2992
jlaskey@3 2993 return expression;
jlaskey@3 2994 }
jlaskey@3 2995
jlaskey@3 2996 /**
jlaskey@3 2997 * MultiplicativeExpression :
jlaskey@3 2998 * UnaryExpression
jlaskey@3 2999 * MultiplicativeExpression * UnaryExpression
jlaskey@3 3000 * MultiplicativeExpression / UnaryExpression
jlaskey@3 3001 * MultiplicativeExpression % UnaryExpression
jlaskey@3 3002 *
jlaskey@3 3003 * See 11.5
jlaskey@3 3004 *
jlaskey@3 3005 * AdditiveExpression :
jlaskey@3 3006 * MultiplicativeExpression
jlaskey@3 3007 * AdditiveExpression + MultiplicativeExpression
jlaskey@3 3008 * AdditiveExpression - MultiplicativeExpression
jlaskey@3 3009 *
jlaskey@3 3010 * See 11.6
jlaskey@3 3011 *
jlaskey@3 3012 * ShiftExpression :
jlaskey@3 3013 * AdditiveExpression
jlaskey@3 3014 * ShiftExpression << AdditiveExpression
jlaskey@3 3015 * ShiftExpression >> AdditiveExpression
jlaskey@3 3016 * ShiftExpression >>> AdditiveExpression
jlaskey@3 3017 *
jlaskey@3 3018 * See 11.7
jlaskey@3 3019 *
jlaskey@3 3020 * RelationalExpression :
jlaskey@3 3021 * ShiftExpression
jlaskey@3 3022 * RelationalExpression < ShiftExpression
jlaskey@3 3023 * RelationalExpression > ShiftExpression
jlaskey@3 3024 * RelationalExpression <= ShiftExpression
jlaskey@3 3025 * RelationalExpression >= ShiftExpression
jlaskey@3 3026 * RelationalExpression instanceof ShiftExpression
jlaskey@3 3027 * RelationalExpression in ShiftExpression // if !noIf
jlaskey@3 3028 *
jlaskey@3 3029 * See 11.8
jlaskey@3 3030 *
jlaskey@3 3031 * RelationalExpression
jlaskey@3 3032 * EqualityExpression == RelationalExpression
jlaskey@3 3033 * EqualityExpression != RelationalExpression
jlaskey@3 3034 * EqualityExpression === RelationalExpression
jlaskey@3 3035 * EqualityExpression !== RelationalExpression
jlaskey@3 3036 *
jlaskey@3 3037 * See 11.9
jlaskey@3 3038 *
jlaskey@3 3039 * BitwiseANDExpression :
jlaskey@3 3040 * EqualityExpression
jlaskey@3 3041 * BitwiseANDExpression & EqualityExpression
jlaskey@3 3042 *
jlaskey@3 3043 * BitwiseXORExpression :
jlaskey@3 3044 * BitwiseANDExpression
jlaskey@3 3045 * BitwiseXORExpression ^ BitwiseANDExpression
jlaskey@3 3046 *
jlaskey@3 3047 * BitwiseORExpression :
jlaskey@3 3048 * BitwiseXORExpression
jlaskey@3 3049 * BitwiseORExpression | BitwiseXORExpression
jlaskey@3 3050 *
jlaskey@3 3051 * See 11.10
jlaskey@3 3052 *
jlaskey@3 3053 * LogicalANDExpression :
jlaskey@3 3054 * BitwiseORExpression
jlaskey@3 3055 * LogicalANDExpression && BitwiseORExpression
jlaskey@3 3056 *
jlaskey@3 3057 * LogicalORExpression :
jlaskey@3 3058 * LogicalANDExpression
jlaskey@3 3059 * LogicalORExpression || LogicalANDExpression
jlaskey@3 3060 *
jlaskey@3 3061 * See 11.11
jlaskey@3 3062 *
jlaskey@3 3063 * ConditionalExpression :
jlaskey@3 3064 * LogicalORExpression
jlaskey@3 3065 * LogicalORExpression ? AssignmentExpression : AssignmentExpression
jlaskey@3 3066 *
jlaskey@3 3067 * See 11.12
jlaskey@3 3068 *
jlaskey@3 3069 * AssignmentExpression :
jlaskey@3 3070 * ConditionalExpression
jlaskey@3 3071 * LeftHandSideExpression AssignmentOperator AssignmentExpression
jlaskey@3 3072 *
jlaskey@3 3073 * AssignmentOperator :
jlaskey@3 3074 * = *= /= %= += -= <<= >>= >>>= &= ^= |=
jlaskey@3 3075 *
jlaskey@3 3076 * See 11.13
jlaskey@3 3077 *
jlaskey@3 3078 * Expression :
jlaskey@3 3079 * AssignmentExpression
jlaskey@3 3080 * Expression , AssignmentExpression
jlaskey@3 3081 *
jlaskey@3 3082 * See 11.14
jlaskey@3 3083 *
jlaskey@3 3084 * Parse expression.
jlaskey@3 3085 * @return Expression node.
jlaskey@3 3086 */
attila@430 3087 private Expression expression() {
jlaskey@3 3088 // TODO - Destructuring array.
jlaskey@3 3089 // Include commas in expression parsing.
jlaskey@3 3090 return expression(unaryExpression(), COMMARIGHT.getPrecedence(), false);
jlaskey@3 3091 }
lagergren@211 3092
attila@963 3093 private JoinPredecessorExpression joinPredecessorExpression() {
attila@963 3094 return new JoinPredecessorExpression(expression());
attila@963 3095 }
attila@963 3096
attila@430 3097 private Expression expression(final Expression exprLhs, final int minPrecedence, final boolean noIn) {
jlaskey@3 3098 // Get the precedence of the next operator.
jlaskey@3 3099 int precedence = type.getPrecedence();
attila@430 3100 Expression lhs = exprLhs;
jlaskey@3 3101
jlaskey@3 3102 // While greater precedence.
jlaskey@3 3103 while (type.isOperator(noIn) && precedence >= minPrecedence) {
jlaskey@3 3104 // Capture the operator token.
jlaskey@3 3105 final long op = token;
jlaskey@3 3106
jlaskey@3 3107 if (type == TERNARY) {
jlaskey@3 3108 // Skip operator.
jlaskey@3 3109 next();
jlaskey@3 3110
jlaskey@3 3111 // Pass expression. Middle expression of a conditional expression can be a "in"
jlaskey@3 3112 // expression - even in the contexts where "in" is not permitted.
attila@963 3113 final Expression trueExpr = expression(unaryExpression(), ASSIGN.getPrecedence(), false);
jlaskey@3 3114
jlaskey@3 3115 expect(COLON);
jlaskey@3 3116
jlaskey@3 3117 // Fail expression.
attila@963 3118 final Expression falseExpr = expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
jlaskey@3 3119
jlaskey@3 3120 // Build up node.
attila@963 3121 lhs = new TernaryNode(op, lhs, new JoinPredecessorExpression(trueExpr), new JoinPredecessorExpression(falseExpr));
jlaskey@3 3122 } else {
jlaskey@3 3123 // Skip operator.
jlaskey@3 3124 next();
jlaskey@3 3125
jlaskey@3 3126 // Get the next primary expression.
attila@963 3127 Expression rhs;
attila@963 3128 final boolean isAssign = Token.descType(op) == ASSIGN;
attila@963 3129 if(isAssign) {
attila@963 3130 defaultNames.push(lhs);
jlaskey@3 3131 }
attila@963 3132 try {
attila@963 3133 rhs = unaryExpression();
attila@963 3134 // Get precedence of next operator.
attila@963 3135 int nextPrecedence = type.getPrecedence();
attila@963 3136
attila@963 3137 // Subtask greater precedence.
attila@963 3138 while (type.isOperator(noIn) &&
attila@963 3139 (nextPrecedence > precedence ||
attila@963 3140 nextPrecedence == precedence && !type.isLeftAssociative())) {
attila@963 3141 rhs = expression(rhs, nextPrecedence, noIn);
attila@963 3142 nextPrecedence = type.getPrecedence();
attila@963 3143 }
attila@963 3144 } finally {
attila@963 3145 if(isAssign) {
attila@963 3146 defaultNames.pop();
attila@963 3147 }
attila@963 3148 }
jlaskey@3 3149 lhs = verifyAssignment(op, lhs, rhs);
jlaskey@3 3150 }
jlaskey@3 3151
jlaskey@3 3152 precedence = type.getPrecedence();
jlaskey@3 3153 }
jlaskey@3 3154
jlaskey@3 3155 return lhs;
jlaskey@3 3156 }
jlaskey@3 3157
attila@430 3158 private Expression assignmentExpression(final boolean noIn) {
jlaskey@3 3159 // TODO - Handle decompose.
jlaskey@3 3160 // Exclude commas in expression parsing.
jlaskey@3 3161 return expression(unaryExpression(), ASSIGN.getPrecedence(), noIn);
jlaskey@3 3162 }
jlaskey@3 3163
jlaskey@3 3164 /**
jlaskey@3 3165 * Parse an end of line.
jlaskey@3 3166 */
jlaskey@3 3167 private void endOfLine() {
jlaskey@3 3168 switch (type) {
jlaskey@3 3169 case SEMICOLON:
jlaskey@3 3170 case EOL:
jlaskey@3 3171 next();
jlaskey@3 3172 break;
jlaskey@3 3173 case RPAREN:
jlaskey@3 3174 case RBRACKET:
jlaskey@3 3175 case RBRACE:
jlaskey@3 3176 case EOF:
jlaskey@3 3177 break;
jlaskey@3 3178 default:
jlaskey@3 3179 if (last != EOL) {
jlaskey@3 3180 expect(SEMICOLON);
jlaskey@3 3181 }
jlaskey@3 3182 break;
jlaskey@3 3183 }
jlaskey@3 3184 }
jlaskey@3 3185
lagergren@108 3186 @Override
lagergren@108 3187 public String toString() {
attila@963 3188 return "'JavaScript Parsing'";
lagergren@108 3189 }
attila@144 3190
attila@233 3191 private static void markEval(final LexicalContext lc) {
lagergren@211 3192 final Iterator<FunctionNode> iter = lc.getFunctions();
lagergren@211 3193 boolean flaggedCurrentFn = false;
lagergren@211 3194 while (iter.hasNext()) {
lagergren@211 3195 final FunctionNode fn = iter.next();
lagergren@211 3196 if (!flaggedCurrentFn) {
attila@233 3197 lc.setFlag(fn, FunctionNode.HAS_EVAL);
lagergren@211 3198 flaggedCurrentFn = true;
lagergren@211 3199 } else {
attila@233 3200 lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL);
lagergren@211 3201 }
attila@416 3202 lc.setBlockNeedsScope(lc.getFunctionBody(fn));
lagergren@211 3203 }
attila@144 3204 }
attila@144 3205
lagergren@253 3206 private void prependStatement(final Statement statement) {
lagergren@211 3207 lc.prependStatement(statement);
lagergren@211 3208 }
lagergren@211 3209
lagergren@253 3210 private void appendStatement(final Statement statement) {
lagergren@211 3211 lc.appendStatement(statement);
attila@144 3212 }
jlaskey@3 3213 }

mercurial