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

Mon, 01 Jul 2013 12:38:01 +0530

author
sundar
date
Mon, 01 Jul 2013 12:38:01 +0530
changeset 387
02588d68399d
parent 351
a2fa56222fa2
child 391
9165138b427c
permissions
-rw-r--r--

8019473: Parser issues related to functions and blocks
Reviewed-by: lagergren

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

mercurial