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

Wed, 08 Jan 2014 17:51:47 +0530

author
sundar
date
Wed, 08 Jan 2014 17:51:47 +0530
changeset 754
8c19fdb184f8
parent 750
23cbfa168a4e
child 761
37bf1b9838b5
permissions
-rw-r--r--

8031317: SyntaxError when property setter has no parameter
Reviewed-by: lagergren, hannesw

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

mercurial