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