diff --git a/bundles/com.aptana.js.core/src/com/aptana/js/core/parsing/GraalJSParser.java b/bundles/com.aptana.js.core/src/com/aptana/js/core/parsing/GraalJSParser.java index f3dbf5f604..eae38112a5 100644 --- a/bundles/com.aptana.js.core/src/com/aptana/js/core/parsing/GraalJSParser.java +++ b/bundles/com.aptana.js.core/src/com/aptana/js/core/parsing/GraalJSParser.java @@ -142,7 +142,7 @@ public void error(final ParserException e) // Reset state and fall back to non-strict script as our goal working.getErrors().clear(); fParser = new CommentCollectingParser(env, src, errorManager); - result = fParser.parse(filename, startOffset, source.length() - startOffset, false); + result = fParser.parse(filename, startOffset, source.length() - startOffset, 0); } // If any errors found, will run in a simple recovery mode where will assume basic expected tokens({, },IDENT, (,)) are available and proceed with the parse without failing. @@ -152,7 +152,7 @@ public void error(final ParserException e) // considered in the recovery mode inRecoveryMode[0] = true; fParser = new CommentCollectingParser(env, src, errorManager, true); - result = fParser.parse(filename, startOffset, source.length() - startOffset, false); + result = fParser.parse(filename, startOffset, source.length() - startOffset, 0); } return result; @@ -255,18 +255,6 @@ protected void expectDontAdvance(TokenType expected) throws ParserException } } - private void insertToken(TokenType expectedTokenType) - { - long expectedNewToken = Token.toDesc(expectedTokenType, linePosition, - expectedTokenType.getName() != null ? expectedTokenType.getLength() : 0); - - // insert the token that is expected before the unexpected token - stream.insert(k, expectedNewToken); - token = expectedNewToken; - k--; - - } - } } diff --git a/bundles/com.oracle.js.parser/META-INF/MANIFEST.MF b/bundles/com.oracle.js.parser/META-INF/MANIFEST.MF index 5c3c9529bc..5a287dd901 100644 --- a/bundles/com.oracle.js.parser/META-INF/MANIFEST.MF +++ b/bundles/com.oracle.js.parser/META-INF/MANIFEST.MF @@ -8,3 +8,4 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Export-Package: com.oracle.js.parser, com.oracle.js.parser.ir, com.oracle.js.parser.ir.visitor +Require-Bundle: org.graalvm.collections;visibility:=reexport diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/AbstractParser.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/AbstractParser.java index 8a1be04d43..e19313b8e5 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/AbstractParser.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/AbstractParser.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -31,8 +47,8 @@ import static com.oracle.js.parser.TokenType.EOL; import static com.oracle.js.parser.TokenType.IDENT; -import java.util.HashMap; -import java.util.Map; +import java.math.BigInteger; + import java.util.function.Function; import com.oracle.js.parser.Lexer.LexerToken; @@ -85,14 +101,9 @@ public abstract class AbstractParser { /** Is this parser running under strict mode? */ protected boolean isStrictMode; - /** Is this parser running under strong mode? */ - protected boolean isStrongMode; - /** What should line numbers be counted from? */ protected final int lineOffset; - private final Map canonicalNames = new HashMap<>(); - /** * Construct a parser. * @@ -204,17 +215,16 @@ private void checkDirectiveComment() { * @return tokenType of next token. */ protected TokenType nextToken() { - // Capture last token type, but ignore comments (which are irrelevant for the purpose of - // newline detection). - if (type != COMMENT) { - last = type; - } if (type != EOF) { - // Set up next token. k++; final long lastToken = token; - previousToken = token; + // Capture last token type, but ignore comments (which are irrelevant for the purpose of + // newline detection). + if (type != COMMENT) { + last = type; + previousToken = token; + } token = getToken(k); type = Token.descType(token); @@ -292,9 +302,8 @@ protected final ParserException error(final String message) { * @return ParserException upon failure. Caller should throw and not ignore */ protected final ParserException error(final JSErrorType errorType, final String message) { - // TODO - column needs to account for tabs. final int position = Token.descPosition(token); - final int column = position - linePosition; + final int column = source.getColumn(position); final String formatted = ErrorManager.format(message, source, line, column, token); return new ParserException(errorType, formatted, source, line, column, token); } @@ -331,6 +340,12 @@ protected final String expectMessage(final TokenType expected) { return msg; } + protected final String expectMessage(final TokenType expected, final long errorToken) { + final String expectedName = expected.getNameOrType(); + final String tokenString = Token.toString(source, errorToken); + return AbstractParser.message("expected", expectedName, tokenString); + } + /** * Check current token and advance to the next token. * @@ -357,22 +372,18 @@ protected void expectDontAdvance(final TokenType expected) throws ParserExceptio } /** - * Check next token, get its value and advance. + * Get the value of the current token. If the current token contains an escape sequence, the + * method does not attempt to convert it. * - * @param expected Expected tokenType. - * @return The JavaScript value of the token - * @throws ParserException on unexpected token type + * @return JavaScript value of the token. */ - protected final Object expectValue(final TokenType expected) throws ParserException { - if (type != expected) { - throw error(expectMessage(expected)); + protected final Object getValueNoEscape() { + try { + return lexer.getValueOf(token, isStrictMode, false); + } catch (final ParserException e) { + errors.error(e); } - - final Object value = getValue(); - - next(); - - return value; + return null; } /** @@ -420,25 +431,22 @@ protected final IdentNode getIdent() { // Capture IDENT token. long identToken = token; - if (isNonStrictModeIdent()) { - // Fake out identifier. - identToken = Token.recast(token, IDENT); - // Get IDENT. + if (type == IDENT) { final String ident = (String) getValue(identToken); next(); - // Create IDENT node. - return createIdentNode(identToken, finish, ident).setIsFutureStrictName(); - } + return createIdentNode(identToken, finish, ident); + } else if (type.isContextualKeyword() || isNonStrictModeIdent()) { + final String ident = type.getName(); - // Get IDENT. - final String ident = (String) expectValue(IDENT); - if (ident == null) { - return null; + next(); + + return new IdentNode(identToken, finish, ident); + } else { + // Not an IDENT. + throw error(expectMessage(IDENT)); } - // Create IDENT node. - return createIdentNode(identToken, finish, ident); } /** @@ -453,9 +461,16 @@ protected final IdentNode getIdent() { * name will be deduplicated. */ protected IdentNode createIdentNode(final long identToken, final int identFinish, final String name) { - final String existingName = canonicalNames.putIfAbsent(name, name); - final String canonicalName = existingName != null ? existingName : name; - return new IdentNode(identToken, identFinish, canonicalName); + assert isInterned(name) : name; + return new IdentNode(identToken, identFinish, name); + } + + private boolean isInterned(final String name) { + return isSame(lexer.stringIntern(name), name) || isSame(name.intern(), name); + } + + private static boolean isSame(Object a, Object b) { + return a == b; } /** @@ -464,14 +479,25 @@ protected IdentNode createIdentNode(final long identToken, final int identFinish * @return true if current token is an identifier name */ protected final boolean isIdentifierName() { - final TokenKind kind = type.getKind(); - if (kind == TokenKind.KEYWORD || kind == TokenKind.FUTURE || kind == TokenKind.FUTURESTRICT) { + return isIdentifierName(token); + } + + /** + * Check if token is an identifier name + * + * @return true if token is an identifier name + */ + protected final boolean isIdentifierName(long currentToken) { + final TokenType currentType = Token.descType(currentToken); + assert currentType != IDENT; // handled before + final TokenKind kind = currentType.getKind(); + if (kind == TokenKind.KEYWORD || kind == TokenKind.FUTURE || kind == TokenKind.FUTURESTRICT || kind == TokenKind.CONTEXTUAL) { return true; } // only literals allowed are null, false and true if (kind == TokenKind.LITERAL) { - switch (type) { + switch (currentType) { case FALSE: case NULL: case TRUE: @@ -482,7 +508,7 @@ protected final boolean isIdentifierName() { } // Fake out identifier. - final long identToken = Token.recast(token, IDENT); + final long identToken = Token.recast(currentToken, IDENT); // Get IDENT. final String ident = (String) getValue(identToken); return !ident.isEmpty() && Character.isJavaIdentifierStart(ident.charAt(0)); @@ -505,11 +531,8 @@ protected final IdentNode getIdentifierName() { // Create IDENT node. return createIdentNode(identToken, finish, ident); } else { - expectDontAdvance(IDENT); - - // Fake out identifier. - final long identToken = Token.recast(token, IDENT); - return createIdentNode(identToken, finish, ""); + expect(IDENT); + return null; } } @@ -532,6 +555,8 @@ protected final LiteralNode getLiteral() throws ParserException { if (value == null) { node = LiteralNode.newInstance(literalToken, finish); + } else if (value instanceof BigInteger) { + node = LiteralNode.newInstance(literalToken, finish, (BigInteger) value); } else if (value instanceof Number) { node = LiteralNode.newInstance(literalToken, finish, (Number) value, getNumberToStringConverter()); } else if (value instanceof String) { diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ECMAErrors.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ECMAErrors.java index 4ffb4ec917..8aa0a8b5aa 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ECMAErrors.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ECMAErrors.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ErrorManager.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ErrorManager.java index 607e676558..c4fba55d40 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ErrorManager.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ErrorManager.java @@ -1,32 +1,46 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; -import java.io.PrintWriter; - /** * Handles JavaScript error reporting. */ @@ -91,7 +105,7 @@ public static String format(final String message, final Source source, final int sb.append(source.getName()).append(':').append(line).append(':').append(column).append(' ').append(message).append(eoln); // Source content. - final String sourceLine = source.getSourceLine(position); + final CharSequence sourceLine = source.getSourceLine(position); sb.append(sourceLine).append(eoln); // Pointer to column. @@ -229,35 +243,6 @@ public ParserException getParserException() { return parserException; } - /** - * {@link ErrorManager} that reports to a {@link PrintWriter}. - */ - public static class PrintWriterErrorManager extends ErrorManager { - /** Reporting writer. */ - private final PrintWriter writer; - - /** - * Constructor. Reports to {@link System#err}. - */ - public PrintWriterErrorManager() { - this(new PrintWriter(System.err, true)); - } - - /** - * Constructor. - * - * @param writer I/O writer to report on. - */ - public PrintWriterErrorManager(final PrintWriter writer) { - this.writer = writer; - } - - @Override - protected void message(String message) { - writer.println(message); - } - } - /** * {@link ErrorManager} that reports to a {@link StringBuilder}. */ diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSErrorType.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSErrorType.java index 5ebdc760fe..2039681f5c 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSErrorType.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSErrorType.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSType.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSType.java index b06264fc7f..b76f171fb5 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSType.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/JSType.java @@ -1,31 +1,46 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; - // @formatter:off /** * Representation for ECMAScript types - this maps directly to the ECMA script standard @@ -39,17 +54,6 @@ private JSType() { private static final double INT32_LIMIT = 4294967296.0; - /** - * Returns true if double number can be represented as an int - * - * @param number a long to inspect - * - * @return true for int representable longs - */ - public static boolean isRepresentableAsInt(final long number) { - return (int) number == number; - } - /** * Returns true if double number can be represented as an int. Note that it returns true for negative * zero. If you need to exclude negative zero, use {@link #isStrictlyRepresentableAsInt(double)}. @@ -74,20 +78,6 @@ public static boolean isStrictlyRepresentableAsInt(final double number) { return isRepresentableAsInt(number) && isNotNegativeZero(number); } - /** - * Returns true if Object can be represented as an int - * - * @param obj an object to inspect - * - * @return true for int representable objects - */ - public static boolean isRepresentableAsInt(final Object obj) { - if (obj instanceof Number) { - return isRepresentableAsInt(((Number) obj).doubleValue()); - } - return false; - } - /** * Returns true if double number can be represented as a long. Note that it returns true for negative * zero. If you need to exclude negative zero, use {@link #isStrictlyRepresentableAsLong(double)}. @@ -111,30 +101,6 @@ public static boolean isStrictlyRepresentableAsLong(final double number) { return isRepresentableAsLong(number) && isNotNegativeZero(number); } - /** - * Returns true if Object can be represented as a long - * - * @param obj an object to inspect - * - * @return true for long representable objects - */ - public static boolean isRepresentableAsLong(final Object obj) { - if (obj instanceof Number) { - return isRepresentableAsLong(((Number) obj).doubleValue()); - } - return false; - } - - /** - * Returns true if the number is the negative zero ({@code -0.0d}). - * - * @param number the number to test - * @return true if it is the negative zero, false otherwise. - */ - public static boolean isNegativeZero(final double number) { - return number == 0.0d && Double.doubleToRawLongBits(number) == 0x8000000000000000L; - } - /** * Returns true if the number is not the negative zero ({@code -0.0d}). * @param number the number to test @@ -144,17 +110,6 @@ private static boolean isNotNegativeZero(final double number) { return Double.doubleToRawLongBits(number) != 0x8000000000000000L; } - /** - * JavaScript compliant conversion of number to boolean - * - * @param num a number - * - * @return a boolean - */ - public static boolean toBoolean(final double num) { - return num != 0 && !Double.isNaN(num); - } - /** * JavaScript compliant conversion of Object to boolean See ECMA 9.2 ToBoolean * @@ -183,75 +138,6 @@ public static boolean toBoolean(final Object obj) { return true; } - /** - * JavaScript compliant conversion of number to String - * - * @param num a number - * @param radix a radix for the conversion - * - * @return a string - */ - public static String toString(final double num, final int radix) { - assert radix >= 2 && radix <= 36 : "invalid radix"; - - if (isRepresentableAsInt(num)) { - return Integer.toString((int) num, radix); - } - - if (num == Double.POSITIVE_INFINITY) { - return "Infinity"; - } - - if (num == Double.NEGATIVE_INFINITY) { - return "-Infinity"; - } - - if (Double.isNaN(num)) { - return "NaN"; - } - - if (num == 0.0) { - return "0"; - } - - final String chars = "0123456789abcdefghijklmnopqrstuvwxyz"; - final StringBuilder sb = new StringBuilder(); - - final boolean negative = num < 0.0; - final double signedNum = negative ? -num : num; - - double intPart = Math.floor(signedNum); - double decPart = signedNum - intPart; - - // encode integer part from least significant digit, then reverse - do { - final double remainder = intPart % radix; - sb.append(chars.charAt((int) remainder)); - intPart -= remainder; - intPart /= radix; - } while (intPart >= 1.0); - - if (negative) { - sb.append('-'); - } - sb.reverse(); - - // encode decimal part - if (decPart > 0.0) { - final int dot = sb.length(); - sb.append('.'); - do { - decPart *= radix; - final double d = Math.floor(decPart); - sb.append(chars.charAt((int) d)); - decPart -= d; - } while (decPart > 0.0 && sb.length() - dot < 1100); - // somewhat arbitrarily use same limit as V8 - } - - return sb.toString(); - } - /** * JavaScript compliant conversion of Object to number See ECMA 9.3 ToNumber * @@ -410,20 +296,6 @@ public static int toInt32(final Object obj) { return toInt32(toNumber(obj)); } - // Minimum and maximum range between which every long value can be precisely represented as a double. - private static final long MAX_PRECISE_DOUBLE = 1L << 53; - private static final long MIN_PRECISE_DOUBLE = -MAX_PRECISE_DOUBLE; - - /** - * JavaScript compliant long to int32 conversion - * - * @param num a long - * @return an int32 - */ - public static int toInt32(final long num) { - return (int) (num >= MIN_PRECISE_DOUBLE && num <= MAX_PRECISE_DOUBLE ? num : (long) (num % INT32_LIMIT)); - } - /** * JavaScript compliant number to int32 conversion * @@ -454,46 +326,6 @@ public static long toUint32(final double num) { return doubleToInt32(num) & MAX_UINT; } - /** - * JavaScript compliant Object to uint16 conversion ECMA 9.7 ToUint16: (Unsigned 16 Bit Integer) - * - * @param obj an object - * @return a uint16 - */ - public static int toUint16(final Object obj) { - return toUint16(toNumber(obj)); - } - - /** - * JavaScript compliant number to uint16 conversion - * - * @param num a number - * @return a uint16 - */ - public static int toUint16(final int num) { - return num & 0xffff; - } - - /** - * JavaScript compliant number to uint16 conversion - * - * @param num a number - * @return a uint16 - */ - public static int toUint16(final long num) { - return (int) num & 0xffff; - } - - /** - * JavaScript compliant number to uint16 conversion - * - * @param num a number - * @return a uint16 - */ - public static int toUint16(final double num) { - return (int) doubleToInt32(num) & 0xffff; - } - private static long doubleToInt32(final double num) { final int exponent = Math.getExponent(num); if (exponent < 31) { @@ -509,16 +341,6 @@ private static long doubleToInt32(final double num) { return (long) (d % INT32_LIMIT); } - /** - * Check whether a number is finite - * - * @param num a number - * @return true if finite - */ - public static boolean isFinite(final double num) { - return !Double.isInfinite(num) && !Double.isNaN(num); - } - private static double parseRadix(final char[] chars, final int start, final int length, final int radix) { int pos = 0; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Lexer.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Lexer.java index 60ff037e85..df1aacd482 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Lexer.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Lexer.java @@ -1,31 +1,48 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; import static com.oracle.js.parser.TokenType.ADD; +import static com.oracle.js.parser.TokenType.BIGINT; import static com.oracle.js.parser.TokenType.BINARY_NUMBER; import static com.oracle.js.parser.TokenType.COMMENT; import static com.oracle.js.parser.TokenType.DECIMAL; @@ -40,6 +57,7 @@ import static com.oracle.js.parser.TokenType.HEXADECIMAL; import static com.oracle.js.parser.TokenType.LBRACE; import static com.oracle.js.parser.TokenType.LPAREN; +import static com.oracle.js.parser.TokenType.NON_OCTAL_DECIMAL; import static com.oracle.js.parser.TokenType.OCTAL; import static com.oracle.js.parser.TokenType.OCTAL_LEGACY; import static com.oracle.js.parser.TokenType.RBRACE; @@ -53,6 +71,8 @@ import static com.oracle.js.parser.TokenType.XML; import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; // @formatter:off /** @@ -61,9 +81,6 @@ */ @SuppressWarnings("fallthrough") public class Lexer extends Scanner { - private static final long MIN_INT_L = Integer.MIN_VALUE; - private static final long MAX_INT_L = Integer.MAX_VALUE; - private static final boolean XML_LITERALS = Options.getBooleanProperty("lexer.xmlliterals"); /** Content source. */ @@ -95,28 +112,13 @@ public class Lexer extends Scanner { private final boolean pauseOnFunctionBody; private boolean pauseOnNextLeftBrace; + boolean pauseOnRightBrace; - private int templateExpressionOpenBraces; + /** Map to intern strings during parsing (memory footprint). */ + private final Map internedStrings; - private static final String SPACETAB = " \t"; // ASCII space and tab - private static final String LFCR = "\n\r"; // line feed and carriage return (ctrl-m) - - private static final String JSON_WHITESPACE_EOL = LFCR; - private static final String JSON_WHITESPACE = SPACETAB + LFCR; - - private static final String JAVASCRIPT_WHITESPACE_EOL = - LFCR + - "\u2028" + // line separator - "\u2029" // paragraph separator - ; - private static final String JAVASCRIPT_WHITESPACE = - SPACETAB + - JAVASCRIPT_WHITESPACE_EOL + - "\u000b" + // tabulation line - "\u000c" + // ff (ctrl-l) - "\u00a0" + // Latin-1 space + private static final String JAVASCRIPT_WHITESPACE_HIGH = "\u1680" + // Ogham space mark - "\u180e" + // separator, Mongolian vowel "\u2000" + // en quad "\u2001" + // em quad "\u2002" + // en space @@ -128,40 +130,15 @@ public class Lexer extends Scanner { "\u2008" + // punctuation space "\u2009" + // thin space "\u200a" + // hair space + "\u2028" + // line separator + "\u2029" + // paragraph separator "\u202f" + // narrow no-break space "\u205f" + // medium mathematical space "\u3000" + // ideographic space "\ufeff" // byte order mark ; - private static final String JAVASCRIPT_WHITESPACE_IN_REGEXP = - "\\u000a" + // line feed - "\\u000d" + // carriage return (ctrl-m) - "\\u2028" + // line separator - "\\u2029" + // paragraph separator - "\\u0009" + // tab - "\\u0020" + // ASCII space - "\\u000b" + // tabulation line - "\\u000c" + // ff (ctrl-l) - "\\u00a0" + // Latin-1 space - "\\u1680" + // Ogham space mark - "\\u180e" + // separator, Mongolian vowel - "\\u2000" + // en quad - "\\u2001" + // em quad - "\\u2002" + // en space - "\\u2003" + // em space - "\\u2004" + // three-per-em space - "\\u2005" + // four-per-em space - "\\u2006" + // six-per-em space - "\\u2007" + // figure space - "\\u2008" + // punctuation space - "\\u2009" + // thin space - "\\u200a" + // hair space - "\\u202f" + // narrow no-break space - "\\u205f" + // medium mathematical space - "\\u3000" + // ideographic space - "\\ufeff" // byte order mark - ; + private static final int JAVASCRIPT_WHITESPACE_HIGH_START = JAVASCRIPT_WHITESPACE_HIGH.charAt(0); public static String unicodeEscape(final char ch) { final StringBuilder sb = new StringBuilder(); @@ -177,16 +154,6 @@ public static String unicodeEscape(final char ch) { return sb.toString(); } - /** - * Constructor - * - * @param source the source - * @param stream the token stream to lex - */ - public Lexer(final Source source, final TokenStream stream) { - this(source, stream, false, false, false); - } - /** * Constructor * @@ -215,7 +182,7 @@ public Lexer(final Source source, final TokenStream stream, final boolean script * avoid reading ahead unnecessarily when we skip the function bodies. */ public Lexer(final Source source, final int start, final int len, final TokenStream stream, final boolean scripting, final boolean es6, final boolean shebang, final boolean pauseOnFunctionBody) { - super(source.getContent(), 1, start, len); + super(source.getContent().toString().toCharArray(), 1, start, len); this.source = source; this.stream = stream; this.scripting = scripting; @@ -226,6 +193,7 @@ public Lexer(final Source source, final int start, final int len, final TokenStr this.last = EOL; this.pauseOnFunctionBody = pauseOnFunctionBody; + this.internedStrings = new HashMap<>(); } private Lexer(final Lexer lexer, final State state) { @@ -242,6 +210,7 @@ private Lexer(final Lexer lexer, final State state) { linePosition = state.linePosition; last = EOL; pauseOnFunctionBody = false; + internedStrings = lexer.internedStrings; } static class State extends Scanner.State { @@ -333,15 +302,6 @@ protected void add(final TokenType type, final int start) { add(type, start, position); } - /** - * Return the String of valid whitespace characters for regular - * expressions in JavaScript - * @return regexp whitespace string - */ - public static String getWhitespaceRegExp() { - return JAVASCRIPT_WHITESPACE_IN_REGEXP; - } - /** * Skip end of line. * @@ -387,7 +347,25 @@ private void skipLine(final boolean addEOL) { * @return true if valid JavaScript whitespace */ public static boolean isJSWhitespace(final char ch) { - return JAVASCRIPT_WHITESPACE.indexOf(ch) != -1; + if (ch <= 0x000d) { + return (ch >= 0x0009); // \t\n\u000b\u000c\r + } else if (ch < JAVASCRIPT_WHITESPACE_HIGH_START) { + return (ch == ' ' || ch == 0x00a0); + } else { + return isWhitespaceHigh(ch); + } + } + + private static boolean isWhitespaceHigh(final char ch) { + for (int pos = 0; pos < JAVASCRIPT_WHITESPACE_HIGH.length(); pos++) { + char cur = JAVASCRIPT_WHITESPACE_HIGH.charAt(pos); + if (cur == ch) { + return true; + } else if (cur > ch) { + return false; + } + } + return false; } /** @@ -396,28 +374,9 @@ public static boolean isJSWhitespace(final char ch) { * @return true if valid JavaScript end of line */ public static boolean isJSEOL(final char ch) { - return JAVASCRIPT_WHITESPACE_EOL.indexOf(ch) != -1; + return ch == '\n' || ch == '\r' || ch == '\u2028' || ch == '\u2029'; } - /** - * Test whether a char is valid JSON whitespace - * @param ch a char - * @return true if valid JSON whitespace - */ - public static boolean isJsonWhitespace(final char ch) { - return JSON_WHITESPACE.indexOf(ch) != -1; - } - - /** - * Test whether a char is valid JSON end of line - * @param ch a char - * @return true if valid JSON end of line - */ - public static boolean isJsonEOL(final char ch) { - return JSON_WHITESPACE_EOL.indexOf(ch) != -1; - } - - /** * Test if char is a string delimiter, e.g. '\' or '"'. * @param ch a char @@ -577,13 +536,13 @@ public RegexToken valueOfPattern(final int start, final int length) { } // Get pattern as string. - final String regex = sb.toString(); + final String regex = stringIntern(sb.toString()); // Skip /. skip(1); // Options as string. - final String options = source.getString(position, scanIdentifier()); + final String options = stringIntern(source.getString(position, scanIdentifier())); reset(savePosition); @@ -631,6 +590,9 @@ protected boolean scanLiteral(final long token, final TokenType startTokenType, if (stream.get(stream.last()) != token) { return false; } + + // Record current position in case multiple heredocs start on this line - see JDK-8073653 + final State state = saveState(); // Rewind to token start position reset(Token.descPosition(token)); @@ -638,7 +600,7 @@ protected boolean scanLiteral(final long token, final TokenType startTokenType, return scanRegEx(); } else if (ch0 == '<') { if (ch1 == '<') { - return scanHereString(lir); + return scanHereString(lir, state); } else if (Character.isJavaIdentifierStart(ch1)) { return scanXMLLiteral(); } @@ -748,7 +710,7 @@ private int hexSequence(final int length, final TokenType type) { final int digit = convertDigit(ch0, 16); if (digit == -1) { - error(Lexer.message("invalid.hex"), type, position, limit); + error(Lexer.message("invalid.hex"), type, position, limit - position); return i == 0 ? -1 : value; } @@ -777,7 +739,7 @@ private int varlenHexSequence(final TokenType type) { skip(1); return value; } else { - error(Lexer.message("invalid.hex"), type, position, limit); + error(Lexer.message("invalid.hex"), type, position, limit - position); skip(1); return -1; } @@ -786,14 +748,14 @@ private int varlenHexSequence(final TokenType type) { final int digit = convertDigit(ch0, 16); if (digit == -1) { - error(Lexer.message("invalid.hex"), type, position, limit); + error(Lexer.message("invalid.hex"), type, position, limit - position); return i == 0 ? -1 : value; } value = digit | value << 4; if (value > 1114111) { - error(Lexer.message("invalid.hex"), type, position, limit); + error(Lexer.message("invalid.hex"), type, position, limit - position); return -1; } @@ -841,28 +803,48 @@ private int octalSequence() { return value; } + public boolean checkIdentForKeyword(final long token, final String keyword) { + final int len = Token.descLength(token); + final int start = Token.descPosition(token); + if (len != keyword.length()) { + return false; + } + + for (int i = 0; i < len; ++i) { + if (content[start + i] != keyword.charAt(i)) { + return false; + } + } + + return true; + } + /** * Convert a string to a JavaScript identifier. * * @param start Position in source content. * @param length Length of token. + * @param convertUnicode convert Unicode symbols in the Ident string. * @return Ident string or null if an error. */ - private String valueOfIdent(final int start, final int length) throws RuntimeException { - // Save the current position. - final int savePosition = position; + private String valueOfIdent(final int start, final int length, final boolean convertUnicode) { // End of scan. final int end = start + length; - // Reset to beginning of content. - reset(start); // Buffer for recording characters. final StringBuilder sb = new StringBuilder(length); + int pos = start; + // Scan until end of line or end of file. - while (!atEOF() && position < end && !isEOL(ch0)) { + while (pos < end) { + + char curCh0 = content[pos]; + // If escape character. - if (ch0 == '\\' && ch1 == 'u') { - skip(2); + if (convertUnicode && curCh0 == '\\' && charAt(pos + 1) == 'u') { + // Save the current position. + final int savePosition = position; + reset(pos + 2); final int ch = unicodeEscapeSequence(TokenType.IDENT); if (Character.isBmpCodePoint(ch) && isWhitespace((char)ch)) { return null; @@ -873,17 +855,16 @@ private String valueOfIdent(final int start, final int length) throws RuntimeExc } else { sb.appendCodePoint(ch); } + pos = position; + reset(savePosition); } else { // Add regular character. - sb.append(ch0); - skip(1); + sb.append(curCh0); + pos++; } } - // Restore position. - reset(savePosition); - - return sb.toString(); + return stringIntern(sb.toString()); } /** @@ -952,7 +933,7 @@ private String valueOfString(final int start, final int length, final boolean st // octal escape sequences are not allowed (eg. "\02", "\31"). // See section 7.8.4 String literals production EscapeSequence if (next != '0' || (ch0 >= '0' && ch0 <= '9')) { - error(Lexer.message("strict.no.octal"), STRING, position, limit); + error(Lexer.message("strict.no.octal"), STRING, position, limit - position); } } reset(afterSlash); @@ -1048,7 +1029,7 @@ private String valueOfString(final int start, final int length, final boolean st // Restore position. reset(savePosition); - return sb.toString(); + return stringIntern(sb.toString()); } /** @@ -1073,7 +1054,7 @@ protected void scanString(final boolean add) { type = ESCSTRING; skip(1); if (!isEscapeCharacter(ch0)) { - error(Lexer.message("invalid.escape.char"), STRING, position, limit); + error(Lexer.message("invalid.escape.char"), STRING, position, limit - position); } if (isEOL(ch0)) { // Multiline string literal @@ -1090,7 +1071,7 @@ protected void scanString(final boolean add) { // Skip close quote. skip(1); } else { - error(Lexer.message("missing.close.quote"), STRING, position, limit); + error(Lexer.message("missing.close.quote"), STRING, position, limit - position); } // If not just scanning. @@ -1129,14 +1110,31 @@ protected void scanString(final boolean add) { } /** - * Scan over a template string literal. + * Scan a template literal, stopping at the first expression. */ private void scanTemplate() { assert ch0 == '`'; - TokenType type = TEMPLATE; - - // Skip over quote and record beginning of string content. + // Skip over quote skip(1); + + scanTemplateString(TEMPLATE); + } + + /** + * Continue scanning a template literal after an expression. + */ + protected final void scanTemplateSpan() { + scanTemplateString(TEMPLATE_MIDDLE); + } + + /** + * Scan a template literal string span. + */ + private void scanTemplateString(TokenType type) { + assert type == TEMPLATE || type == TEMPLATE_MIDDLE; + // already skipped over '`' or '}' + + // Record beginning of string content. State stringState = saveState(); // Scan until close quote @@ -1152,27 +1150,12 @@ private void scanTemplate() { skip(2); stringState.setLimit(position - 2); add(type == TEMPLATE ? TEMPLATE_HEAD : type, stringState.position, stringState.limit); - - // scan to RBRACE - Lexer expressionLexer = new Lexer(this, saveState()); - expressionLexer.templateExpressionOpenBraces = 1; - expressionLexer.lexify(); - restoreState(expressionLexer.saveState()); - - // scan next middle or tail of the template literal - assert ch0 == '}'; - type = TEMPLATE_MIDDLE; - - // Skip over rbrace and record beginning of string content. - skip(1); - stringState = saveState(); - - continue; + return; } else if (ch0 == '\\') { skip(1); // EscapeSequence if (!isEscapeCharacter(ch0)) { - error(Lexer.message("invalid.escape.char"), TEMPLATE, position, limit); + error(Lexer.message("invalid.escape.char"), TEMPLATE, position, limit - position); } if (isEOL(ch0)) { // LineContinuation @@ -1189,7 +1172,7 @@ private void scanTemplate() { skip(1); } - error(Lexer.message("missing.close.quote"), TEMPLATE, position, limit); + error(Lexer.message("missing.close.quote"), TEMPLATE, position, limit - position); } /** @@ -1212,7 +1195,7 @@ protected boolean isEscapeCharacter(final char ch) { private static Number valueOf(final String valueString, final int radix) throws NumberFormatException { try { final long value = Long.parseLong(valueString, radix); - if (value >= MIN_INT_L && value <= MAX_INT_L) { + if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) { return (int)value; } return value; @@ -1240,6 +1223,26 @@ private static Number valueOf(final String valueString, final int radix) throws } } + private static BigInteger valueOfBigInt(final String valueString) { + if (valueString.length() > 2 && valueString.charAt(0) == '0') { + switch (valueString.charAt(1)) { + case 'x': + case 'X': + return new BigInteger(valueString.substring(2), 16); + case 'o': + case 'O': + return new BigInteger(valueString.substring(2), 8); + case 'b': + case 'B': + return new BigInteger(valueString.substring(2), 2); + default: + return new BigInteger(valueString, 10); + } + } else { + return new BigInteger(valueString, 10); + } + } + /** * Scan a number. */ @@ -1291,7 +1294,10 @@ protected void scanNumber() { // Skip remaining digits. while ((digit = convertDigit(ch0, 10)) != -1) { // Check octal only digits. - octal = octal && digit < 8; + if (octal && digit >= 8) { + octal = false; + type = NON_OCTAL_DECIMAL; + } // Skip digit. skip(1); } @@ -1327,6 +1333,12 @@ protected void scanNumber() { } } + if (ch0 == 'n' && type != FLOATING) { + // Skip n + skip(1); + type = BIGINT; + } + if (Character.isJavaIdentifierStart(ch0)) { error(Lexer.message("missing.space.after.number"), type, position, 1); } @@ -1409,6 +1421,72 @@ private boolean scanXMLLiteral() { return false; } + /** + * Determines if the specified character is permissible as the first character in a ECMAScript identifier. + * + * @param codePoint the character to be tested + * @return {@code true} if the character may start an ECMAScript identifier; {@code false} otherwise + */ + private static boolean isJSIdentifierStart(int codePoint) { + return (Character.isUnicodeIdentifierStart(codePoint) && codePoint != '\u2e2f') + || codePoint == '$' + || codePoint == '_' + || isOtherIDStart(codePoint); + } + + /** + * Determines if the specified character has Other_ID_Start Unicode property. + * + * @param codePoint the character to be tested. + * @return {@code true} if the character has Other_ID_Start Unicode property, + * {@code false} otherwise. + */ + private static boolean isOtherIDStart(int codePoint) { + return codePoint == 0x1885 + || codePoint == 0x1886 + || codePoint == 0x2118 + || codePoint == 0x212e + || codePoint == 0x309b + || codePoint == 0x309c; + } + + /** + * Determines if the specified character may be part of an ECMAScript identifier as other than the first character. + * + * @param codePoint the character to be tested + * @return {@code true} if the character may be part of an ECMAScript identifier; {@code false} otherwise + */ + private static boolean isJSIdentifierPart(int codePoint) { + return (Character.isUnicodeIdentifierPart(codePoint) && !Character.isIdentifierIgnorable(codePoint) && codePoint != '\u2e2f') + || codePoint == '$' + || codePoint == '\u200c' // + || codePoint == '\u200d' // + || isOtherIDContinue(codePoint); + } + + /** + * Determines if the specified character has Other_ID_Continue Unicode property. + * + * @param codePoint the character to be tested. + * @return {@code true} if the character has Other_ID_Continue Unicode property, + * {@code false} otherwise. + */ + private static boolean isOtherIDContinue(int codePoint) { + return isOtherIDStart(codePoint) + || codePoint == 0x00b7 + || codePoint == 0x0387 + || codePoint == 0x1369 + || codePoint == 0x136a + || codePoint == 0x136b + || codePoint == 0x136c + || codePoint == 0x136d + || codePoint == 0x136e + || codePoint == 0x136f + || codePoint == 0x1370 + || codePoint == 0x1371 + || codePoint == 0x19da; + } + /** * Scan over identifier characters. * @@ -1420,12 +1498,16 @@ private int scanIdentifier() { // Make sure first character is valid start character. if (ch0 == '\\' && ch1 == 'u') { skip(2); - final int ch = unicodeEscapeSequence(TokenType.IDENT); + final int codePoint = unicodeEscapeSequence(TokenType.IDENT); - if (!Character.isJavaIdentifierStart(ch)) { - error(Lexer.message("illegal.identifier.character"), TokenType.IDENT, start, position); + if (!isJSIdentifierStart(codePoint)) { + error(Lexer.message("illegal.identifier.character"), TokenType.IDENT, start, position - start); } - } else if (!Character.isJavaIdentifierStart(ch0)) { + } else if (isJSIdentifierStart(ch0)) { + skip(1); + } else if (Character.isHighSurrogate(ch0) && Character.isLowSurrogate(ch1) && isJSIdentifierStart(Character.toCodePoint(ch0, ch1))) { + skip(2); + } else { // Not an identifier. return 0; } @@ -1434,13 +1516,15 @@ private int scanIdentifier() { while (!atEOF()) { if (ch0 == '\\' && ch1 == 'u') { skip(2); - final int ch = unicodeEscapeSequence(TokenType.IDENT); + final int codePoint = unicodeEscapeSequence(TokenType.IDENT); - if (!Character.isJavaIdentifierPart(ch)) { - error(Lexer.message("illegal.identifier.character"), TokenType.IDENT, start, position); + if (!isJSIdentifierPart(codePoint)) { + error(Lexer.message("illegal.identifier.character"), TokenType.IDENT, start, position - start); } - } else if (Character.isJavaIdentifierPart(ch0)) { + } else if (isJSIdentifierPart(ch0)) { skip(1); + } else if (Character.isHighSurrogate(ch0) && Character.isLowSurrogate(ch1) && isJSIdentifierPart(Character.toCodePoint(ch0, ch1))) { + skip(2); } else { break; } @@ -1462,7 +1546,7 @@ private int scanIdentifier() { private boolean identifierEqual(final int aStart, final int aLength, final int bStart, final int bLength) { if (aLength == bLength) { for (int i = 0; i < aLength; i++) { - if (content.charAt(aStart + i) != content.charAt(bStart + i)) { + if (content[aStart + i] != content[bStart + i]) { return false; } } @@ -1599,7 +1683,7 @@ public void lexify() { if (stringStart != limit) { // Concatenate remaining string. if (primed) { - add(ADD, stringStart, 1); + add(ADD, stringStart, stringStart + 1); } add(stringType, stringStart, limit); @@ -1628,7 +1712,7 @@ private void editString(final TokenType stringType, final State stringState) { * * @return TRUE if is a here string. */ - private boolean scanHereString(final LineInfoReceiver lir) { + private boolean scanHereString(final LineInfoReceiver lir, final State oldState) { assert ch0 == '<' && ch1 == '<'; if (scripting) { // Record beginning of here string. @@ -1653,7 +1737,7 @@ private boolean scanHereString(final LineInfoReceiver lir) { final int identLength = scanIdentifier(); if (noStringEditing) { if (ch0 != quoteChar) { - error(Lexer.message("here.non.matching.delimiter"), last, position, position); + error(Lexer.message("here.non.matching.delimiter"), last, position, 0); restoreState(saved); return false; } @@ -1678,6 +1762,11 @@ private boolean scanHereString(final LineInfoReceiver lir) { int lastLinePosition = position; restState.setLimit(position); + if (oldState.position > position) { + restoreState(oldState); + skipLine(false); + } + // Record beginning of string. final State stringState = saveState(); int stringEnd = position; @@ -1705,7 +1794,7 @@ private boolean scanHereString(final LineInfoReceiver lir) { // If marker is missing. if (stringState.isEmpty() || atEOF()) { - error(Lexer.message("here.missing.end.marker", source.getString(identStart, identLength)), last, position, position); + error(Lexer.message("here.missing.end.marker", source.getString(identStart, identLength)), last, position, 0); restoreState(saved); return false; @@ -1714,12 +1803,12 @@ private boolean scanHereString(final LineInfoReceiver lir) { // Remove last end of line if specified. if (excludeLastEOL) { // Handles \n. - if (content.charAt(stringEnd - 1) == '\n') { + if (content[stringEnd - 1] == '\n') { stringEnd--; } // Handles \r and \r\n. - if (content.charAt(stringEnd - 1) == '\r') { + if (content[stringEnd - 1] == '\r') { stringEnd--; } @@ -1785,16 +1874,6 @@ public void lexify() { // Scan and add a number. scanNumber(); } else if ((type = TokenLookup.lookupOperator(ch0, ch1, ch2, ch3)) != null) { - if (templateExpressionOpenBraces > 0) { - if (type == LBRACE) { - templateExpressionOpenBraces++; - } else if (type == RBRACE) { - if (--templateExpressionOpenBraces == 0) { - break; - } - } - } - // Get the number of characters in the token. final int typeLength = type.getLength(); // Skip that many characters. @@ -1808,8 +1887,11 @@ public void lexify() { } else if (type == LBRACE && pauseOnNextLeftBrace) { pauseOnNextLeftBrace = false; break; + } else if (type == RBRACE && pauseOnRightBrace) { + break; } - } else if (Character.isJavaIdentifierStart(ch0) || ch0 == '\\' && ch1 == 'u') { + } else if (isJSIdentifierStart((Character.isHighSurrogate(ch0) && Character.isLowSurrogate(ch1)) ? Character.toCodePoint(ch0, ch1) : ch0) + || (ch0 == '\\' && ch1 == 'u')) { // Scan and add identifier or keyword. scanIdentifierOrKeyword(); } else if (isStringDelimiter(ch0)) { @@ -1821,6 +1903,8 @@ public void lexify() { } else if (isTemplateDelimiter(ch0) && es6) { // Scan and add template in ES6 mode. scanTemplate(); + // Let the parser continue from here. + break; } else if (isTemplateDelimiter(ch0) && scripting) { // Scan and add an exec string ('`') in scripting mode. scanString(true); @@ -1839,11 +1923,23 @@ public void lexify() { * @return JavaScript value. */ Object getValueOf(final long token, final boolean strict) { + return getValueOf(token, strict, true); + } + + /** + * Return value of token given its token descriptor. + * + * @param token Token descriptor. + * @param convertUnicode Perform Unicode conversion. + * @return JavaScript value. + */ + Object getValueOf(final long token, final boolean strict, final boolean convertUnicode) { final int start = Token.descPosition(token); final int len = Token.descLength(token); switch (Token.descType(token)) { case DECIMAL: + case NON_OCTAL_DECIMAL: return Lexer.valueOf(source.getString(start, len), 10); // number case HEXADECIMAL: return Lexer.valueOf(source.getString(start + 2, len - 2), 16); // number @@ -1853,6 +1949,8 @@ Object getValueOf(final long token, final boolean strict) { return Lexer.valueOf(source.getString(start + 2, len - 2), 8); // number case BINARY_NUMBER: return Lexer.valueOf(source.getString(start + 2, len - 2), 2); // number + case BIGINT: + return Lexer.valueOfBigInt(source.getString(start, len - 1)); // number case FLOATING: final String str = source.getString(start, len); final double value = Double.valueOf(str); @@ -1872,11 +1970,11 @@ Object getValueOf(final long token, final boolean strict) { } return value; case STRING: - return source.getString(start, len); // String + return stringIntern(source.getString(start, len)); // String case ESCSTRING: return valueOfString(start, len, strict); // String case IDENT: - return valueOfIdent(start, len); // String + return valueOfIdent(start, len, convertUnicode); // String case REGEX: return valueOfPattern(start, len); // RegexToken::LexerToken case TEMPLATE: @@ -1895,6 +1993,26 @@ Object getValueOf(final long token, final boolean strict) { return null; } + /** + * Returns the Template Value of the specified part of a tagged template literal. + * + * @param token template string token. + * @return Template Value if the value is string, returns {@code null} + * otherwise (i.e. if the value is undefined). + */ + String valueOfTaggedTemplateString(final long token) { + final int savePosition = position; + + try { + return valueOfString(Token.descPosition(token), Token.descLength(token), true); + } catch (ParserException ex) { + // An invalid escape sequence in a tagged template string is not an error. + return null; + } finally { + reset(savePosition); + } + } + /** * Get the raw string value of a template literal string part. * @@ -1931,7 +2049,12 @@ public String valueOfRawString(final long token) { // Restore position. reset(savePosition); - return sb.toString(); + return stringIntern(sb.toString()); + } + + public String stringIntern(String candidate) { + String interned = internedStrings.putIfAbsent(candidate, candidate); + return interned == null ? candidate : interned; } /** diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Namespace.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Namespace.java index 07260efaee..76c58eb765 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Namespace.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Namespace.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Options.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Options.java index 3c32a8c043..ab51525a1f 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Options.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Options.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java index d766877f9a..877ab509d9 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Parser.java @@ -1,32 +1,52 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; import static com.oracle.js.parser.TokenType.ARROW; +import static com.oracle.js.parser.TokenType.AS; import static com.oracle.js.parser.TokenType.ASSIGN; +import static com.oracle.js.parser.TokenType.ASSIGN_INIT; +import static com.oracle.js.parser.TokenType.ASYNC; +import static com.oracle.js.parser.TokenType.AWAIT; import static com.oracle.js.parser.TokenType.CASE; import static com.oracle.js.parser.TokenType.CATCH; import static com.oracle.js.parser.TokenType.CLASS; @@ -45,7 +65,9 @@ import static com.oracle.js.parser.TokenType.EXPORT; import static com.oracle.js.parser.TokenType.EXTENDS; import static com.oracle.js.parser.TokenType.FINALLY; +import static com.oracle.js.parser.TokenType.FROM; import static com.oracle.js.parser.TokenType.FUNCTION; +import static com.oracle.js.parser.TokenType.GET; import static com.oracle.js.parser.TokenType.IDENT; import static com.oracle.js.parser.TokenType.IF; import static com.oracle.js.parser.TokenType.IMPORT; @@ -60,7 +82,10 @@ import static com.oracle.js.parser.TokenType.RBRACKET; import static com.oracle.js.parser.TokenType.RPAREN; import static com.oracle.js.parser.TokenType.SEMICOLON; +import static com.oracle.js.parser.TokenType.SET; +import static com.oracle.js.parser.TokenType.SPREAD_ARGUMENT; import static com.oracle.js.parser.TokenType.SPREAD_ARRAY; +import static com.oracle.js.parser.TokenType.SPREAD_OBJECT; import static com.oracle.js.parser.TokenType.STATIC; import static com.oracle.js.parser.TokenType.STRING; import static com.oracle.js.parser.TokenType.SUPER; @@ -75,10 +100,9 @@ import static com.oracle.js.parser.TokenType.YIELD; import static com.oracle.js.parser.TokenType.YIELD_STAR; -import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; -import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -91,6 +115,7 @@ import com.oracle.js.parser.ir.BaseNode; import com.oracle.js.parser.ir.BinaryNode; import com.oracle.js.parser.ir.Block; +import com.oracle.js.parser.ir.BlockExpression; import com.oracle.js.parser.ir.BlockStatement; import com.oracle.js.parser.ir.BreakNode; import com.oracle.js.parser.ir.CallNode; @@ -101,13 +126,20 @@ import com.oracle.js.parser.ir.DebuggerNode; import com.oracle.js.parser.ir.EmptyNode; import com.oracle.js.parser.ir.ErrorNode; +import com.oracle.js.parser.ir.ExportClauseNode; +import com.oracle.js.parser.ir.ExportNode; +import com.oracle.js.parser.ir.ExportSpecifierNode; import com.oracle.js.parser.ir.Expression; import com.oracle.js.parser.ir.ExpressionList; import com.oracle.js.parser.ir.ExpressionStatement; import com.oracle.js.parser.ir.ForNode; +import com.oracle.js.parser.ir.FromNode; import com.oracle.js.parser.ir.FunctionNode; import com.oracle.js.parser.ir.IdentNode; import com.oracle.js.parser.ir.IfNode; +import com.oracle.js.parser.ir.ImportClauseNode; +import com.oracle.js.parser.ir.ImportNode; +import com.oracle.js.parser.ir.ImportSpecifierNode; import com.oracle.js.parser.ir.IndexNode; import com.oracle.js.parser.ir.JoinPredecessorExpression; import com.oracle.js.parser.ir.LabelNode; @@ -117,8 +149,11 @@ import com.oracle.js.parser.ir.Module; import com.oracle.js.parser.ir.Module.ExportEntry; import com.oracle.js.parser.ir.Module.ImportEntry; +import com.oracle.js.parser.ir.NameSpaceImportNode; +import com.oracle.js.parser.ir.NamedImportsNode; import com.oracle.js.parser.ir.Node; import com.oracle.js.parser.ir.ObjectNode; +import com.oracle.js.parser.ir.ParameterNode; import com.oracle.js.parser.ir.PropertyKey; import com.oracle.js.parser.ir.PropertyNode; import com.oracle.js.parser.ir.ReturnNode; @@ -144,15 +179,26 @@ public class Parser extends AbstractParser { private static final String ARGUMENTS_NAME = "arguments"; /** The eval function variable name. */ private static final String EVAL_NAME = "eval"; + private static final String CONSTRUCTOR_NAME = "constructor"; + private static final String PROTO_NAME = "__proto__"; + private static final String NEW_TARGET_NAME = "new.target"; + private static final String PROTOTYPE_NAME = "prototype"; + /** EXEC name - special property used by $EXEC API. */ private static final String EXEC_NAME = "$EXEC"; - /** Function name prefix for anonymous functions. */ - private static final String ANON_FUNCTION_PREFIX = "L:"; + /** Function name for anonymous functions. */ + private static final String ANONYMOUS_FUNCTION_NAME = ":anonymous"; /** Function name for the program entry point. */ private static final String PROGRAM_NAME = ":program"; - /** Function name prefix for arrow functions. */ - private static final String ARROW_FUNCTION_PREFIX = "=>:"; - private static final char NESTED_FUNCTION_SEPARATOR = '#'; + /** Internal lexical binding name for {@code catch} error (destructuring). */ + private static final String ERROR_BINDING_NAME = ":error"; + /** Internal lexical binding name for {@code switch} expression. */ + private static final String SWITCH_BINDING_NAME = ":switch"; + /** Function name for arrow functions. */ + private static final String ARROW_FUNCTION_NAME = ":=>"; + + private static final String FUNCTION_PARAMETER_CONTEXT = "function parameter"; + private static final String CATCH_PARAMETER_CONTEXT = "catch parameter"; private static final boolean ES6_FOR_OF = Options.getBooleanProperty("parser.for.of", true); private static final boolean ES6_CLASS = Options.getBooleanProperty("parser.class", true); @@ -166,6 +212,15 @@ public class Parser extends AbstractParser { private static final boolean ES6_DEFAULT_PARAMETER = Options.getBooleanProperty("parser.default.parameter", true); private static final boolean ES6_NEW_TARGET = Options.getBooleanProperty("parser.new.target", true); + private static final boolean ES8_TRAILING_COMMA = Options.getBooleanProperty("parser.trailing.comma", true); + private static final boolean ES8_ASYNC_FUNCTION = Options.getBooleanProperty("parser.async.function", true); + private static final boolean ES8_REST_SPREAD_PROPERTY = Options.getBooleanProperty("parser.rest.spread.property", true); + private static final boolean ES8_FOR_AWAIT_OF = Options.getBooleanProperty("parser.for.await.of", true); + private static final boolean ES2019_OPTIONAL_CATCH_BINDING = Options.getBooleanProperty("parser.optional.catch.binding", true); + + private static final int REPARSE_IS_PROPERTY_ACCESSOR = 1 << 0; + private static final int REPARSE_IS_METHOD = 1 << 1; + /** Current env. */ private final ScriptEnvironment env; @@ -178,7 +233,7 @@ public class Parser extends AbstractParser { private List functionDeclarations; private final ParserContext lc; - private final Deque defaultNames; + private final List defaultNames; /** Namespace for function names where not explicitly given */ private final Namespace namespace; @@ -188,6 +243,12 @@ public class Parser extends AbstractParser { private RecompilableScriptFunctionData reparsedFunction; + private boolean isModule; + + public static final boolean PROFILE_PARSING = Options.getBooleanProperty("parser.profiling", false); + public static final boolean PROFILE_PARSING_PRINT = Options.getBooleanProperty("parser.profiling.print", true); + public static long totalParsingDuration = 0L; + /** * Constructor * @@ -223,7 +284,7 @@ public Parser(final ScriptEnvironment env, final Source source, final ErrorManag public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset) { super(source, errors, strict, lineOffset); this.lc = new ParserContext(); - this.defaultNames = new ArrayDeque<>(); + this.defaultNames = new ArrayList<>(); this.env = env; this.namespace = new Namespace(env.getNamespace()); this.scripting = env.scripting; @@ -243,15 +304,6 @@ public void lineInfo(final int receiverLine, final int receiverLinePosition) { } } - /** - * Sets the name for the first function. This is only used when reparsing anonymous functions to ensure they can - * preserve their already assigned name, as that name doesn't appear in their source text. - * @param name the name for the first parsed function. - */ - public void setFunctionName(final String name) { - defaultNames.push(createIdentNode(0, 0, name)); - } - /** * Execute parse and return the resulting function node. * Errors will be thrown and the error manager will contain information @@ -262,7 +314,7 @@ public void setFunctionName(final String name) { * @return function node resulting from successful parse */ public FunctionNode parse() { - return parse(PROGRAM_NAME, 0, source.getLength(), false); + return parse(PROGRAM_NAME, 0, source.getLength(), 0); } /** @@ -283,6 +335,19 @@ private void scanFirstToken() { next(); } + /** + * Prepare {@link TokenStream} and {@link Lexer} for parsing. + * + * @param startPos source start position + * @param len source length + */ + private void prepareLexer(final int startPos, final int len) { + stream = new TokenStream(); + lexer = new Lexer(source, startPos, len, stream, scripting && env.syntaxExtensions, env.es6, shebang && env.syntaxExtensions, reparsedFunction != null); + lexer.line = lexer.pendingLine = lineOffset + 1; + line = lineOffset; + } + /** * Execute parse and return the resulting function node. * Errors will be thrown and the error manager will contain information @@ -293,26 +358,32 @@ private void scanFirstToken() { * @param scriptName name for the script, given to the parsed FunctionNode * @param startPos start position in source * @param len length of parse - * @param allowPropertyFunction if true, "get" and "set" are allowed as first tokens of the program, followed by - * a property getter or setter function. This is used when reparsing a function that can potentially be defined as a - * property getter or setter in an object literal. + * @param reparseFlags flags provided by {@link RecompilableScriptFunctionData} as context for + * the code being reparsed. This allows us to recognize special forms of functions such + * as property getters and setters or instances of ES6 method shorthand in object literals. * * @return function node resulting from successful parse */ - public FunctionNode parse(final String scriptName, final int startPos, final int len, final boolean allowPropertyFunction) { + public FunctionNode parse(final String scriptName, final int startPos, final int len, final int reparseFlags) { + long startTime = PROFILE_PARSING ? System.nanoTime() : 0L; try { - stream = new TokenStream(); - lexer = new Lexer(source, startPos, len, stream, scripting && env.syntaxExtensions, env.es6, shebang && env.syntaxExtensions, reparsedFunction != null); - lexer.line = lexer.pendingLine = lineOffset + 1; - line = lineOffset; + prepareLexer(startPos, len); scanFirstToken(); - // Begin parse. - return program(scriptName, allowPropertyFunction); + + return program(scriptName, reparseFlags); } catch (final Exception e) { handleParseException(e); return null; + } finally { + if (PROFILE_PARSING) { + long duration = (System.nanoTime() - startTime); + totalParsingDuration += duration; + if (PROFILE_PARSING_PRINT) { + System.out.println("Parsing: " + duration / 1_000_000); + } + } } } @@ -329,13 +400,10 @@ public FunctionNode parse(final String scriptName, final int startPos, final int */ public FunctionNode parseModule(final String moduleName, final int startPos, final int len) { try { - stream = new TokenStream(); - lexer = new Lexer(source, startPos, len, stream, scripting && env.syntaxExtensions, env.es6, shebang && env.syntaxExtensions, reparsedFunction != null); - lexer.line = lexer.pendingLine = lineOffset + 1; - line = lineOffset; + prepareLexer(startPos, len); scanFirstToken(); - // Begin parse. + return module(moduleName); } catch (final Exception e) { handleParseException(e); @@ -354,20 +422,17 @@ public FunctionNode parseModule(final String moduleName) { * Errors will be thrown and the error manager will contain information * if parsing should fail. This method is used to check if parameter Strings * passed to "Function" constructor is a valid or not. - * - * @return the list of IdentNodes representing the formal parameter list */ - public List parseFormalParameterList() { + public void parseFormalParameterList() { try { stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && env.syntaxExtensions, env.es6, shebang && env.syntaxExtensions); scanFirstToken(); - return formalParameterList(TokenType.EOF, false); + formalParameterList(TokenType.EOF, false, false); } catch (final Exception e) { handleParseException(e); - return null; } } @@ -379,7 +444,7 @@ public List parseFormalParameterList() { * * @return function node resulting from successful parse */ - public FunctionNode parseFunctionBody() { + public FunctionNode parseFunctionBody(boolean generator, boolean async) { try { stream = new TokenStream(); lexer = new Lexer(source, stream, scripting && env.syntaxExtensions, env.es6, shebang && env.syntaxExtensions); @@ -392,21 +457,25 @@ public FunctionNode parseFunctionBody() { // Set up the function to append elements. final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM_NAME); - final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, FunctionNode.Kind.NORMAL, functionLine, Collections.emptyList()); - lc.push(function); + final FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; + final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, functionKind, functionLine, Collections.emptyList(), 0); + if (async) { + function.setFlag(FunctionNode.IS_ASYNC); + } + lc.push(function); final ParserContextBlockNode body = newBlock(); - functionDeclarations = new ArrayList<>(); - sourceElements(false); - addFunctionDeclarations(function); - functionDeclarations = null; - - restoreBlock(body); + try { + sourceElements(0); + addFunctionDeclarations(function); + } finally { + functionDeclarations = null; + restoreBlock(body); + lc.pop(function); + } body.setFlag(Block.NEEDS_SCOPE); - final Block functionBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); - lc.pop(function); expect(EOF); @@ -414,7 +483,6 @@ public FunctionNode parseFunctionBody() { function, functionToken, ident, - Collections.emptyList(), FunctionNode.Kind.NORMAL, functionLine, functionBody); @@ -503,20 +571,16 @@ private ParserContextBlockNode newBlock() { return lc.push(new ParserContextBlockNode(token)); } - private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final FunctionNode.Kind kind, final int functionLine, final List parameters) { - // Build function name. - final StringBuilder sb = new StringBuilder(); + private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final FunctionNode.Kind kind, final int functionLine) { + return createParserContextFunctionNode(ident, functionToken, kind, functionLine, null, 0); + } + private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final FunctionNode.Kind kind, final int functionLine, + final List parameters, int functionLength) { final ParserContextFunctionNode parentFunction = lc.getCurrentFunction(); - if (parentFunction != null && !parentFunction.isProgram()) { - sb.append(parentFunction.getName()).append(NESTED_FUNCTION_SEPARATOR); - } assert ident.getName() != null; - sb.append(ident.getName()); - - final String name = namespace.uniqueName(sb.toString()); - //assert parentFunction != null || name.equals(PROGRAM.symbolName()) : "name = " + name; + final String name = ident.getName(); int flags = 0; if (isStrictMode) { @@ -524,28 +588,35 @@ private ParserContextFunctionNode createParserContextFunctionNode(final IdentNod } if (parentFunction == null) { flags |= FunctionNode.IS_PROGRAM; + flags |= FunctionNode.IS_ANONYMOUS; } - final ParserContextFunctionNode functionNode = new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, kind, parameters); + final ParserContextFunctionNode functionNode = new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, kind, parameters, functionLength); functionNode.setFlag(flags); return functionNode; } - private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine, final Block body) { - assert body.isFunctionBody() || body.getFlag(Block.IS_PARAMETER_BLOCK) && ((BlockStatement) body.getLastStatement()).getBlock().isFunctionBody(); - // Start new block. + private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, + final FunctionNode.Kind kind, final int functionLine, final Block body) { + assert body.isFunctionBody() || (body.isParameterBlock() && ((BlockStatement) body.getLastStatement()).getBlock().isFunctionBody()); + + long lastTokenWithDelimiter = Token.withDelimiter(function.getLastToken()); + // EOL uses length field to store the line number + int lastTokenFinish = Token.descPosition(lastTokenWithDelimiter) + (Token.descType(lastTokenWithDelimiter) == TokenType.EOL ? 0 : Token.descLength(lastTokenWithDelimiter)); + final FunctionNode functionNode = new FunctionNode( source, functionLine, body.getToken(), - Token.descPosition(body.getToken()), + lastTokenFinish, startToken, function.getLastToken(), - namespace, ident, function.getName(), - parameters, + function.getLength(), + function.getParameterCount(), + optimizeList(function.getParameters()), kind, function.getFlags(), body, @@ -582,12 +653,19 @@ private Block getBlock(final boolean needsBraces) { } // Block closing brace. + int realFinish; if (needsBraces) { + expectDontAdvance(RBRACE); + // otherwise in case block containing single braced block the inner + // block could end somewhere later after comments and spaces + realFinish = Token.descPosition(token) + Token.descLength(token); expect(RBRACE); + } else { + realFinish = finish; } final int flags = newBlock.getFlags() | (needsBraces ? 0 : Block.IS_SYNTHETIC); - return new Block(blockToken, Math.max(finish, Token.descPosition(blockToken)), flags, newBlock.getStatements()); + return new Block(blockToken, Math.max(realFinish, Token.descPosition(blockToken)), flags, newBlock.getStatements()); } /** @@ -608,17 +686,17 @@ private List caseStatementList() { * @return Statements. */ private Block getStatement() { - return getStatement(false); + return getStatement(false, false); } - private Block getStatement(boolean labelledStatement) { + private Block getStatement(boolean labelledStatement, boolean mayBeFunctionDeclaration) { if (type == LBRACE) { return getBlock(true); } // Set up new block. Captures first token. final ParserContextBlockNode newBlock = newBlock(); try { - statement(false, false, true, labelledStatement); + statement(false, 0, true, labelledStatement, mayBeFunctionDeclaration); } finally { restoreBlock(newBlock); } @@ -647,7 +725,7 @@ private void detectSpecialFunction(final IdentNode ident) { private void detectSpecialProperty(final IdentNode ident) { if (isArguments(ident)) { // skip over arrow functions, e.g. function f() { return (() => arguments.length)(); } - getCurrentNonArrowFunction().setFlag(FunctionNode.USES_ARGUMENTS); + lc.getCurrentNonArrowFunction().setFlag(FunctionNode.USES_ARGUMENTS); } } @@ -659,6 +737,10 @@ private boolean isES6() { return env.es6; } + private boolean isES8() { + return env.es8; + } + private static boolean isArguments(final String name) { return ARGUMENTS_NAME.equals(name); } @@ -689,6 +771,7 @@ private Expression verifyAssignment(final long op, final Expression lhs, final E switch (opType) { case ASSIGN: + case ASSIGN_INIT: case ASSIGN_ADD: case ASSIGN_BIT_AND: case ASSIGN_BIT_OR: @@ -702,14 +785,18 @@ private Expression verifyAssignment(final long op, final Expression lhs, final E case ASSIGN_SHR: case ASSIGN_SUB: if (lhs instanceof IdentNode) { - if (!checkIdentLValue((IdentNode)lhs)) { + IdentNode ident = (IdentNode)lhs; + if (!checkIdentLValue(ident)) { return referenceError(lhs, rhs, false); } - verifyIdent((IdentNode)lhs, "assignment"); + if (ident.isNewTarget()) { + return referenceError(lhs, rhs, true); + } + verifyStrictIdent(ident, "assignment"); break; } else if (lhs instanceof AccessNode || lhs instanceof IndexNode) { break; - } else if (opType == ASSIGN && isDestructuringLhs(lhs)) { + } else if ((opType == ASSIGN || opType == ASSIGN_INIT) && isDestructuringLhs(lhs)) { verifyDestructuringAssignmentPattern(lhs, "assignment"); break; } else { @@ -725,7 +812,7 @@ private Expression verifyAssignment(final long op, final Expression lhs, final E private boolean isDestructuringLhs(Expression lhs) { if (lhs instanceof ObjectNode || lhs instanceof ArrayLiteralNode) { - return ES6_DESTRUCTURING && isES6(); + return ES6_DESTRUCTURING && isES6() && !lhs.isParenthesized(); } return false; } @@ -738,11 +825,12 @@ protected void verifySpreadElement(Expression lvalue) { if (!checkValidLValue(lvalue, contextString)) { throw error(AbstractParser.message("invalid.lvalue"), lvalue.getToken()); } + lvalue.accept(this); } @Override public boolean enterIdentNode(IdentNode identNode) { - verifyIdent(identNode, contextString); + verifyStrictIdent(identNode, contextString); if (!checkIdentLValue(identNode)) { referenceError(identNode, null, true); return false; @@ -813,7 +901,7 @@ private static UnaryNode incDecExpression(final long firstToken, final TokenType * * Parse the top level script. */ - private FunctionNode program(final String scriptName, final boolean allowPropertyFunction) { + private FunctionNode program(final String scriptName, final int reparseFlags) { // Make a pseudo-token for the script holding its start and length. int functionStart = Math.min(Token.descPosition(Token.withDelimiter(token)), finish); final long functionToken = Token.toDesc(FUNCTION, functionStart, source.getLength() - functionStart); @@ -825,24 +913,25 @@ private FunctionNode program(final String scriptName, final boolean allowPropert functionToken, FunctionNode.Kind.SCRIPT, functionLine, - Collections.emptyList()); + Collections.emptyList(), 0); lc.push(script); final ParserContextBlockNode body = newBlock(); - functionDeclarations = new ArrayList<>(); - sourceElements(allowPropertyFunction); - addFunctionDeclarations(script); - functionDeclarations = null; - - restoreBlock(body); + try { + sourceElements(reparseFlags); + addFunctionDeclarations(script); + } finally { + functionDeclarations = null; + restoreBlock(body); + lc.pop(script); + } body.setFlag(Block.NEEDS_SCOPE); final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); - lc.pop(script); script.setLastToken(token); expect(EOF); - return createFunctionNode(script, functionToken, ident, Collections.emptyList(), FunctionNode.Kind.SCRIPT, functionLine, programBody); + return createFunctionNode(script, functionToken, ident, FunctionNode.Kind.SCRIPT, functionLine, programBody); } /** @@ -861,7 +950,7 @@ private String getDirective(final Node stmt) { // A directive is either a string or an escape string if (tt == TokenType.STRING || tt == TokenType.ESCSTRING) { // Make sure that we don't unescape anything. Return as seen in source! - return source.getString(lit.getStart(), Token.descLength(litToken)); + return source.getString(lit.getStart() + 1, Token.descLength(litToken) - 2); } } } @@ -878,10 +967,10 @@ private String getDirective(final Node stmt) { * * Parse the elements of the script or function. */ - private void sourceElements(final boolean shouldAllowPropertyFunction) { + private void sourceElements(final int reparseFlags) { List directiveStmts = null; boolean checkDirective = true; - boolean allowPropertyFunction = shouldAllowPropertyFunction; + int functionFlags = reparseFlags; final boolean oldStrictMode = isStrictMode; @@ -895,8 +984,8 @@ private void sourceElements(final boolean shouldAllowPropertyFunction) { try { // Get the next element. - statement(true, allowPropertyFunction, false, false); - allowPropertyFunction = false; + statement(true, functionFlags, false, false, true); + functionFlags = 0; // check for directive prologues if (checkDirective) { @@ -920,9 +1009,12 @@ private void sourceElements(final boolean shouldAllowPropertyFunction) { // handle use strict directive if ("use strict".equals(directive)) { - isStrictMode = true; final ParserContextFunctionNode function = lc.getCurrentFunction(); + if (!function.isSimpleParameterList()) { + throw error(AbstractParser.message("use.strict.non.simple.param"), lastStatement.getToken()); + } function.setFlag(FunctionNode.IS_STRICT); + isStrictMode = true; // We don't need to check these, if lexical environment is already strict if (!oldStrictMode && directiveStmts != null) { @@ -935,9 +1027,9 @@ private void sourceElements(final boolean shouldAllowPropertyFunction) { // verify that function name as well as parameter names // satisfy strict mode restrictions. - verifyIdent(function.getIdent(), "function name"); + verifyStrictIdent(function.getIdent(), "function name"); for (final IdentNode param : function.getParameters()) { - verifyIdent(param, "function parameter"); + verifyStrictIdent(param, FUNCTION_PARAMETER_CONTEXT); } } } @@ -1008,66 +1100,66 @@ private void sourceElements(final boolean shouldAllowPropertyFunction) { * GeneratorDeclaration */ private void statement() { - statement(false, false, false, false); + statement(false, 0, false, false, false); } /** * @param topLevel does this statement occur at the "top level" of a script or a function? - * @param allowPropertyFunction allow property "get" and "set" functions? + * @param reparseFlags reparse flags to decide whether to allow property "get" and "set" functions or ES6 methods. * @param singleStatement are we in a single statement context? */ - private void statement(final boolean topLevel, final boolean allowPropertyFunction, final boolean singleStatement, final boolean labelledStatement) { + private void statement(final boolean topLevel, final int reparseFlags, final boolean singleStatement, final boolean labelledStatement, final boolean mayBeFunctionDeclaration) { switch (type) { case LBRACE: block(); - break; + return; case VAR: variableStatement(type); - break; + return; case SEMICOLON: emptyStatement(); - break; + return; case IF: ifStatement(); - break; + return; case FOR: forStatement(); - break; + return; case WHILE: whileStatement(); - break; + return; case DO: doStatement(); - break; + return; case CONTINUE: continueStatement(); - break; + return; case BREAK: breakStatement(); - break; + return; case RETURN: returnStatement(); - break; + return; case WITH: withStatement(); - break; + return; case SWITCH: switchStatement(); - break; + return; case THROW: throwStatement(); - break; + return; case TRY: tryStatement(); - break; + return; case DEBUGGER: debuggerStatement(); - break; + return; case RPAREN: case RBRACKET: case EOF: expect(SEMICOLON); - break; + return; case FUNCTION: // As per spec (ECMA section 12), function declarations as arbitrary statement // is not "portable". Implementation can issue a warning or disallow the same. @@ -1075,56 +1167,104 @@ private void statement(final boolean topLevel, final boolean allowPropertyFuncti // ES6 B.3.2 Labelled Function Declarations // It is a Syntax Error if any strict mode source code matches this rule: // LabelledItem : FunctionDeclaration. - if (!labelledStatement || isStrictMode) { + // ES6 B.3.4 FunctionDeclarations in IfStatement Statement Clauses + if (isStrictMode || !mayBeFunctionDeclaration) { throw error(AbstractParser.message("expected.stmt", "function declaration"), token); } } - functionExpression(true, topLevel || labelledStatement); + functionExpression(true, topLevel || labelledStatement, singleStatement); return; - default: - if (useBlockScope() && (type == LET && lookaheadIsLetDeclaration(false) || type == CONST)) { + case LET: + if (useBlockScope()) { + TokenType lookahead = lookaheadOfLetDeclaration(false); + if (lookahead != null) { // lookahead is let declaration + if (singleStatement) { + // ExpressionStatement should not have "let [" in its lookahead. + // The IDENT check is not needed here - the only purpose of this + // shortcut is to produce the same error mesage as Nashorn. + if (lookahead == LBRACKET || T(k + 1) == IDENT) { + throw error(AbstractParser.message("expected.stmt", "let declaration"), token); + } // else break and call expressionStatement() + } else { + variableStatement(type); + return; + } + } + } + break; + case CONST: + if (useBlockScope()) { if (singleStatement) { - throw error(AbstractParser.message("expected.stmt", type.getName() + " declaration"), token); + throw error(AbstractParser.message("expected.stmt", "const declaration"), token); } variableStatement(type); - break; - } else if (ES6_CLASS && type == CLASS && isES6()) { + return; + } else if (env.constAsVar) { + variableStatement(TokenType.VAR); + return; + } + break; + case CLASS: + if (ES6_CLASS && isES6()) { if (singleStatement) { throw error(AbstractParser.message("expected.stmt", "class declaration"), token); } - classDeclaration(false); - break; + classDeclaration(inGeneratorFunction(), inAsyncFunction(), false); + return; } - if (env.constAsVar && type == CONST) { - variableStatement(TokenType.VAR); - break; + break; + case ASYNC: + if (isAsync() && lookaheadIsAsyncFunction()) { + if (singleStatement) { + throw error(AbstractParser.message("expected.stmt", "async function declaration"), token); + } + asyncFunctionExpression(true, topLevel || labelledStatement); + return; } + break; + default: + break; + } - if (isBindingIdentifier()) { - // FIXME This doesn't handle comment at next lookahead properly! We need a variant of T that skips comments (and probably EOL too)! - if (T(k + 1) == COLON && (type != YIELD || !inGeneratorFunction())) { - labelStatement(); - return; - } - if (allowPropertyFunction) { - final String ident = (String)getValue(); - final long propertyToken = token; - final int propertyLine = line; - if ("get".equals(ident)) { - next(); - addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine)); - return; - } else if ("set".equals(ident)) { - next(); - addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine)); - return; - } - } + if (isBindingIdentifier()) { + if (T(k + 1) == COLON && (type != YIELD || !inGeneratorFunction()) && (!isAwait() || !inAsyncFunction())) { + labelStatement(mayBeFunctionDeclaration); + return; } + if (reparseFlags != 0 && reparseFunctionStatement(reparseFlags)) { + return; + } + } - expressionStatement(); - break; + expressionStatement(); + } + + private boolean reparseFunctionStatement(final int reparseFlags) { + final boolean allowPropertyFunction = (reparseFlags & REPARSE_IS_PROPERTY_ACCESSOR) != 0; + final boolean isES6Method = (reparseFlags & REPARSE_IS_METHOD) != 0; + if (allowPropertyFunction) { + final long propertyToken = token; + final int propertyLine = line; + if (type == GET) { + next(); + addPropertyFunctionStatement(propertyGetterFunction(propertyToken, propertyLine, false, false)); + return true; + } else if (type == SET) { + next(); + addPropertyFunctionStatement(propertySetterFunction(propertyToken, propertyLine, false, false)); + return true; + } + } else if (isES6Method) { + final String ident = (String)getValue(); + IdentNode identNode = createIdentNode(token, finish, ident).setIsPropertyName(); + final long propertyToken = token; + final int propertyLine = line; + next(); + final int flags = CONSTRUCTOR_NAME.equals(ident) ? FunctionNode.IS_CLASS_CONSTRUCTOR : FunctionNode.IS_METHOD; + addPropertyFunctionStatement(propertyMethodFunction(identNode, propertyToken, propertyLine, false, flags, false, false)); + return true; } + return false; } private void addPropertyFunctionStatement(final PropertyFunction propertyFunction) { @@ -1133,38 +1273,58 @@ private void addPropertyFunctionStatement(final PropertyFunction propertyFunctio } /** - * ClassDeclaration[Yield, Default] : - * class BindingIdentifier[?Yield] ClassTail[?Yield] - * [+Default] class ClassTail[?Yield] + * Parse ClassDeclaration. + * + *
+     * ClassDeclaration[Yield, Await, Default] :
+     *   class BindingIdentifier[?Yield, ?Await] ClassTail[?Yield, ?Await]
+     *   [+Default] class ClassTail[?Yield, ?Await]
+     * 
+ * + * @return Class expression node. */ - private ClassNode classDeclaration(boolean isDefault) { + private Expression classDeclaration(boolean yield, boolean await, boolean defaultExport) { + assert type == CLASS; int classLineNumber = line; + long classToken = token; + next(); - ClassNode classExpression = classExpression(!isDefault); + IdentNode className = null; + if (!defaultExport || isBindingIdentifier()) { + className = bindingIdentifier("class name", yield, await); + } - if (!isDefault) { - VarNode classVar = new VarNode(classLineNumber, classExpression.getToken(), classExpression.getIdent().getFinish(), classExpression.getIdent(), classExpression, VarNode.IS_CONST); + Expression classExpression = classTail(classLineNumber, classToken, className, yield, await); + + if (!defaultExport) { + VarNode classVar = new VarNode(classLineNumber, Token.recast(classExpression.getToken(), LET), classExpression.getFinish(), className, classExpression, VarNode.IS_LET); appendStatement(classVar); } return classExpression; } /** - * ClassExpression[Yield] : - * class BindingIdentifier[?Yield]opt ClassTail[?Yield] + * Parse ClassExpression. + * + *
+     * ClassExpression[Yield, Await] :
+     *   class BindingIdentifier[?Yield, ?Await]opt ClassTail[?Yield, ?Await]
+     * 
+ * + * @return Class expression node. */ - private ClassNode classExpression(boolean isStatement) { + private Expression classExpression(boolean yield, boolean await) { assert type == CLASS; int classLineNumber = line; long classToken = token; next(); IdentNode className = null; - if (isStatement || type == IDENT) { - className = getIdent(); + if (isBindingIdentifier()) { + className = bindingIdentifier("class name", yield, await); } - return classTail(classLineNumber, classToken, className); + return classTail(classLineNumber, classToken, className, yield, await); } private static final class ClassElementKey { @@ -1213,14 +1373,14 @@ public boolean equals(Object obj) { * static MethodDefinition[?Yield] * ; */ - private ClassNode classTail(int classLineNumber, long classToken, IdentNode className) { + private Expression classTail(int classLineNumber, long classToken, IdentNode className, boolean yield, boolean await) { boolean oldStrictMode = isStrictMode; isStrictMode = true; try { Expression classHeritage = null; if (type == EXTENDS) { next(); - classHeritage = leftHandSideExpression(); + classHeritage = leftHandSideExpression(yield, await); } expect(LBRACE); @@ -1236,21 +1396,27 @@ private ClassNode classTail(int classLineNumber, long classToken, IdentNode clas if (type == RBRACE) { break; } - long classElementToken = token; boolean isStatic = false; if (type == STATIC) { isStatic = true; next(); } + long classElementToken = token; + int classElementLine = line; + boolean async = false; + if (isAsync() && lookaheadIsAsyncMethod()) { + async = true; + next(); + } boolean generator = false; - if (ES6_GENERATOR_FUNCTION && type == MUL) { + if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) { generator = true; next(); } - PropertyNode classElement = methodDefinition(isStatic, classHeritage != null, generator); + PropertyNode classElement = methodDefinition(isStatic, classHeritage != null, generator, async, classElementToken, classElementLine, yield, await); if (classElement.isComputed()) { classElements.add(classElement); - } else if (!classElement.isStatic() && classElement.getKeyName().equals("constructor")) { + } else if (!classElement.isStatic() && classElement.getKeyName().equals(CONSTRUCTOR_NAME)) { if (constructor == null) { constructor = classElement; } else { @@ -1290,12 +1456,31 @@ private ClassNode classTail(int classLineNumber, long classToken, IdentNode clas long lastToken = token; expect(RBRACE); + classElements.trimToSize(); + int classFinish = Token.descPosition(lastToken) + Token.descLength(lastToken); + if (constructor == null) { constructor = createDefaultClassConstructor(classLineNumber, classToken, lastToken, className, classHeritage != null); + } else { + // constructor takes on source section of class declaration/expression + FunctionNode ctor = (FunctionNode)constructor.getValue(); + int flags = ctor.getFlags(); + if (className == null) { + flags |= FunctionNode.IS_ANONYMOUS; + } + constructor = constructor.setValue(new FunctionNode(ctor.getSource(), ctor.getLineNumber(), ctor.getToken(), classFinish, classToken, lastToken, ctor.getIdent(), className == null ? null : className.getName(), + ctor.getLength(), ctor.getNumOfParams(), ctor.getParameters(), ctor.getKind(), flags, ctor.getBody(), ctor.getEndParserState(), ctor.getModule())); } - classElements.trimToSize(); - return new ClassNode(classLineNumber, classToken, finish, className, classHeritage, constructor, classElements); + ClassNode classBody = new ClassNode(classToken, classFinish, className, classHeritage, constructor, classElements); + if (className == null) { + return classBody; + } else { + List statements = Arrays.asList(new VarNode(classLineNumber, Token.recast(classToken, CONST), classFinish, className, classBody, VarNode.IS_CONST), + new ExpressionStatement(classLineNumber, classToken, classFinish, className)); + Block classBodyBlock = new Block(classToken, classFinish, Block.IS_SYNTHETIC | Block.IS_EXPRESSION_BLOCK, statements); + return new BlockExpression(classToken, classFinish, classBodyBlock); + } } finally { isStrictMode = oldStrictMode; } @@ -1307,8 +1492,8 @@ private PropertyNode createDefaultClassConstructor(int classLineNumber, long cla final List parameters; final long identToken = Token.recast(classToken, TokenType.IDENT); if (subclass) { - IdentNode superIdent = createIdentNode(identToken, ctorFinish, SUPER.getName()).setIsDirectSuper(); - IdentNode argsIdent = createIdentNode(identToken, ctorFinish, "args").setIsRestParameter(); + IdentNode superIdent = new IdentNode(identToken, ctorFinish, SUPER.getName()).setIsDirectSuper(); + IdentNode argsIdent = new IdentNode(identToken, ctorFinish, "args").setIsRestParameter(); Expression spreadArgs = new UnaryNode(Token.recast(classToken, TokenType.SPREAD_ARGUMENT), argsIdent); CallNode superCall = new CallNode(classLineNumber, classToken, ctorFinish, superIdent, Collections.singletonList(spreadArgs), false); statements = Collections.singletonList(new ExpressionStatement(classLineNumber, classToken, ctorFinish, superCall)); @@ -1319,8 +1504,8 @@ private PropertyNode createDefaultClassConstructor(int classLineNumber, long cla } Block body = new Block(classToken, ctorFinish, Block.IS_BODY, statements); - IdentNode ctorName = className != null ? className : createIdentNode(identToken, ctorFinish, "constructor"); - ParserContextFunctionNode function = createParserContextFunctionNode(ctorName, classToken, FunctionNode.Kind.NORMAL, classLineNumber, parameters); + final IdentNode ctorName = className != null ? className : new IdentNode(identToken, ctorFinish, CONSTRUCTOR_NAME); + ParserContextFunctionNode function = createParserContextFunctionNode(ctorName, classToken, FunctionNode.Kind.NORMAL, classLineNumber, parameters, 0); function.setLastToken(lastToken); function.setFlag(FunctionNode.IS_METHOD); @@ -1337,33 +1522,30 @@ private PropertyNode createDefaultClassConstructor(int classLineNumber, long cla function, classToken, ctorName, - parameters, FunctionNode.Kind.NORMAL, classLineNumber, body - ), null, null, false, false); + ), null, null, false, false, false, false); return constructor; } - private PropertyNode methodDefinition(boolean isStatic, boolean subclass, boolean generator) { - final long methodToken = token; - final int methodLine = line; - final boolean computed = type == LBRACKET; - final boolean isIdent = type == IDENT; - Expression propertyName = propertyName(); + private PropertyNode methodDefinition(boolean isStatic, boolean subclass, boolean generator, boolean async, long methodToken, int methodLine, boolean yield, boolean await) { + final TokenType startTokenType = type; + final boolean computed = startTokenType == LBRACKET; + Expression propertyName = propertyName(yield, await); int flags = FunctionNode.IS_METHOD; if (!computed) { final String name = ((PropertyKey)propertyName).getPropertyName(); - if (!generator && isIdent && type != LPAREN && name.equals("get")) { - PropertyFunction methodDefinition = propertyGetterFunction(methodToken, methodLine, flags); + if (!generator && startTokenType == GET && type != LPAREN) { + PropertyFunction methodDefinition = propertyGetterFunction(methodToken, methodLine, yield, await); verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true); - return new PropertyNode(methodToken, finish, methodDefinition.key, null, methodDefinition.functionNode, null, isStatic, methodDefinition.computed); - } else if (!generator && isIdent && type != LPAREN && name.equals("set")) { - PropertyFunction methodDefinition = propertySetterFunction(methodToken, methodLine, flags); + return new PropertyNode(methodToken, finish, methodDefinition.key, null, methodDefinition.functionNode, null, isStatic, methodDefinition.computed, false, false); + } else if (!generator && startTokenType == SET && type != LPAREN) { + PropertyFunction methodDefinition = propertySetterFunction(methodToken, methodLine, yield, await); verifyAllowedMethodName(methodDefinition.key, isStatic, methodDefinition.computed, generator, true); - return new PropertyNode(methodToken, finish, methodDefinition.key, null, null, methodDefinition.functionNode, isStatic, methodDefinition.computed); + return new PropertyNode(methodToken, finish, methodDefinition.key, null, null, methodDefinition.functionNode, isStatic, methodDefinition.computed, false, false); } else { - if (!isStatic && !generator && name.equals("constructor")) { + if (!isStatic && !generator && name.equals(CONSTRUCTOR_NAME)) { flags |= FunctionNode.IS_CLASS_CONSTRUCTOR; if (subclass) { flags |= FunctionNode.IS_SUBCLASS_CONSTRUCTOR; @@ -1372,8 +1554,8 @@ private PropertyNode methodDefinition(boolean isStatic, boolean subclass, boolea verifyAllowedMethodName(propertyName, isStatic, computed, generator, false); } } - PropertyFunction methodDefinition = propertyMethodFunction(propertyName, methodToken, methodLine, generator, flags, computed); - return new PropertyNode(methodToken, finish, methodDefinition.key, methodDefinition.functionNode, null, null, isStatic, computed); + PropertyFunction methodDefinition = propertyMethodFunction(propertyName, methodToken, methodLine, generator, flags, computed, async); + return new PropertyNode(methodToken, finish, methodDefinition.key, methodDefinition.functionNode, null, null, isStatic, computed, false, false); } /** @@ -1381,10 +1563,10 @@ private PropertyNode methodDefinition(boolean isStatic, boolean subclass, boolea */ private void verifyAllowedMethodName(Expression key, boolean isStatic, boolean computed, boolean generator, boolean accessor) { if (!computed) { - if (!isStatic && generator && ((PropertyKey) key).getPropertyName().equals("constructor")) { + if (!isStatic && generator && ((PropertyKey) key).getPropertyName().equals(CONSTRUCTOR_NAME)) { throw error(AbstractParser.message("generator.constructor"), key.getToken()); } - if (!isStatic && accessor && ((PropertyKey) key).getPropertyName().equals("constructor")) { + if (!isStatic && accessor && ((PropertyKey) key).getPropertyName().equals(CONSTRUCTOR_NAME)) { throw error(AbstractParser.message("accessor.constructor"), key.getToken()); } if (isStatic && ((PropertyKey) key).getPropertyName().equals("prototype")) { @@ -1393,6 +1575,34 @@ private void verifyAllowedMethodName(Expression key, boolean isStatic, boolean c } } + private boolean isPropertyName(long currentToken) { + TokenType currentType = Token.descType(currentToken); + if (ES6_COMPUTED_PROPERTY_NAME && currentType == LBRACKET && isES6()) { + // computed property + return true; + } + + switch (currentType) { + case IDENT: + return true; + case NON_OCTAL_DECIMAL: + case OCTAL_LEGACY: + if (isStrictMode) { + return false; + } + case STRING: + case ESCSTRING: + case DECIMAL: + case HEXADECIMAL: + case OCTAL: + case BINARY_NUMBER: + case FLOATING: + return true; + default: + return isIdentifierName(currentToken); + } + } + /** * block : * { StatementList? } @@ -1436,17 +1646,47 @@ private void statementList() { /** * Make sure that the identifier name used is allowed. * - * @param ident Identifier that is verified - * @param contextString String used in error message to give context to the user + * @param ident the identifier that is verified */ - private void verifyIdent(final IdentNode ident, final String contextString) { - verifyStrictIdent(ident, contextString); + private void verifyIdent(final IdentNode ident, final boolean yield, final boolean await) { + // It is a Syntax Error if StringValue of IdentifierName is the same as the StringValue of any ReservedWord except for yield or await. if (isES6()) { - TokenType tokenType = TokenLookup.lookupKeyword(ident.getName(), 0, ident.getName().length()); - if (tokenType != IDENT && tokenType.getKind() != TokenKind.FUTURESTRICT) { - throw error(expectMessage(IDENT)); + if (isEscapedIdent(ident) && isReservedWordSequence(ident.getName())) { + throw error(AbstractParser.message("escaped.keyword", ident.getName()), ident.getToken()); + } else { + assert !isReservedWordSequence(ident.getName()) : ident.getName(); } } + // It is a Syntax Error if this production has a [Yield] parameter and StringValue of Identifier is "yield". + if (yield) { + if (ident.isTokenType(YIELD)) { + throw error(expectMessage(IDENT, ident.getToken()), ident.getToken()); + } else if (isEscapedIdent(ident) && YIELD.getName().equals(ident.getName())) { + throw error(AbstractParser.message("escaped.keyword", ident.getName()), ident.getToken()); + } else { + assert !YIELD.getName().equals(ident.getName()); + } + } + // It is a Syntax Error if this production has an [Await] parameter or if the goal symbol + // of the syntactic grammar is Module and StringValue of Identifier is "await". + if (await || isModule) { + if (ident.isTokenType(AWAIT)) { + throw error(expectMessage(IDENT, ident.getToken()), ident.getToken()); + } else if (isEscapedIdent(ident) && AWAIT.getName().equals(ident.getName())) { + throw error(AbstractParser.message("escaped.keyword", ident.getName()), ident.getToken()); + } else { + assert !AWAIT.getName().equals(ident.getName()); + } + } + } + + private static boolean isEscapedIdent(final IdentNode ident) { + return ident.getName().length() != Token.descLength(ident.getToken()); + } + + private static boolean isReservedWordSequence(final String name) { + TokenType tokenType = TokenLookup.lookupKeyword(name.toCharArray(), 0, name.length()); + return (tokenType != IDENT && !tokenType.isContextualKeyword() && !tokenType.isFutureStrict()); } /** @@ -1553,19 +1793,20 @@ private ForVariableDeclarationListResult variableDeclarationList(final TokenType // Get starting token. final int varLine = line; final long varToken = Token.recast(token, varType); - // Get name of var. - if (type == YIELD && inGeneratorFunction()) { - expect(IDENT); - } + // Get name of var. final String contextString = "variable name"; final Expression binding = bindingIdentifierOrPattern(contextString); final boolean isDestructuring = !(binding instanceof IdentNode); if (isDestructuring) { - final int finalVarFlags = varFlags; + final int finalVarFlags = varFlags | VarNode.IS_DESTRUCTURING; verifyDestructuringBindingPattern(binding, new Consumer() { + @Override public void accept(IdentNode identNode) { - verifyIdent(identNode, contextString); + verifyStrictIdent(identNode, contextString); + if (varType != VAR && identNode.getName().equals(LET.getName())) { + throw error(AbstractParser.message("let.lexical.binding")); // ES8 13.3.1.1 + } final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags); appendStatement(var); } @@ -1584,13 +1825,13 @@ public void accept(IdentNode identNode) { // Get initializer expression. Suppress IN if not statement. if (!isDestructuring) { - defaultNames.push(binding); + pushDefaultName(binding); } try { - init = assignmentExpression(!isStatement); + init = assignmentExpression(isStatement); } finally { if (!isDestructuring) { - defaultNames.pop(); + popDefaultName(); } } } else if (isStatement) { @@ -1605,10 +1846,10 @@ public void accept(IdentNode identNode) { if (!isDestructuring) { assert init != null || varType != CONST || !isStatement; final IdentNode ident = (IdentNode)binding; + if (varType != VAR && ident.getName().equals(LET.getName())) { + throw error(AbstractParser.message("let.lexical.binding")); // ES8 13.3.1.1 + } if (!isStatement) { - if (ident.getName().equals("let")) { - throw error("let is not a valid binding name in a for loop"); // ES6 13.7.5.1 - } if (init == null && varType == CONST) { forResult.recordMissingAssignment(binding); } @@ -1619,7 +1860,7 @@ public void accept(IdentNode identNode) { } else { assert init != null || !isStatement; if (init != null) { - final Expression assignment = verifyAssignment(Token.recast(varToken, ASSIGN), binding, init); + final Expression assignment = verifyAssignment(Token.recast(varToken, ASSIGN_INIT), binding, init); if (isStatement) { appendStatement(new ExpressionStatement(varLine, assignment.getToken(), finish, assignment)); } else { @@ -1645,34 +1886,56 @@ public void accept(IdentNode identNode) { return forResult; } + private boolean isIdentifier() { + return type == IDENT || type.isContextualKeyword() || isNonStrictModeIdent(); + } + + /** + * IdentifierReference or LabelIdentifier. + */ + private IdentNode identifier(boolean yield, boolean await) { + final IdentNode ident = getIdent(); + verifyIdent(ident, yield, await); + return ident; + } + + private IdentNode identifier() { + return identifier(inGeneratorFunction(), inAsyncFunction()); + } + private boolean isBindingIdentifier() { - return type == IDENT || isNonStrictModeIdent(); + return type == IDENT || type.isContextualKeyword() || isNonStrictModeIdent(); } - private IdentNode bindingIdentifier(String contextString) { - final IdentNode name = getIdent(); - verifyIdent(name, contextString); - return name; + private IdentNode bindingIdentifier(String contextString, boolean yield, boolean await) { + final IdentNode ident = getIdent(); + verifyIdent(ident, yield, await); + verifyStrictIdent(ident, contextString); + return ident; } - private Expression bindingPattern() { + private Expression bindingPattern(boolean yield, boolean await) { if (type == LBRACKET) { - return arrayLiteral(); + return arrayLiteral(yield, await); } else if (type == LBRACE) { - return objectLiteral(); + return objectLiteral(yield, await); } else { throw error(AbstractParser.message("expected.binding")); } } - private Expression bindingIdentifierOrPattern(String contextString) { + private Expression bindingIdentifierOrPattern(String contextString, boolean yield, boolean await) { if (isBindingIdentifier() || !(ES6_DESTRUCTURING && isES6())) { - return bindingIdentifier(contextString); + return bindingIdentifier(contextString, yield, await); } else { - return bindingPattern(); + return bindingPattern(yield, await); } } + private Expression bindingIdentifierOrPattern(String contextString) { + return bindingIdentifierOrPattern(contextString, inGeneratorFunction(), inAsyncFunction()); + } + private abstract class VerifyDestructuringPatternNodeVisitor extends NodeVisitor { VerifyDestructuringPatternNodeVisitor(LexicalContext lc) { super(lc); @@ -1681,6 +1944,9 @@ private abstract class VerifyDestructuringPatternNodeVisitor extends NodeVisitor @Override public boolean enterLiteralNode(LiteralNode literalNode) { if (literalNode.isArray()) { + if (literalNode.isParenthesized()) { + throw error(AbstractParser.message("invalid.lvalue"), literalNode.getToken()); + } if (((ArrayLiteralNode)literalNode).hasSpread() && ((ArrayLiteralNode)literalNode).hasTrailingComma()) { throw error("Rest element must be last", literalNode.getElementExpressions().get(literalNode.getElementExpressions().size() - 1).getToken()); } @@ -1694,8 +1960,9 @@ public boolean enterLiteralNode(LiteralNode literalNode) { restElement = true; Expression lvalue = ((UnaryNode) element).getExpression(); verifySpreadElement(lvalue); + } else { + element.accept(this); } - element.accept(this); } } return false; @@ -1708,7 +1975,26 @@ public boolean enterLiteralNode(LiteralNode literalNode) { @Override public boolean enterObjectNode(ObjectNode objectNode) { - return true; + if (objectNode.isParenthesized()) { + throw error(AbstractParser.message("invalid.lvalue"), objectNode.getToken()); + } + boolean restElement = false; + for (PropertyNode property : objectNode.getElements()) { + if (property != null) { + if (restElement) { + throw error("Unexpected element after rest element", property.getToken()); + } + Expression key = property.getKey(); + if (key.isTokenType(SPREAD_OBJECT)) { + restElement = true; + Expression lvalue = ((UnaryNode) key).getExpression(); + verifySpreadElement(lvalue); + } else { + property.accept(this); + } + } + } + return false; } @Override @@ -1731,16 +2017,6 @@ public boolean enterBinaryNode(BinaryNode binaryNode) { return enterDefault(binaryNode); } } - - @Override - public boolean enterUnaryNode(UnaryNode unaryNode) { - if (unaryNode.isTokenType(SPREAD_ARRAY)) { - // rest element - return true; - } else { - return enterDefault(unaryNode); - } - } } /** @@ -1752,7 +2028,7 @@ private void verifyDestructuringBindingPattern(Expression pattern, Consumer + * YieldExpression[In, Await] : * yield - * yield [no LineTerminator here] AssignmentExpression[?In, Yield] - * yield [no LineTerminator here] * AssignmentExpression[?In, Yield] + * yield [no LineTerminator here] AssignmentExpression[?In, +Yield, ?Await] + * yield [no LineTerminator here] * AssignmentExpression[?In, +Yield, ?Await] + * */ - private Expression yieldExpression(boolean noIn) { - assert inGeneratorFunction(); + private Expression yieldExpression(boolean in, boolean await) { + assert inGeneratorFunction() && isES6(); // Capture YIELD token. long yieldToken = token; // YIELD tested in caller. @@ -2335,7 +2613,7 @@ private Expression yieldExpression(boolean noIn) { } default: - expression = assignmentExpression(noIn); + expression = assignmentExpression(in, true, await); break; } @@ -2343,6 +2621,18 @@ private Expression yieldExpression(boolean noIn) { return new UnaryNode(yieldToken, expression); } + private Expression awaitExpression(boolean yield) { + assert inAsyncFunction(); + // Capture await token. + long awaitToken = token; + nextOrEOL(); + + Expression expression = unaryExpression(yield, true); + + // Construct and add AWAIT node. + return new UnaryNode(Token.recast(awaitToken, AWAIT), expression); + } + private static UnaryNode newUndefinedLiteral(long token, int finish) { return new UnaryNode(Token.recast(token, VOID), LiteralNode.newInstance(token, finish, 0)); } @@ -2401,6 +2691,9 @@ private void switchStatement() { final int switchLine = line; final long switchToken = token; + // Block around the switch statement with a variable capturing the switch expression value. + final ParserContextBlockNode outerBlock = useBlockScope() ? newBlock() : null; + // Block to capture variables declared inside the switch statement. final ParserContextBlockNode switchBlock = newBlock(); @@ -2411,19 +2704,29 @@ private void switchStatement() { final ParserContextSwitchNode switchNode = new ParserContextSwitchNode(); lc.push(switchNode); - CaseNode defaultCase = null; + int defaultCaseIndex = -1; // Prepare to accumulate cases. - final List cases = new ArrayList<>(); + final ArrayList cases = new ArrayList<>(); - Expression expression = null; + SwitchNode switchStatement = null; try { expect(LPAREN); - expression = expression(); + int expressionLine = line; + Expression expression = expression(); expect(RPAREN); expect(LBRACE); + // Desugar expression to an assignment to a synthetic let variable in the outer block. + // This simplifies lexical scope analysis (the expression is outside the switch block). + // e.g.: let x = 1; switch (x) { case 0: let x = 2; } => + // let x = 1; { let :switch = x; { let x; switch (:switch) { case 0: x = 2; } } } + if (useBlockScope()) { + IdentNode switchExprName = new IdentNode(Token.recast(expression.getToken(), IDENT), expression.getFinish(), SWITCH_BINDING_NAME); + outerBlock.appendStatement(new VarNode(expressionLine, Token.recast(expression.getToken(), LET), expression.getFinish(), switchExprName, expression, VarNode.IS_LET)); + expression = switchExprName; + } while (type != RBRACE) { // Prepare for next case. @@ -2437,7 +2740,7 @@ private void switchStatement() { break; case DEFAULT: - if (defaultCase != null) { + if (defaultCaseIndex != -1) { throw error(AbstractParser.message("duplicate.default.in.switch")); } next(); @@ -2456,20 +2759,30 @@ private void switchStatement() { final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); if (caseExpression == null) { - defaultCase = caseNode; + assert defaultCaseIndex == -1; + defaultCaseIndex = cases.size(); } cases.add(caseNode); } next(); + + switchStatement = new SwitchNode(switchLine, switchToken, finish, expression, optimizeList(cases), defaultCaseIndex); } finally { lc.pop(switchNode); restoreBlock(switchBlock); - } - SwitchNode switchStatement = new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase); - appendStatement(new BlockStatement(switchLine, new Block(switchToken, finish, switchBlock.getFlags() | Block.IS_SYNTHETIC | Block.IS_SWITCH_BLOCK, switchStatement))); + if (switchStatement != null) { + appendStatement(new BlockStatement(switchLine, new Block(switchToken, switchStatement.getFinish(), switchBlock.getFlags() | Block.IS_SYNTHETIC | Block.IS_SWITCH_BLOCK, switchStatement))); + } + if (outerBlock != null) { + restoreBlock(outerBlock); + if (switchStatement != null) { + appendStatement(new BlockStatement(switchLine, new Block(switchToken, switchStatement.getFinish(), outerBlock.getFlags() | Block.IS_SYNTHETIC, outerBlock.getStatements()))); + } + } + } } /** @@ -2480,11 +2793,11 @@ private void switchStatement() { * * Parse label statement. */ - private void labelStatement() { + private void labelStatement(final boolean mayBeFunctionDeclaration) { // Capture label token. final long labelToken = token; // Get label ident. - final IdentNode ident = getIdent(); + final IdentNode ident = identifier(); expect(COLON); @@ -2496,7 +2809,7 @@ private void labelStatement() { Block body = null; try { lc.push(labelNode); - body = getStatement(true); + body = getStatement(true, mayBeFunctionDeclaration); } finally { lc.pop(labelNode); } @@ -2572,18 +2885,31 @@ private void tryStatement() { // Create try. try { - final Block tryBody = getBlock(true); - final List catchBlocks = new ArrayList<>(); + final Block tryBody = getBlock(true); + final ArrayList catchBlocks = new ArrayList<>(); while (type == CATCH) { final int catchLine = line; final long catchToken = token; next(); + + if (type == LBRACE && ES2019_OPTIONAL_CATCH_BINDING) { + catchBlocks.add(catchBlock(catchToken, catchLine, null, null, null)); + break; + } + expect(LPAREN); - final IdentNode exception = getIdent(); - // ECMA 12.4.1 strict mode restrictions - verifyIdent(exception, "catch argument"); + final Expression catchParameter = bindingIdentifierOrPattern(CATCH_PARAMETER_CONTEXT); + final IdentNode exception; + final Expression pattern; + if (catchParameter instanceof IdentNode) { + exception = ((IdentNode)catchParameter).setIsCatchParameter(); + pattern = null; + } else { + exception = new IdentNode(Token.recast(catchParameter.getToken(), IDENT), catchParameter.getFinish(), ERROR_BINDING_NAME).setIsCatchParameter(); + pattern = catchParameter; + } // Nashorn extension: catch clause can have optional // condition. So, a single try can have more than one @@ -2599,16 +2925,7 @@ private void tryStatement() { expect(RPAREN); - final ParserContextBlockNode catchBlock = newBlock(); - try { - // Get CATCH body. - final Block catchBody = getBlock(true); - final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, false); - appendStatement(catchNode); - } finally { - restoreBlock(catchBlock); - catchBlocks.add(new Block(catchBlock.getToken(), finish, catchBlock.getFlags() | Block.IS_SYNTHETIC, catchBlock.getStatements())); - } + catchBlocks.add(catchBlock(catchToken, catchLine, exception, pattern, ifExpression)); // If unconditional catch then should to be the end. if (ifExpression == null) { @@ -2629,7 +2946,7 @@ private void tryStatement() { throw error(AbstractParser.message("missing.catch.or.finally"), tryToken); } - final TryNode tryNode = new TryNode(tryLine, tryToken, finish, tryBody, catchBlocks, finallyStatements); + final TryNode tryNode = new TryNode(tryLine, tryToken, finish, tryBody, optimizeList(catchBlocks), finallyStatements); // Add try. assert lc.peek() == outer; appendStatement(tryNode); @@ -2640,6 +2957,34 @@ private void tryStatement() { appendStatement(new BlockStatement(startLine, new Block(tryToken, finish, outer.getFlags() | Block.IS_SYNTHETIC, outer.getStatements()))); } + private Block catchBlock(final long catchToken, final int catchLine, final IdentNode exception, final Expression pattern, final Expression ifExpression) { + final ParserContextBlockNode catchBlock = newBlock(); + try { + if (exception != null) { + appendStatement(new VarNode(catchLine, Token.recast(exception.getToken(), LET), exception.getFinish(), exception.setIsDeclaredHere(), null, VarNode.IS_LET)); + if (pattern != null) { + verifyDestructuringBindingPattern(pattern, new Consumer() { + @Override + public void accept(IdentNode identNode) { + verifyStrictIdent(identNode, CATCH_PARAMETER_CONTEXT); + final int varFlags = VarNode.IS_LET | VarNode.IS_DESTRUCTURING; + final VarNode var = new VarNode(catchLine, Token.recast(identNode.getToken(), LET), identNode.getFinish(), identNode.setIsDeclaredHere(), null, varFlags); + appendStatement(var); + } + }); + } + } + + // Get CATCH body. + final Block catchBody = getBlock(true); + final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, pattern, ifExpression, catchBody, false); + appendStatement(catchNode); + } finally { + restoreBlock(catchBlock); + } + return new Block(catchBlock.getToken(), Math.max(finish, Token.descPosition(catchBlock.getToken())), catchBlock.getFlags() | Block.IS_SYNTHETIC, catchBlock.getStatements()); + } + /** * DebuggerStatement : * debugger ; @@ -2679,7 +3024,7 @@ private void debuggerStatement() { * @return Expression node. */ @SuppressWarnings("fallthrough") - private Expression primaryExpression() { + private Expression primaryExpression(boolean yield, boolean await) { // Capture first token. final int primaryLine = line; final long primaryToken = token; @@ -2689,14 +3034,18 @@ private Expression primaryExpression() { final String name = type.getName(); next(); markThis(lc); - return new IdentNode(primaryToken, finish, name); + return new IdentNode(primaryToken, finish, name).setIsThis(); case IDENT: - final IdentNode ident = getIdent(); + final IdentNode ident = identifier(yield, await); if (ident == null) { break; } detectSpecialProperty(ident); return ident; + case NON_OCTAL_DECIMAL: + if (isStrictMode) { + throw error(AbstractParser.message("strict.no.nonoctaldecimal"), token); + } case OCTAL_LEGACY: if (isStrictMode) { throw error(AbstractParser.message("strict.no.octal"), token); @@ -2707,6 +3056,7 @@ private Expression primaryExpression() { case HEXADECIMAL: case OCTAL: case BINARY_NUMBER: + case BIGINT: case FLOATING: case REGEX: case XML: @@ -2723,36 +3073,14 @@ private Expression primaryExpression() { next(); return LiteralNode.newInstance(primaryToken, finish); case LBRACKET: - return arrayLiteral(); + return arrayLiteral(yield, await); case LBRACE: - return objectLiteral(); + return objectLiteral(yield, await); case LPAREN: - next(); - - if (ES6_ARROW_FUNCTION && isES6()) { - if (type == RPAREN) { - // () - nextOrEOL(); - expectDontAdvance(ARROW); - return new ExpressionList(primaryToken, finish, Collections.emptyList()); - } else if (ES6_REST_PARAMETER && type == ELLIPSIS) { - // (...rest) - IdentNode restParam = formalParameterList(false).get(0); - expectDontAdvance(RPAREN); - nextOrEOL(); - expectDontAdvance(ARROW); - return new ExpressionList(primaryToken, finish, Collections.singletonList(restParam)); - } - } - - final Expression expression = expression(); - - expect(RPAREN); - - return expression; + return parenthesizedExpressionAndArrowParameterList(yield, await); case TEMPLATE: case TEMPLATE_HEAD: - return templateLiteral(); + return templateLiteral(yield, await); default: // In this context some operator tokens mark the start of a literal. @@ -2760,8 +3088,8 @@ private Expression primaryExpression() { next(); return getLiteral(); } - if (isNonStrictModeIdent()) { - return getIdent(); + if (type.isContextualKeyword() || isNonStrictModeIdent()) { + return identifier(yield, await); } break; } @@ -2792,6 +3120,8 @@ CallNode execString(final int primaryLine, final long primaryToken) { } /** + * Parse ArrayLiteral. + * * ArrayLiteral : * [ Elision? ] * [ ElementList ] @@ -2805,23 +3135,20 @@ CallNode execString(final int primaryLine, final long primaryToken) { * , * Elision , * - * See 12.1.4 - * JavaScript 1.8 - * - * Parse array literal. * @return Expression node. */ - private LiteralNode arrayLiteral() { + private LiteralNode arrayLiteral(boolean yield, boolean await) { // Capture LBRACKET token. final long arrayToken = token; // LBRACKET tested in caller. next(); // Prepare to accumulate elements. - final List elements = new ArrayList<>(); + final ArrayList elements = new ArrayList<>(); // Track elisions. boolean elision = true; boolean hasSpread = false; + boolean hasCoverInitializedName = false; loop: while (true) { long spreadToken = 0; @@ -2857,12 +3184,13 @@ private LiteralNode arrayLiteral() { } // Add expression element. - Expression expression = assignmentExpression(false); + Expression expression = assignmentExpression(true, yield, await, true); if (expression != null) { if (spreadToken != 0) { expression = new UnaryNode(Token.recast(spreadToken, SPREAD_ARRAY), expression); } elements.add(expression); + hasCoverInitializedName = hasCoverInitializedName || hasCoverInitializedName(expression); } else { expect(RBRACKET); } @@ -2872,7 +3200,7 @@ private LiteralNode arrayLiteral() { } } - return LiteralNode.newInstance(arrayToken, finish, elements, hasSpread, elision); + return LiteralNode.newInstance(arrayToken, finish, optimizeList(elements), hasSpread, elision, hasCoverInitializedName); } /** @@ -2889,7 +3217,7 @@ private LiteralNode arrayLiteral() { * Parse an object literal. * @return Expression node. */ - private ObjectNode objectLiteral() { + private ObjectNode objectLiteral(boolean yield, boolean await) { // Capture LBRACE token. final long objectToken = token; // LBRACE tested in caller. @@ -2897,11 +3225,12 @@ private ObjectNode objectLiteral() { // Object context. // Prepare to accumulate elements. - final List elements = new ArrayList<>(); + final ArrayList elements = new ArrayList<>(); final Map map = new HashMap<>(); // Create a block for the object literal. boolean commaSeen = true; + boolean hasCoverInitializedName = false; loop: while (true) { switch (type) { @@ -2924,9 +3253,10 @@ private ObjectNode objectLiteral() { commaSeen = false; // Get and add the next property. - final PropertyNode property = propertyAssignment(); + final PropertyNode property = propertyDefinition(yield, await); + hasCoverInitializedName = hasCoverInitializedName || property.isCoverInitializedName() || hasCoverInitializedName(property.getValue()); - if (property.isComputed()) { + if (property.isComputed() || property.getKey().isTokenType(SPREAD_OBJECT)) { elements.add(property); break; } @@ -2955,8 +3285,7 @@ private ObjectNode objectLiteral() { if (!isES6()) { checkPropertyRedefinition(property, value, getter, setter, prevValue, prevGetter, prevSetter); } else { - if (property.getKey() instanceof IdentNode && ((IdentNode)property.getKey()).isProtoPropertyName() && - existingProperty.getKey() instanceof IdentNode && ((IdentNode)existingProperty.getKey()).isProtoPropertyName()) { + if (property.isProto() && existingProperty.isProto()) { throw error(AbstractParser.message("multiple.proto.key"), property.getToken()); } } @@ -2975,7 +3304,13 @@ private ObjectNode objectLiteral() { } } - return new ObjectNode(objectToken, finish, elements); + return new ObjectNode(objectToken, finish, optimizeList(elements), hasCoverInitializedName); + } + + private static boolean hasCoverInitializedName(Expression value) { + return (value != null && ( + (value instanceof ObjectNode && ((ObjectNode) value).hasCoverInitializedName()) || + (value instanceof ArrayLiteralNode && ((ArrayLiteralNode) value).hasCoverInitializedName()))); } private void checkPropertyRedefinition(final PropertyNode property, final Expression value, final FunctionNode getter, final FunctionNode setter, final Expression prevValue, final FunctionNode prevGetter, final FunctionNode prevSetter) { @@ -3018,6 +3353,10 @@ private PropertyKey literalPropertyName() { switch (type) { case IDENT: return getIdent().setIsPropertyName(); + case NON_OCTAL_DECIMAL: + if (isStrictMode) { + throw error(AbstractParser.message("strict.no.nonoctaldecimal"), token); + } case OCTAL_LEGACY: if (isStrictMode) { throw error(AbstractParser.message("strict.no.octal"), token); @@ -3029,7 +3368,7 @@ private PropertyKey literalPropertyName() { case OCTAL: case BINARY_NUMBER: case FLOATING: - return getLiteral(); + return (PropertyKey) getLiteral(); default: return getIdentifierName().setIsPropertyName(); } @@ -3041,9 +3380,9 @@ private PropertyKey literalPropertyName() { * * @return PropertyName node */ - private Expression computedPropertyName() { + private Expression computedPropertyName(boolean yield, boolean await) { expect(LBRACKET); - Expression expression = assignmentExpression(false); + Expression expression = assignmentExpression(true, yield, await); expect(RBRACKET); return expression; } @@ -3055,34 +3394,29 @@ private Expression computedPropertyName() { * * @return PropertyName node */ - private Expression propertyName() { + private Expression propertyName(boolean yield, boolean await) { if (ES6_COMPUTED_PROPERTY_NAME && type == LBRACKET && isES6()) { - return computedPropertyName(); + return computedPropertyName(yield, await); } else { return (Expression)literalPropertyName(); } } /** - * PropertyAssignment : - * PropertyName : AssignmentExpression - * get PropertyName ( ) { FunctionBody } - * set PropertyName ( PropertySetParameterList ) { FunctionBody } - * - * PropertySetParameterList : - * Identifier + * Parse an object literal property definition. * - * PropertyName : - * IdentifierName - * StringLiteral - * NumericLiteral + * PropertyDefinition : + * IdentifierReference + * CoverInitializedName + * PropertyName : AssignmentExpression + * MethodDefinition * - * See 11.1.5 + * CoverInitializedName : + * IdentifierReference = AssignmentExpression * - * Parse an object literal property. * @return Property or reference node. */ - private PropertyNode propertyAssignment() { + private PropertyNode propertyDefinition(boolean yield, boolean await) { // Capture firstToken. final long propertyToken = token; final int functionLine = line; @@ -3090,89 +3424,98 @@ private PropertyNode propertyAssignment() { final Expression propertyName; final boolean isIdentifier; + boolean async = false; + if (isAsync() && lookaheadIsAsyncMethod()) { + async = true; + next(); + } boolean generator = false; - if (ES6_GENERATOR_FUNCTION && type == MUL && isES6()) { + if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) { generator = true; next(); } final boolean computed = type == LBRACKET; - if (type == IDENT) { - // Get IDENT. - final String ident = (String)expectValue(IDENT); - - if (type != COLON && (type != LPAREN || !isES6())) { - final long getSetToken = propertyToken; - - switch (ident) { - case "get": - final PropertyFunction getter = propertyGetterFunction(getSetToken, functionLine); - return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed); + if (type == IDENT || (isIdentifier() && !(type == GET || type == SET))) { + isIdentifier = true; + propertyName = getIdent().setIsPropertyName(); + } else if (type == GET || type == SET) { + final TokenType getOrSet = type; + next(); - case "set": - final PropertyFunction setter = propertySetterFunction(getSetToken, functionLine); - return new PropertyNode(propertyToken, finish, setter.key, null, null, setter.functionNode, false, setter.computed); - default: - break; + if (type != COLON && type != COMMARIGHT && type != RBRACE && ((type != ASSIGN && type != LPAREN) || !isES6())) { + final long getOrSetToken = propertyToken; + if (getOrSet == GET) { + final PropertyFunction getter = propertyGetterFunction(getOrSetToken, functionLine, yield, await); + return new PropertyNode(propertyToken, finish, getter.key, null, getter.functionNode, null, false, getter.computed, false, false); + } else if (getOrSet == SET) { + final PropertyFunction setter = propertySetterFunction(getOrSetToken, functionLine, yield, await); + return new PropertyNode(propertyToken, finish, setter.key, null, null, setter.functionNode, false, setter.computed, false, false); } } isIdentifier = true; - IdentNode identNode = createIdentNode(propertyToken, finish, ident).setIsPropertyName(); - if (type == COLON && ident.equals("__proto__")) { - identNode = identNode.setIsProtoPropertyName(); - } - propertyName = identNode; + propertyName = new IdentNode(propertyToken, finish, getOrSet.getName()).setIsPropertyName(); + } else if (type == ELLIPSIS && ES8_REST_SPREAD_PROPERTY && isES8() && !(generator || async)) { + long spreadToken = Token.recast(propertyToken, TokenType.SPREAD_OBJECT); + next(); + Expression assignmentExpression = assignmentExpression(true, yield, await); + Expression spread = new UnaryNode(spreadToken, assignmentExpression); + return new PropertyNode(propertyToken, finish, spread, null, null, null, false, false, false, false); } else { - isIdentifier = isNonStrictModeIdent(); - propertyName = propertyName(); + isIdentifier = false; + propertyName = propertyName(yield, await); } Expression propertyValue; - if (generator) { + if (generator || async) { expectDontAdvance(LPAREN); } + boolean coverInitializedName = false; + boolean proto = false; if (type == LPAREN && isES6()) { - propertyValue = propertyMethodFunction(propertyName, propertyToken, functionLine, generator, FunctionNode.IS_METHOD, computed).functionNode; + propertyValue = propertyMethodFunction(propertyName, propertyToken, functionLine, generator, FunctionNode.IS_METHOD, computed, async).functionNode; } else if (isIdentifier && (type == COMMARIGHT || type == RBRACE || type == ASSIGN) && isES6()) { - propertyValue = createIdentNode(propertyToken, finish, ((IdentNode) propertyName).getPropertyName()); + IdentNode ident = (IdentNode) propertyName; + verifyIdent(ident, yield, await); + propertyValue = createIdentNode(propertyToken, finish, ident.getPropertyName()); if (type == ASSIGN && ES6_DESTRUCTURING) { - // TODO if not destructuring, this is a SyntaxError + // If not destructuring, this is a SyntaxError long assignToken = token; + coverInitializedName = true; next(); - Expression rhs = assignmentExpression(false); + Expression rhs = assignmentExpression(true, yield, await); propertyValue = verifyAssignment(assignToken, propertyValue, rhs); } } else { expect(COLON); - defaultNames.push(propertyName); + if (!computed && PROTO_NAME.equals(((PropertyKey) propertyName).getPropertyName())) { + proto = true; + } + + pushDefaultName(propertyName); try { - propertyValue = assignmentExpression(false); + propertyValue = assignmentExpression(true, yield, await, true); } finally { - defaultNames.pop(); + popDefaultName(); } } - return new PropertyNode(propertyToken, finish, propertyName, propertyValue, null, null, false, computed); + return new PropertyNode(propertyToken, finish, propertyName, propertyValue, null, null, false, computed, coverInitializedName, proto); } - private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine) { - return propertyGetterFunction(getSetToken, functionLine, FunctionNode.IS_METHOD); - } - - private PropertyFunction propertyGetterFunction(final long getSetToken, final int functionLine, final int flags) { + private PropertyFunction propertyGetterFunction(long getSetToken, int functionLine, boolean yield, boolean await) { final boolean computed = type == LBRACKET; - final Expression propertyName = propertyName(); - final String getterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); - final IdentNode getNameNode = createIdentNode(propertyName.getToken(), finish, ("get " + getterName)); + final Expression propertyName = propertyName(yield, await); + final IdentNode getterName = createMethodNameIdent(propertyName, "get "); expect(LPAREN); expect(RPAREN); - final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.emptyList()); - functionNode.setFlag(flags); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getterName, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.emptyList(), 0); + functionNode.setFlag(FunctionNode.IS_METHOD); if (computed) { functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } @@ -3190,8 +3533,7 @@ private PropertyFunction propertyGetterFunction(final long getSetToken, final in final FunctionNode function = createFunctionNode( functionNode, getSetToken, - getNameNode, - Collections.emptyList(), + getterName, FunctionNode.Kind.GETTER, functionLine, functionBody); @@ -3199,22 +3541,16 @@ private PropertyFunction propertyGetterFunction(final long getSetToken, final in return new PropertyFunction(propertyName, function, computed); } - private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) { - return propertySetterFunction(getSetToken, functionLine, FunctionNode.IS_METHOD); - } - - private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine, final int flags) { + private PropertyFunction propertySetterFunction(long getSetToken, int functionLine, boolean yield, boolean await) { final boolean computed = type == LBRACKET; - final Expression propertyName = propertyName(); - final String setterName = propertyName instanceof PropertyKey ? ((PropertyKey) propertyName).getPropertyName() : getDefaultValidFunctionName(functionLine, false); - final IdentNode setNameNode = createIdentNode(propertyName.getToken(), finish, ("set " + setterName)); + final Expression propertyName = propertyName(yield, await); + final IdentNode setterName = createMethodNameIdent(propertyName, "set "); expect(LPAREN); // be sloppy and allow missing setter parameter even though // spec does not permit it! final IdentNode argIdent; if (isBindingIdentifier()) { - argIdent = getIdent(); - verifyIdent(argIdent, "setter argument"); + argIdent = bindingIdentifier("setter argument", false, false); } else { argIdent = null; } @@ -3225,8 +3561,8 @@ private PropertyFunction propertySetterFunction(final long getSetToken, final in } - final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setNameNode, getSetToken, FunctionNode.Kind.SETTER, functionLine, parameters); - functionNode.setFlag(flags); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setterName, getSetToken, FunctionNode.Kind.SETTER, functionLine, parameters, parameters.size()); + functionNode.setFlag(FunctionNode.IS_METHOD); if (computed) { functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } @@ -3243,8 +3579,7 @@ private PropertyFunction propertySetterFunction(final long getSetToken, final in final FunctionNode function = createFunctionNode( functionNode, getSetToken, - setNameNode, - parameters, + setterName, FunctionNode.Kind.SETTER, functionLine, functionBody); @@ -3252,39 +3587,36 @@ private PropertyFunction propertySetterFunction(final long getSetToken, final in return new PropertyFunction(propertyName, function, computed); } - private PropertyFunction propertyMethodFunction(Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, boolean computed) { - final String methodName = key instanceof PropertyKey ? ((PropertyKey) key).getPropertyName() : getDefaultValidFunctionName(methodLine, false); - final IdentNode methodNameNode = createIdentNode(((Node)key).getToken(), finish, methodName); + private PropertyFunction propertyMethodFunction(Expression key, final long methodToken, final int methodLine, final boolean generator, final int flags, boolean computed, boolean async) { + final IdentNode methodNameNode = createMethodNameIdent(key, ""); FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; - final ParserContextFunctionNode functionNode = createParserContextFunctionNode(methodNameNode, methodToken, functionKind, methodLine, null); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(methodNameNode, methodToken, functionKind, methodLine); functionNode.setFlag(flags); if (computed) { functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } + if (async) { + functionNode.setFlag(FunctionNode.IS_ASYNC); + } lc.push(functionNode); try { - ParserContextBlockNode parameterBlock = newBlock(); - final List parameters; - try { - expect(LPAREN); - parameters = formalParameterList(generator); - functionNode.setParameters(parameters); - expect(RPAREN); - } finally { - restoreBlock(parameterBlock); - } + expect(LPAREN); + formalParameterList(generator, async); + expect(RPAREN); Block functionBody = functionBody(functionNode); + verifyParameterList(functionNode); - functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); + if (functionNode.getParameterBlock() != null) { + functionBody = wrapParameterBlock(functionNode.getParameterBlock(), functionBody); + } final FunctionNode function = createFunctionNode( functionNode, methodToken, methodNameNode, - parameters, functionKind, methodLine, functionBody); @@ -3294,6 +3626,27 @@ private PropertyFunction propertyMethodFunction(Expression key, final long metho } } + private IdentNode createMethodNameIdent(Expression propertyKey, String prefix) { + String methodName; + boolean intern = false; + if (propertyKey instanceof IdentNode) { + methodName = ((IdentNode) propertyKey).getPropertyName(); + } else if (propertyKey instanceof PropertyKey) { + methodName = ((PropertyKey) propertyKey).getPropertyName(); + intern = true; + } else { + return new IdentNode(propertyKey.getToken(), propertyKey.getFinish(), getDefaultFunctionName()); + } + if (!prefix.isEmpty()) { + methodName = new StringBuilder(prefix.length() + methodName.length()).append(prefix).append(methodName).toString(); + intern = true; + } + if (intern) { + methodName = lexer.stringIntern(methodName); + } + return createIdentNode(propertyKey.getToken(), propertyKey.getFinish(), methodName); + } + private static class PropertyFunction { final Expression key; final FunctionNode functionNode; @@ -3326,17 +3679,23 @@ private static class PropertyFunction { * Parse left hand side expression. * @return Expression node. */ - private Expression leftHandSideExpression() { + private Expression leftHandSideExpression(boolean yield, boolean await) { int callLine = line; long callToken = token; - Expression lhs = memberExpression(); + Expression lhs = memberExpression(yield, await); if (type == LPAREN) { - final List arguments = optimizeList(argumentList()); + final List arguments = optimizeList(argumentList(yield, await)); // Catch special functions. if (lhs instanceof IdentNode) { + // async () => ... + // async ( ArgumentsList ) => ... + if (ES8_ASYNC_FUNCTION && isES8() && lhs.isTokenType(ASYNC) && type == ARROW && checkNoLineTerminator()) { + return new ExpressionList(callToken, callLine, arguments); + } + detectSpecialFunction((IdentNode)lhs); } @@ -3352,7 +3711,7 @@ private Expression leftHandSideExpression() { switch (type) { case LPAREN: { // Get NEW or FUNCTION arguments. - final List arguments = optimizeList(argumentList()); + final List arguments = optimizeList(argumentList(yield, await)); // Create call node. lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); @@ -3363,7 +3722,7 @@ private Expression leftHandSideExpression() { next(); // Get array index. - final Expression rhs = expression(); + final Expression rhs = expression(true, yield, await); expect(RBRACKET); @@ -3385,7 +3744,7 @@ private Expression leftHandSideExpression() { case TEMPLATE: case TEMPLATE_HEAD: { // tagged template literal - final List arguments = templateLiteralArgumentList(); + final List arguments = templateLiteralArgumentList(yield, await); // Create call node. lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); @@ -3410,20 +3769,20 @@ private Expression leftHandSideExpression() { * Parse new expression. * @return Expression node. */ - private Expression newExpression() { + private Expression newExpression(boolean yield, boolean await) { final long newToken = token; // NEW is tested in caller. next(); if (ES6_NEW_TARGET && type == PERIOD && isES6()) { next(); - if (type == IDENT && "target".equals(getValue())) { + if (type == IDENT && "target".equals(getValueNoEscape())) { if (lc.getCurrentFunction().isProgram()) { throw error(AbstractParser.message("new.target.in.function"), token); } next(); markNewTarget(lc); - return new IdentNode(newToken, finish, "new.target"); + return new IdentNode(newToken, finish, NEW_TARGET_NAME).setIsNewTarget(); } else { throw error(AbstractParser.message("expected.target"), token); } @@ -3431,7 +3790,7 @@ private Expression newExpression() { // Get function base. final int callLine = line; - final Expression constructor = memberExpression(); + final Expression constructor = memberExpression(yield, await); if (constructor == null) { return null; } @@ -3440,7 +3799,7 @@ private Expression newExpression() { // Allow for missing arguments. if (type == LPAREN) { - arguments = argumentList(); + arguments = argumentList(yield, await); } else { arguments = new ArrayList<>(); } @@ -3455,7 +3814,7 @@ private Expression newExpression() { // The object literal following the "new Constructor()" expression // is passed as an additional (last) argument to the constructor. if (env.syntaxExtensions && type == LBRACE) { - arguments.add(objectLiteral()); + arguments.add(objectLiteral(yield, await)); } final CallNode callNode = new CallNode(callLine, constructor.getToken(), finish, constructor, optimizeList(arguments), true); @@ -3486,7 +3845,7 @@ private Expression newExpression() { * Parse member expression. * @return Expression node. */ - private Expression memberExpression() { + private Expression memberExpression(boolean yield, boolean await) { // Prepare to build operation. Expression lhs; boolean isSuper = false; @@ -3494,7 +3853,7 @@ private Expression memberExpression() { switch (type) { case NEW: // Get new expression. - lhs = newExpression(); + lhs = newExpression(yield, await); break; case FUNCTION: @@ -3504,24 +3863,23 @@ private Expression memberExpression() { case CLASS: if (ES6_CLASS && isES6()) { - lhs = classExpression(false); + lhs = classExpression(yield, await); break; - } else { - // fall through } + // fall through case SUPER: if (ES6_CLASS && isES6()) { - ParserContextFunctionNode currentFunction = getCurrentNonArrowFunction(); + ParserContextFunctionNode currentFunction = lc.getCurrentNonArrowFunction(); if (currentFunction.isMethod()) { long identToken = Token.recast(token, IDENT); next(); - lhs = createIdentNode(identToken, finish, SUPER.getName()); + lhs = new IdentNode(identToken, finish, SUPER.getName()).setIsSuper(); switch (type) { case LBRACKET: case PERIOD: - getCurrentNonArrowFunction().setFlag(FunctionNode.USES_SUPER); + currentFunction.setFlag(FunctionNode.USES_SUPER); isSuper = true; break; case LPAREN: @@ -3535,16 +3893,20 @@ private Expression memberExpression() { throw error(AbstractParser.message("invalid.super"), identToken); } break; - } else { - // fall through } - } else { - // fall through } + // fall through + + case ASYNC: + if (isAsync() && lookaheadIsAsyncFunction()) { + lhs = asyncFunctionExpression(false, false); + break; + } + // fall through default: // Get primary expression. - lhs = primaryExpression(); + lhs = primaryExpression(yield, await); break; } @@ -3558,7 +3920,7 @@ private Expression memberExpression() { next(); // Get array index. - final Expression index = expression(); + final Expression index = expression(true, yield, await); expect(RBRACKET); @@ -3595,7 +3957,7 @@ private Expression memberExpression() { case TEMPLATE_HEAD: { // tagged template literal final int callLine = line; - final List arguments = templateLiteralArgumentList(); + final List arguments = templateLiteralArgumentList(yield, await); lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false); @@ -3610,6 +3972,8 @@ private Expression memberExpression() { } /** + * Parse function call arguments. + * * Arguments : * ( ) * ( ArgumentList ) @@ -3620,12 +3984,9 @@ private Expression memberExpression() { * ArgumentList , AssignmentExpression * ArgumentList , ... AssignmentExpression * - * See 11.2 - * - * Parse function call arguments. * @return Argument list. */ - private ArrayList argumentList() { + private ArrayList argumentList(boolean yield, boolean await) { // Prepare to accumulate list of arguments. final ArrayList nodeList = new ArrayList<>(); // LPAREN tested in caller. @@ -3638,6 +3999,10 @@ private ArrayList argumentList() { // Comma prior to every argument except the first. if (!first) { expect(COMMARIGHT); + // Trailing comma. + if (ES8_TRAILING_COMMA && isES8() && type == RPAREN) { + break; + } } else { first = false; } @@ -3649,7 +4014,7 @@ private ArrayList argumentList() { } // Get argument expression. - Expression expression = assignmentExpression(false); + Expression expression = assignmentExpression(true, yield, await); if (spreadToken != 0) { expression = new UnaryNode(Token.recast(spreadToken, TokenType.SPREAD_ARGUMENT), expression); } @@ -3675,6 +4040,38 @@ private static List optimizeList(final ArrayList list) { } } + private static List optimizeList(final List list) { + switch(list.size()) { + case 0: + return Collections.emptyList(); + case 1: + return Collections.singletonList(list.get(0)); + default: + ((ArrayList) list).trimToSize(); + return list; + } + } + + /** + * AsyncFunctionExpression : + * async [no LineTerminator here] function ( FormalParameters[Await] ) { AsyncFunctionBody } + * async [no LineTerminator here] function BindingIdentifier[Await] ( FormalParameters[Await] ) { AsyncFunctionBody } + */ + private Expression asyncFunctionExpression(final boolean isStatement, final boolean topLevel) { + assert isAsync() && lookaheadIsAsyncFunction(); + long asyncToken = token; + nextOrEOL(); + return functionExpression(isStatement, topLevel, true, Token.recast(asyncToken, FUNCTION), false); + } + + private Expression functionExpression(final boolean isStatement, final boolean topLevel) { + return functionExpression(isStatement, topLevel, false, token, false); + } + + private Expression functionExpression(final boolean isStatement, final boolean topLevel, final boolean expressionStatement) { + return functionExpression(isStatement, topLevel, false, token, expressionStatement); + } + /** * FunctionDeclaration : * function Identifier ( FormalParameterList? ) { FunctionBody } @@ -3689,15 +4086,17 @@ private static List optimizeList(final ArrayList list) { * * @return Expression node. */ - private Expression functionExpression(final boolean isStatement, final boolean topLevel) { - final long functionToken = token; - final int functionLine = line; + private Expression functionExpression(final boolean isStatement, final boolean topLevel, final boolean async, final long functionToken, final boolean expressionStatement) { + final int functionLine = line; // FUNCTION is tested in caller. assert type == FUNCTION; next(); boolean generator = false; - if (ES6_GENERATOR_FUNCTION && type == MUL && isES6()) { + if (type == MUL && ES6_GENERATOR_FUNCTION && isES6()) { + if (expressionStatement) { + throw error(AbstractParser.message("expected.stmt", "generator function declaration"), token); + } generator = true; next(); } @@ -3705,18 +4104,13 @@ private Expression functionExpression(final boolean isStatement, final boolean t IdentNode name = null; if (isBindingIdentifier()) { - if (type == YIELD && ((!isStatement && generator) || (isStatement && inGeneratorFunction()))) { - // 12.1.1 Early SyntaxError if: - // GeneratorExpression with BindingIdentifier yield - // HoistableDeclaration with BindingIdentifier yield in generator function body - expect(IDENT); - } - name = getIdent(); - verifyIdent(name, "function name"); + boolean yield = (!isStatement && generator) || (isStatement && inGeneratorFunction()); + boolean await = (!isStatement && async) || (isStatement && inAsyncFunction()); + name = bindingIdentifier("function name", yield, await); } else if (isStatement) { // Nashorn extension: anonymous function statements. // Do not allow anonymous function statement if extensions - // are now allowed. But if we are reparsing then anon function + // are not allowed. But if we are reparsing then anon function // statement is possible - because it was used as function // expression in surrounding code. if (!env.syntaxExtensions && reparsedFunction == null) { @@ -3727,41 +4121,41 @@ private Expression functionExpression(final boolean isStatement, final boolean t // name is null, generate anonymous name boolean isAnonymous = false; if (name == null) { - final String tmpName = getDefaultValidFunctionName(functionLine, isStatement); + final String tmpName = getDefaultFunctionName(); name = new IdentNode(functionToken, Token.descPosition(functionToken), tmpName); isAnonymous = true; } FunctionNode.Kind functionKind = generator ? FunctionNode.Kind.GENERATOR : FunctionNode.Kind.NORMAL; - List parameters = Collections.emptyList(); - final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, functionKind, functionLine, parameters); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, functionKind, functionLine); + if (async) { + functionNode.setFlag(FunctionNode.IS_ASYNC); + } + lc.push(functionNode); Block functionBody = null; // Hide the current default name across function boundaries. E.g. "x3 = function x1() { function() {}}" // If we didn't hide the current default name, then the innermost anonymous function would receive "x3". hideDefaultName(); + try { - ParserContextBlockNode parameterBlock = newBlock(); - try { - expect(LPAREN); - parameters = formalParameterList(generator); - functionNode.setParameters(parameters); - expect(RPAREN); - } finally { - restoreBlock(parameterBlock); - } + expect(LPAREN); + formalParameterList(generator, async); + expect(RPAREN); functionBody = functionBody(functionNode); - - functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); + if (functionNode.getParameterBlock() != null) { + functionBody = wrapParameterBlock(functionNode.getParameterBlock(), functionBody); + } } finally { - defaultNames.pop(); + popDefaultName(); lc.pop(functionNode); } - if (isStatement) { - if (topLevel || useBlockScope() || (!isStrictMode && env.functionDeclarationHoisting && env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.ACCEPT)) { + if (isStatement && !isAnonymous) { + functionNode.setFlag(FunctionNode.IS_STATEMENT); + if (topLevel || useBlockScope() || (!isStrictMode && env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.ACCEPT)) { functionNode.setFlag(FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SyntaxError, AbstractParser.message("strict.no.func.decl.here"), functionToken); @@ -3770,8 +4164,9 @@ private Expression functionExpression(final boolean isStatement, final boolean t } else if (env.functionStatement == ScriptEnvironment.FunctionStatementBehavior.WARNING) { warning(JSErrorType.SyntaxError, AbstractParser.message("no.func.decl.here.warn"), functionToken); } - if (isArguments(name)) { - lc.getCurrentFunction().setFlag(FunctionNode.DEFINES_ARGUMENTS); + if ((topLevel || !useBlockScope()) && isArguments(name)) { + // (only) top-level function declarations override `arguments` + lc.getCurrentFunction().setFlag(FunctionNode.DEFINES_ARGUMENTS); } } @@ -3779,13 +4174,12 @@ private Expression functionExpression(final boolean isStatement, final boolean t functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } - verifyParameterList(parameters, functionNode); + verifyParameterList(functionNode); final FunctionNode function = createFunctionNode( functionNode, functionToken, name, - parameters, functionKind, functionLine, functionBody); @@ -3811,13 +4205,24 @@ private Expression functionExpression(final boolean isStatement, final boolean t return function; } - private void verifyParameterList(final List parameters, final ParserContextFunctionNode functionNode) { + private static Block wrapParameterBlock(ParserContextBlockNode parameterBlock, Block functionBody) { + assert parameterBlock.getFlag(Block.IS_PARAMETER_BLOCK) != 0 && functionBody.isFunctionBody(); + if (parameterBlock.getStatements().isEmpty()) { + return functionBody; + } else { + parameterBlock.getStatements().add(new BlockStatement(functionBody.getFirstStatementLineNumber(), functionBody)); + return new Block(parameterBlock.getToken(), functionBody.getFinish(), parameterBlock.getFlags(), parameterBlock.getStatements()); + } + } + + private void verifyParameterList(final ParserContextFunctionNode functionNode) { IdentNode duplicateParameter = functionNode.getDuplicateParameterBinding(); if (duplicateParameter != null) { - if (functionNode.isStrict() || functionNode.getKind() == FunctionNode.Kind.ARROW || !functionNode.isSimpleParameterList()) { + if (functionNode.isStrict() || functionNode.isMethod() || functionNode.getKind() == FunctionNode.Kind.ARROW || !functionNode.isSimpleParameterList()) { throw error(AbstractParser.message("strict.param.redefinition", duplicateParameter.getName()), duplicateParameter.getToken()); } + final List parameters = functionNode.getParameters(); final int arity = parameters.size(); final HashSet parametersSet = new HashSet<>(arity); @@ -3836,98 +4241,66 @@ private void verifyParameterList(final List parameters, final ParserC } } - private static Block maybeWrapBodyInParameterBlock(Block functionBody, ParserContextBlockNode parameterBlock) { - assert functionBody.isFunctionBody(); - if (!parameterBlock.getStatements().isEmpty()) { - parameterBlock.appendStatement(new BlockStatement(functionBody)); - return new Block(parameterBlock.getToken(), functionBody.getFinish(), (functionBody.getFlags() | Block.IS_PARAMETER_BLOCK) & ~Block.IS_BODY, parameterBlock.getStatements()); - } - return functionBody; - } - - private String getDefaultValidFunctionName(final int functionLine, final boolean isStatement) { - final String defaultFunctionName = getDefaultFunctionName(); - if (isValidIdentifier(defaultFunctionName)) { - if (isStatement) { - // The name will be used as the LHS of a symbol assignment. We add the anonymous function - // prefix to ensure that it can't clash with another variable. - return ANON_FUNCTION_PREFIX + defaultFunctionName; - } - return defaultFunctionName; - } - return ANON_FUNCTION_PREFIX + functionLine; + private void pushDefaultName(final Expression nameExpr) { + defaultNames.add(nameExpr); } - private static boolean isValidIdentifier(final String name) { - if (name == null || name.isEmpty()) { - return false; - } - if (!Character.isJavaIdentifierStart(name.charAt(0))) { - return false; - } - for (int i = 1; i < name.length(); ++i) { - if (!Character.isJavaIdentifierPart(name.charAt(i))) { - return false; - } - } - return true; + private Object popDefaultName() { + return defaultNames.remove(defaultNames.size() - 1); } private String getDefaultFunctionName() { if (!defaultNames.isEmpty()) { - final Object nameExpr = defaultNames.peek(); + final Object nameExpr = defaultNames.get(defaultNames.size() - 1); if (nameExpr instanceof PropertyKey) { markDefaultNameUsed(); return ((PropertyKey)nameExpr).getPropertyName(); } else if (nameExpr instanceof AccessNode) { + AccessNode accessNode = (AccessNode)nameExpr; markDefaultNameUsed(); - return ((AccessNode)nameExpr).getProperty(); + if (accessNode.getBase() instanceof AccessNode) { + AccessNode base = (AccessNode)accessNode.getBase(); + if (base.getBase() instanceof IdentNode && base.getProperty().equals(PROTOTYPE_NAME)) { + return ((IdentNode)base.getBase()).getName() + "." + accessNode.getProperty(); + } + } else if (accessNode.getBase() instanceof IdentNode) { + return ((IdentNode)accessNode.getBase()).getName() + "." + accessNode.getProperty(); + } + return accessNode.getProperty(); } } - return null; + return ANONYMOUS_FUNCTION_NAME; } private void markDefaultNameUsed() { - defaultNames.pop(); + popDefaultName(); hideDefaultName(); } private void hideDefaultName() { // Can be any value as long as getDefaultFunctionName doesn't recognize it as something it can extract a value // from. Can't be null - defaultNames.push(""); + defaultNames.add(""); } /** - * FormalParameterList : - * Identifier - * FormalParameterList , Identifier - * - * See 13 - * * Parse function parameter list. - * @return List of parameter nodes. */ - private List formalParameterList(final boolean yield) { - return formalParameterList(RPAREN, yield); + private void formalParameterList(final boolean yield, final boolean async) { + formalParameterList(RPAREN, yield, async); } /** + * Parse function parameter list. * Same as the other method of the same name - except that the end * token type expected is passed as argument to this method. * * FormalParameterList : * Identifier * FormalParameterList , Identifier - * - * See 13 - * - * Parse function parameter list. - * @return List of parameter nodes. */ - private List formalParameterList(final TokenType endType, final boolean yield) { - // Prepare to gather parameters. - final ArrayList parameters = new ArrayList<>(); + private void formalParameterList(final TokenType endType, final boolean yield, final boolean await) { + final ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); // Track commas. boolean first = true; @@ -3935,6 +4308,10 @@ private List formalParameterList(final TokenType endType, final boole // Comma prior to every argument except the first. if (!first) { expect(COMMARIGHT); + // Trailing comma. + if (ES8_TRAILING_COMMA && isES8() && type == endType) { + break; + } } else { first = false; } @@ -3945,97 +4322,104 @@ private List formalParameterList(final TokenType endType, final boole restParameter = true; } - if (type == YIELD && yield) { - expect(IDENT); + if (type == YIELD && yield || isAwait() && await) { + throw error(expectMessage(IDENT)); } final long paramToken = token; final int paramLine = line; - final String contextString = "function parameter"; IdentNode ident; if (isBindingIdentifier() || restParameter || !(ES6_DESTRUCTURING && isES6())) { - ident = bindingIdentifier(contextString); + ident = bindingIdentifier(FUNCTION_PARAMETER_CONTEXT, yield, await); if (restParameter) { ident = ident.setIsRestParameter(); // rest parameter must be last expectDontAdvance(endType); - parameters.add(ident); - break; - } else if (type == ASSIGN && (ES6_DEFAULT_PARAMETER && isES6())) { + } + if (type == ASSIGN && (ES6_DEFAULT_PARAMETER && isES6())) { next(); - ident = ident.setIsDefaultParameter(); - if (type == YIELD && yield) { + if (type == YIELD && yield || isAwait() && await) { // error: yield in default expression - expect(IDENT); + throw error(expectMessage(IDENT)); } // default parameter - Expression initializer = assignmentExpression(false); + Expression initializer = assignmentExpression(true, yield, await); - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - // desugar to: param = (param === undefined) ? initializer : param; - // possible alternative: if (param === undefined) param = initializer; - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + addDefaultParameter(paramToken, finish, paramLine, ident, initializer, currentFunction); } - } - - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); - if (currentFunction != null) { - currentFunction.addParameterBinding(ident); - if (ident.isRestParameter() || ident.isDefaultParameter()) { - currentFunction.setSimpleParameterList(false); + } else { + if (currentFunction != null) { + currentFunction.addParameter(ident); } } + if (restParameter) { + break; + } } else { - final Expression pattern = bindingPattern(); + final Expression pattern = bindingPattern(yield, await); // Introduce synthetic temporary parameter to capture the object to be destructured. - ident = createIdentNode(paramToken, pattern.getFinish(), String.format("arguments[%d]", parameters.size())).setIsDestructuredParameter(); - verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine, contextString); + verifyDestructuringParameterBindingPattern(pattern, paramToken, paramLine); - Expression value = ident; + Expression initializer = null; if (type == ASSIGN) { next(); - ident = ident.setIsDefaultParameter(); - - // binding pattern with initializer. desugar to: (param === undefined) ? initializer : param - Expression initializer = assignmentExpression(false); - // TODO initializer must not contain yield expression if yield=true (i.e. this is generator function's parameter list) - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + // binding pattern with initializer + initializer = assignmentExpression(true, yield, await); } - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - // destructuring assignment - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), pattern, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + addDestructuringParameter(paramToken, finish, paramLine, pattern, initializer, currentFunction); } } - parameters.add(ident); } + } - parameters.trimToSize(); - return parameters; + private static void addDefaultParameter(long paramToken, int paramFinish, int paramLine, IdentNode target, Expression initializer, ParserContextFunctionNode function) { + assert target != null && initializer != null; + // desugar to: let target = (param === undefined) ? initializer : param; + // we use an special positional parameter node not subjected to TDZ rules; + // thereby, we forego the need for a synthethic param symbol to refer to the passed value. + final int paramIndex = function.getParameterCount(); + final ParameterNode param = new ParameterNode(paramToken, paramFinish, paramIndex); + final BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), param, newUndefinedLiteral(paramToken, paramFinish)); + final Expression value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(param)); + function.addDefaultParameter(new VarNode(paramLine, Token.recast(paramToken, LET), paramFinish, target, value, VarNode.IS_LET)); + } + + private void addDestructuringParameter(long paramToken, int paramFinish, int paramLine, Expression target, Expression initializer, ParserContextFunctionNode function) { + assert isDestructuringLhs(target); + // desugar to: target := (param === undefined) ? initializer : param; + // we use an special positional parameter node not subjected to TDZ rules; + // thereby, we forego the need for a synthethic param symbol to refer to the passed value. + final int paramIndex = function.getParameterCount(); + final ParameterNode param = new ParameterNode(paramToken, paramFinish, paramIndex); + final Expression value; + if (initializer == null) { + value = param; // binding pattern without initializer + } else { + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), param, newUndefinedLiteral(paramToken, paramFinish)); + value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(param)); + } + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN_INIT), target, value); + function.addParameterInitialization(paramLine, assignment, initializer != null); } - private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine, final String contextString) { + private void verifyDestructuringParameterBindingPattern(final Expression pattern, final long paramToken, final int paramLine) { verifyDestructuringBindingPattern(pattern, new Consumer() { + @Override public void accept(IdentNode identNode) { - verifyIdent(identNode, contextString); + verifyStrictIdent(identNode, FUNCTION_PARAMETER_CONTEXT); ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // declare function-scope variables for destructuring bindings - lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); + VarNode declaration = new VarNode(paramLine, Token.recast(paramToken, LET), pattern.getFinish(), identNode, null, VarNode.IS_LET | VarNode.IS_DESTRUCTURING); + currentFunction.addParameterBindingDeclaration(declaration); // detect duplicate bounds names in parameter list - currentFunction.addParameterBinding(identNode); - currentFunction.setSimpleParameterList(false); } } }); @@ -4075,7 +4459,7 @@ private Block functionBody(final ParserContextFunctionNode functionNode) { */ // just expression as function body - final Expression expr = assignmentExpression(false); + final Expression expr = assignmentExpression(true); lastToken = previousToken; functionNode.setLastToken(previousToken); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); @@ -4099,7 +4483,7 @@ private Block functionBody(final ParserContextFunctionNode functionNode) { final List prevFunctionDecls = functionDeclarations; functionDeclarations = new ArrayList<>(); try { - sourceElements(false); + sourceElements(0); addFunctionDeclarations(functionNode); } finally { functionDeclarations = prevFunctionDecls; @@ -4124,7 +4508,7 @@ private Block functionBody(final ParserContextFunctionNode functionNode) { // we'll rather just restart parsing from this well-known, friendly token instead. } } - bodyFinish = finish; + bodyFinish = Token.descPosition(token) + Token.descLength(token); functionNode.setLastToken(token); expect(RBRACE); } @@ -4283,20 +4667,26 @@ private RuntimeNode referenceError(final Expression lhs, final Expression rhs, f * Parse unary expression. * @return Expression node. */ - private Expression unaryExpression() { + private Expression unaryExpression(boolean yield, boolean await) { final int unaryLine = line; final long unaryToken = token; switch (type) { case DELETE: { next(); - final Expression expr = unaryExpression(); + final Expression expr = unaryExpression(yield, await); if (type == TokenType.EXP) { throw error(AbstractParser.message("unexpected.token", type.getNameOrType())); } if (expr instanceof BaseNode || expr instanceof IdentNode) { + if (isStrictMode && expr instanceof IdentNode) { + String varName = ((IdentNode) expr).getName(); + if (!"this".equals(varName)) { + throw error(AbstractParser.message("strict.cant.delete.ident", varName), unaryToken); + } + } return new UnaryNode(unaryToken, expr); } appendStatement(new ExpressionStatement(unaryLine, unaryToken, finish, expr)); @@ -4309,7 +4699,7 @@ private Expression unaryExpression() { case BIT_NOT: case NOT: next(); - final Expression expr = unaryExpression(); + final Expression expr = unaryExpression(yield, await); if (type == TokenType.EXP) { throw error(AbstractParser.message("unexpected.token", type.getNameOrType())); @@ -4322,7 +4712,7 @@ private Expression unaryExpression() { final TokenType opType = type; next(); - final Expression lhs = unaryExpression(); + final Expression lhs = unaryExpression(yield, await); // ++, -- without operand.. if (lhs == null) { throw error(AbstractParser.message("expected.lvalue", type.getNameOrType())); @@ -4331,10 +4721,13 @@ private Expression unaryExpression() { return verifyIncDecExpression(unaryToken, opType, lhs, false); default: + if (isAwait() && await) { + return awaitExpression(yield); + } break; } - Expression expression = leftHandSideExpression(); + Expression expression = leftHandSideExpression(yield, await); if (last != EOL) { switch (type) { @@ -4375,15 +4768,20 @@ private Expression verifyIncDecExpression(final long unaryToken, final TokenType if (!checkIdentLValue((IdentNode)lhs)) { return referenceError(lhs, null, false); } + if (((IdentNode)lhs).isNewTarget()) { + return referenceError(lhs, null, true); + } assert opType == TokenType.INCPREFIX || opType == TokenType.DECPREFIX; String contextString = opType == TokenType.INCPREFIX ? "operand for ++ operator" : "operand for -- operator"; - verifyIdent((IdentNode)lhs, contextString); + verifyStrictIdent((IdentNode)lhs, contextString); } return incDecExpression(unaryToken, opType, lhs, isPostfix); } /** + * Parse Expression. + * * {@code * MultiplicativeExpression : * UnaryExpression @@ -4473,34 +4871,82 @@ private Expression verifyIncDecExpression(final long unaryToken, final TokenType * See 11.14 * } * - * Parse expression. * @return Expression node. */ - protected Expression expression() { - // This method is protected so that subclass can get details - // at expression start point! - + private Expression expression() { // Include commas in expression parsing. - return expression(false); + return expression(true, inGeneratorFunction(), inAsyncFunction()); + } + + private Expression expression(boolean in, boolean yield, boolean await) { + return expression(in, yield, await, false); + } + + private Expression expression(boolean in, boolean yield, boolean await, boolean inPatternPosition) { + Expression assignmentExpression = assignmentExpression(in, yield, await, inPatternPosition); + while (type == COMMARIGHT) { + long commaToken = token; + next(); + + Expression rhs = assignmentExpression(in, yield, await); + assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs); + } + return assignmentExpression; } - private Expression expression(final boolean noIn) { - Expression assignmentExpression = assignmentExpression(noIn); + private Expression parenthesizedExpressionAndArrowParameterList(boolean yield, boolean await) { + long primaryToken = token; + assert type == LPAREN; + next(); + + if (ES6_ARROW_FUNCTION && isES6()) { + if (type == RPAREN) { + // () + nextOrEOL(); + expectDontAdvance(ARROW); + return new ExpressionList(primaryToken, finish, Collections.emptyList()); + } else if (ES6_REST_PARAMETER && type == ELLIPSIS) { + // (...rest) + final IdentNode name = new IdentNode(primaryToken, Token.descPosition(primaryToken), ARROW_FUNCTION_NAME); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, primaryToken, FunctionNode.Kind.ARROW, 0); + // Push a dummy functionNode at the top of the stack to avoid + // pollution of the current function by parameters of the arrow function. + // Real processing/verification of the parameters of the arrow function + // is performed later through convertArrowFunctionParameterList(). + lc.push(functionNode); + try { + formalParameterList(false, false); + expectDontAdvance(RPAREN); + nextOrEOL(); + expectDontAdvance(ARROW); + return new ExpressionList(primaryToken, finish, Collections.singletonList(functionNode.getParameters().get(0))); + } finally { + lc.pop(functionNode); + } + } + } + + Expression assignmentExpression = assignmentExpression(true, yield, await, true); + boolean hasCoverInitializedName = hasCoverInitializedName(assignmentExpression); while (type == COMMARIGHT) { long commaToken = token; next(); boolean rhsRestParameter = false; - if (ES6_ARROW_FUNCTION && ES6_REST_PARAMETER && type == ELLIPSIS && isES6()) { + if (ES6_ARROW_FUNCTION && ES6_REST_PARAMETER && isES6() && type == ELLIPSIS) { // (a, b, ...rest) is not a valid expression, unless we're parsing the parameter list of an arrow function (we need to throw the right error). // But since the rest parameter is always last, at least we know that the expression has to end here and be followed by RPAREN and ARROW, so peek ahead. - if (isRestParameterEndOfArrowFunctionParameterList()) { + if (isRestParameterEndOfArrowParameterList()) { next(); rhsRestParameter = true; } + } else if (ES6_ARROW_FUNCTION && ES8_TRAILING_COMMA && isES8() && type == RPAREN && lookaheadIsArrow()) { + // Trailing comma at end of arrow function parameter list + break; } - Expression rhs = assignmentExpression(noIn); + Expression rhs = assignmentExpression(true, yield, await, true); + hasCoverInitializedName = hasCoverInitializedName || hasCoverInitializedName(rhs); if (rhsRestParameter) { rhs = ((IdentNode)rhs).setIsRestParameter(); @@ -4512,24 +4958,36 @@ private Expression expression(final boolean noIn) { assignmentExpression = new BinaryNode(commaToken, assignmentExpression, rhs); } + + boolean arrowAhead = lookaheadIsArrow(); + if (hasCoverInitializedName && !(type == RPAREN && arrowAhead)) { + throw error(AbstractParser.message("invalid.property.initializer")); + } + + if (!arrowAhead) { + // parenthesized expression + assignmentExpression.makeParenthesized(); + } // else arrow parameter list + + expect(RPAREN); return assignmentExpression; } - private Expression expression(final int minPrecedence, final boolean noIn) { - return expression(unaryExpression(), minPrecedence, noIn); + private Expression expression(int minPrecedence, boolean in, boolean yield, boolean await) { + return expression(unaryExpression(yield, await), minPrecedence, in, yield, await); } private JoinPredecessorExpression joinPredecessorExpression() { return new JoinPredecessorExpression(expression()); } - private Expression expression(final Expression exprLhs, final int minPrecedence, final boolean noIn) { + private Expression expression(Expression exprLhs, int minPrecedence, boolean in, boolean yield, boolean await) { // Get the precedence of the next operator. int precedence = type.getPrecedence(); Expression lhs = exprLhs; // While greater precedence. - while (checkOperator(noIn) && precedence >= minPrecedence) { + while (checkOperator(in) && precedence >= minPrecedence) { // Capture the operator token. final long op = token; @@ -4539,12 +4997,12 @@ private Expression expression(final Expression exprLhs, final int minPrecedence, // Pass expression. Middle expression of a conditional expression can be a "in" // expression - even in the contexts where "in" is not permitted. - final Expression trueExpr = assignmentExpression(false); + final Expression trueExpr = assignmentExpression(true, yield, await); expect(COLON); // Fail expression. - final Expression falseExpr = assignmentExpression(noIn); + final Expression falseExpr = assignmentExpression(in, yield, await); // Build up node. lhs = new TernaryNode(op, lhs, new JoinPredecessorExpression(trueExpr), new JoinPredecessorExpression(falseExpr)); @@ -4554,16 +5012,16 @@ private Expression expression(final Expression exprLhs, final int minPrecedence, assert !Token.descType(op).isAssignment(); // Get the next primary expression. - Expression rhs = unaryExpression(); + Expression rhs = unaryExpression(yield, await); // Get precedence of next operator. int nextPrecedence = type.getPrecedence(); // Subtask greater precedence. - while (checkOperator(noIn) && + while (checkOperator(in) && (nextPrecedence > precedence || nextPrecedence == precedence && !type.isLeftAssociative())) { - rhs = expression(rhs, nextPrecedence, noIn); + rhs = expression(rhs, nextPrecedence, in, yield, await); nextPrecedence = type.getPrecedence(); } lhs = newBinaryExpression(op, lhs, rhs); @@ -4575,8 +5033,16 @@ private Expression expression(final Expression exprLhs, final int minPrecedence, return lhs; } - private boolean checkOperator(final boolean noIn) { - return type.isOperator(noIn) && (type != TokenType.EXP || isES6()); + private boolean checkOperator(final boolean in) { + return type.isOperator(in) && (type != TokenType.EXP || isES6()); + } + + private Expression assignmentExpression(boolean in) { + return assignmentExpression(in, inGeneratorFunction(), inAsyncFunction(), false); + } + + private Expression assignmentExpression(boolean in, boolean yield, boolean await) { + return assignmentExpression(in, yield, await, false); } /** @@ -4586,30 +5052,43 @@ private boolean checkOperator(final boolean noIn) { * ConditionalExpression[?In, ?Yield] * [+Yield] YieldExpression[?In] * ArrowFunction[?In, ?Yield] + * AsyncArrowFunction * LeftHandSideExpression[?Yield] = AssignmentExpression[?In, ?Yield] * LeftHandSideExpression[?Yield] AssignmentOperator AssignmentExpression[?In, ?Yield] */ - protected Expression assignmentExpression(final boolean noIn) { - // This method is protected so that subclass can get details - // at assignment expression start point! - - if (type == YIELD && ES6_GENERATOR_FUNCTION && inGeneratorFunction() && isES6()) { - return yieldExpression(noIn); + private Expression assignmentExpression(boolean in, boolean yield, boolean await, boolean inPatternPosition) { + if (type == YIELD && yield) { + return yieldExpression(in, await); } + boolean asyncArrow = isAsync() && lookaheadIsAsyncArrowParameterListStart(); + // If true, one of: + // 1. async ident + // .. This case is handled below. + // 2. async ( + // .. May turn out to be a CallExpression instead of an AsyncArrowFunction; this is handled + // .. in leftHandSideExpression() which returns an ExpressionList if an AsyncArrowFunction. + final long startToken = token; final int startLine = line; - final Expression exprLhs = conditionalExpression(noIn); + Expression exprLhs = conditionalExpression(in, yield, await); + + if (asyncArrow && exprLhs instanceof IdentNode && isBindingIdentifier() && lookaheadIsArrow()) { + // async ident => + exprLhs = primaryExpression(yield, await); + } if (ES6_ARROW_FUNCTION && type == ARROW && isES6()) { + // Look behind to check there's no LineTerminator between IDENT/RPAREN and ARROW if (checkNoLineTerminator()) { final Expression paramListExpr; if (exprLhs instanceof ExpressionList) { - paramListExpr = (((ExpressionList)exprLhs).getExpressions().isEmpty() ? null : ((ExpressionList)exprLhs).getExpressions().get(0)); + // see primaryExpression() and leftHandSideExpression() + paramListExpr = convertExpressionListToExpression((ExpressionList) exprLhs); } else { paramListExpr = exprLhs; } - return arrowFunction(startToken, startLine, paramListExpr); + return arrowFunction(startToken, startLine, paramListExpr, asyncArrow); } } assert !(exprLhs instanceof ExpressionList); @@ -4617,19 +5096,22 @@ protected Expression assignmentExpression(final boolean noIn) { if (type.isAssignment()) { final boolean isAssign = type == ASSIGN; if (isAssign) { - defaultNames.push(exprLhs); + pushDefaultName(exprLhs); } try { long assignToken = token; next(); - Expression exprRhs = assignmentExpression(noIn); + Expression exprRhs = assignmentExpression(in, yield, await); return verifyAssignment(assignToken, exprLhs, exprRhs); } finally { if (isAssign) { - defaultNames.pop(); + popDefaultName(); } } } else { + if (!inPatternPosition && hasCoverInitializedName(exprLhs)) { + throw error(AbstractParser.message("invalid.property.initializer")); + } return exprLhs; } } @@ -4637,8 +5119,8 @@ protected Expression assignmentExpression(final boolean noIn) { /** * ConditionalExpression. */ - private Expression conditionalExpression(boolean noIn) { - return expression(TERNARY.getPrecedence(), noIn); + private Expression conditionalExpression(boolean in, boolean yield, boolean await) { + return expression(TERNARY.getPrecedence(), in, yield, await); } /** @@ -4648,41 +5130,36 @@ private Expression conditionalExpression(boolean noIn) { * @param functionLine start line of the arrow function * @param paramListExpr ArrowParameters expression or {@code null} for {@code ()} (empty list) */ - private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr) { + private Expression arrowFunction(final long startToken, final int functionLine, final Expression paramListExpr, boolean async) { // caller needs to check that there's no LineTerminator between parameter list and arrow assert type != ARROW || checkNoLineTerminator(); expect(ARROW); final long functionToken = Token.recast(startToken, ARROW); - final IdentNode name = new IdentNode(functionToken, Token.descPosition(functionToken), ARROW_FUNCTION_PREFIX + functionLine); - final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.ARROW, functionLine, null); + final IdentNode name = new IdentNode(functionToken, Token.descPosition(functionToken), ARROW_FUNCTION_NAME); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.ARROW, functionLine); functionNode.setFlag(FunctionNode.IS_ANONYMOUS); + if (async) { + functionNode.setFlag(FunctionNode.IS_ASYNC); + } lc.push(functionNode); try { - ParserContextBlockNode parameterBlock = newBlock(); - final List parameters; - try { - parameters = convertArrowFunctionParameterList(paramListExpr, functionLine); - functionNode.setParameters(parameters); + convertArrowFunctionParameterList(paramListExpr, functionNode); - if (!functionNode.isSimpleParameterList()) { - markEvalInArrowParameterList(parameterBlock); - } - } finally { - restoreBlock(parameterBlock); - } Block functionBody = functionBody(functionNode); - functionBody = maybeWrapBodyInParameterBlock(functionBody, parameterBlock); + verifyParameterList(functionNode); - verifyParameterList(parameters, functionNode); + if (functionNode.getParameterBlock() != null) { + markEvalInArrowParameterList(functionNode.getParameterBlock()); + functionBody = wrapParameterBlock(functionNode.getParameterBlock(), functionBody); + } final FunctionNode function = createFunctionNode( functionNode, functionToken, name, - parameters, FunctionNode.Kind.ARROW, functionLine, functionBody); @@ -4714,90 +5191,140 @@ public boolean enterCallNode(CallNode callNode) { } } - private List convertArrowFunctionParameterList(Expression paramListExpr, int functionLine) { - List parameters; + private static Expression convertExpressionListToExpression(ExpressionList exprList) { + if (exprList.getExpressions().isEmpty()) { + return null; + } else if (exprList.getExpressions().size() == 1) { + return exprList.getExpressions().get(0); + } else { + long token = Token.recast(exprList.getToken(), COMMARIGHT); + Expression result = null; + for (Expression expression : exprList.getExpressions()) { + result = result == null ? expression : newBinaryExpression(token, result, expression); + } + return result; + } + } + + private void convertArrowFunctionParameterList(Expression paramListExpr, ParserContextFunctionNode function) { + final int functionLine = function.getLineNumber(); if (paramListExpr == null) { // empty parameter list, i.e. () => - parameters = Collections.emptyList(); - } else if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr)) { - parameters = Collections.singletonList(verifyArrowParameter(paramListExpr, 0, functionLine)); + return; + } else if (paramListExpr instanceof IdentNode || paramListExpr.isTokenType(ASSIGN) || isDestructuringLhs(paramListExpr) || paramListExpr.isTokenType(SPREAD_ARGUMENT)) { + convertArrowParameter(paramListExpr, 0, functionLine, function); } else if (paramListExpr instanceof BinaryNode && Token.descType(paramListExpr.getToken()) == COMMARIGHT) { - parameters = new ArrayList<>(); + ArrayList params = new ArrayList<>(); Expression car = paramListExpr; do { Expression cdr = ((BinaryNode) car).rhs(); - parameters.add(0, verifyArrowParameter(cdr, parameters.size(), functionLine)); + params.add(cdr); car = ((BinaryNode) car).lhs(); } while (car instanceof BinaryNode && Token.descType(car.getToken()) == COMMARIGHT); - parameters.add(0, verifyArrowParameter(car, parameters.size(), functionLine)); + params.add(car); + + for (int i = params.size() - 1, pos = 0; i >= 0; i--, pos++) { + Expression param = params.get(i); + if (i != 0 && param.isTokenType(SPREAD_ARGUMENT)) { + throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); + } else { + convertArrowParameter(param, pos, functionLine, function); + } + } } else { throw error(AbstractParser.message("expected.arrow.parameter"), paramListExpr.getToken()); } - return parameters; } - private IdentNode verifyArrowParameter(Expression param, int index, int paramLine) { - final String contextString = "function parameter"; + private void convertArrowParameter(Expression param, int index, int paramLine, ParserContextFunctionNode currentFunction) { + assert index == currentFunction.getParameterCount(); if (param instanceof IdentNode) { IdentNode ident = (IdentNode)param; - verifyStrictIdent(ident, contextString); - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + verifyStrictIdent(ident, FUNCTION_PARAMETER_CONTEXT); + if (currentFunction != null && currentFunction.isAsync() && AWAIT.getName().equals(ident.getName())) { + throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); + } if (currentFunction != null) { - currentFunction.addParameterBinding(ident); + currentFunction.addParameter(ident); } - return ident; + return; } if (param.isTokenType(ASSIGN)) { Expression lhs = ((BinaryNode) param).lhs(); long paramToken = lhs.getToken(); Expression initializer = ((BinaryNode) param).rhs(); + if (initializer instanceof IdentNode) { + if (((IdentNode) initializer).getName().equals(AWAIT.getName())) { + throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); + } + } if (lhs instanceof IdentNode) { // default parameter IdentNode ident = (IdentNode) lhs; - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); - - currentFunction.addParameterBinding(ident); - currentFunction.setSimpleParameterList(false); + addDefaultParameter(paramToken, param.getFinish(), paramLine, ident, initializer, currentFunction); } - return ident; + return; } else if (isDestructuringLhs(lhs)) { // binding pattern with initializer - // Introduce synthetic temporary parameter to capture the object to be destructured. - IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter().setIsDefaultParameter(); - verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString); + verifyDestructuringParameterBindingPattern(lhs, paramToken, paramLine); - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + addDestructuringParameter(paramToken, param.getFinish(), paramLine, lhs, initializer, currentFunction); } - return ident; } } else if (isDestructuringLhs(param)) { // binding pattern long paramToken = param.getToken(); - // Introduce synthetic temporary parameter to capture the object to be destructured. - IdentNode ident = createIdentNode(paramToken, param.getFinish(), String.format("arguments[%d]", index)).setIsDestructuredParameter(); - verifyDestructuringParameterBindingPattern(param, paramToken, paramLine, contextString); + verifyDestructuringParameterBindingPattern(param, paramToken, paramLine); - ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, ident); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + addDestructuringParameter(paramToken, param.getFinish(), paramLine, param, null, currentFunction); } - return ident; + } else if (param.isTokenType(SPREAD_ARGUMENT)) { + Expression expression = ((UnaryNode) param).getExpression(); + if (expression instanceof IdentNode && identAtTheEndOfArrowParamList()) { + IdentNode rest = ((IdentNode) expression).setIsRestParameter(); + convertArrowParameter(rest, index, paramLine, currentFunction); + } else { + throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); + } + } else { + throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); } - throw error(AbstractParser.message("invalid.arrow.parameter"), param.getToken()); + } + + // Checks wheter there is IDENT and RPAREN before ARROW. The function + // is used to verify that rest parameter is not followed by a trailing comma. + private boolean identAtTheEndOfArrowParamList() { + int idx = k - 1; + assert T(idx) == ARROW; + while (true) { + idx--; + TokenType t = T(idx); + if (t == COMMENT) { + continue; + } else if (t == RPAREN) { + break; + } else { + return false; + } + } + while (true) { + idx--; + TokenType t = T(idx); + if (t == COMMENT) { + continue; + } else if (t == IDENT) { + break; + } else { + return false; + } + } + return true; } private boolean checkNoLineTerminator() { @@ -4818,7 +5345,7 @@ private boolean checkNoLineTerminator() { case COMMENT: continue; default: - if (t.getKind() == TokenKind.FUTURESTRICT) { + if (t.isContextualKeyword() || t.isFutureStrict()) { return true; } return false; @@ -4831,7 +5358,7 @@ private boolean checkNoLineTerminator() { * Peek ahead to see if what follows after the ellipsis is a rest parameter * at the end of an arrow function parameter list. */ - private boolean isRestParameterEndOfArrowFunctionParameterList() { + private boolean isRestParameterEndOfArrowParameterList() { assert type == ELLIPSIS; // find IDENT, RPAREN, ARROW, in that order, skipping over EOL (where allowed) and COMMENT int i = 1; @@ -4839,6 +5366,8 @@ private boolean isRestParameterEndOfArrowFunctionParameterList() { TokenType t = T(k + i++); if (t == IDENT) { break; + } else if (t.isContextualKeyword()) { + break; } else if (t == EOL || t == COMMENT) { continue; } else { @@ -4868,6 +5397,22 @@ private boolean isRestParameterEndOfArrowFunctionParameterList() { return true; } + private boolean lookaheadIsArrow() { + // find ARROW, skipping over COMMENT + int i = 1; + for (;;) { + TokenType t = T(k + i++); + if (t == ARROW) { + break; + } else if (t == COMMENT) { + continue; + } else { + return false; + } + } + return true; + } + /** * Parse an end of line. */ @@ -4893,37 +5438,55 @@ private void endOfLine() { /** * Parse untagged template literal as string concatenation. */ - private Expression templateLiteral() { + private Expression templateLiteral(boolean yield, boolean await) { assert type == TEMPLATE || type == TEMPLATE_HEAD; final boolean noSubstitutionTemplate = type == TEMPLATE; long lastLiteralToken = token; - LiteralNode literal = getLiteral(); - if (noSubstitutionTemplate) { - return literal; + boolean previousPauseOnRightBrace = lexer.pauseOnRightBrace; + try { + lexer.pauseOnRightBrace = true; + LiteralNode literal = getLiteral(); + if (noSubstitutionTemplate) { + return literal; + } + + Expression concat = literal; + TokenType lastLiteralType; + do { + Expression expression = templateLiteralExpression(yield, await); + expression = new RuntimeNode(Token.recast(expression.getToken(), VOID), expression.getFinish(), RuntimeNode.Request.TO_STRING, expression); + concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, expression); + lastLiteralType = type; + lastLiteralToken = token; + literal = getLiteral(); + concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, literal); + } while (lastLiteralType == TEMPLATE_MIDDLE); + return concat; + } finally { + lexer.pauseOnRightBrace = previousPauseOnRightBrace; } + } - Expression concat = literal; - TokenType lastLiteralType; - do { - Expression expression = expression(); - if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) { - throw error(AbstractParser.message("unterminated.template.expression"), token); - } - expression = new RuntimeNode(Token.recast(expression.getToken(), VOID), expression.getFinish(), RuntimeNode.Request.TO_STRING, expression); - concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, expression); - lastLiteralType = type; - lastLiteralToken = token; - literal = getLiteral(); - concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, literal); - } while (lastLiteralType == TEMPLATE_MIDDLE); - return concat; + /** + * Parse expression inside a template literal. + */ + private Expression templateLiteralExpression(boolean yield, boolean await) { + assert lexer.pauseOnRightBrace; + Expression expression = expression(true, yield, await); + if (type != RBRACE) { + throw error(AbstractParser.message("unterminated.template.expression"), token); + } + lexer.scanTemplateSpan(); + next(); + assert type == TEMPLATE_MIDDLE || type == TEMPLATE_TAIL; + return expression; } /** * Parse tagged template literal as argument list. * @return argument list for a tag function call (template object, ...substitutions) */ - private List templateLiteralArgumentList() { + private List templateLiteralArgumentList(boolean yield, boolean await) { assert type == TEMPLATE || type == TEMPLATE_HEAD; final ArrayList argumentList = new ArrayList<>(); final ArrayList rawStrings = new ArrayList<>(); @@ -4932,36 +5495,46 @@ private List templateLiteralArgumentList() { final long templateToken = token; final boolean hasSubstitutions = type == TEMPLATE_HEAD; - addTemplateLiteralString(rawStrings, cookedStrings); + boolean previousPauseOnRightBrace = lexer.pauseOnRightBrace; + try { + lexer.pauseOnRightBrace = true; + addTemplateLiteralString(rawStrings, cookedStrings); - if (hasSubstitutions) { - TokenType lastLiteralType; - do { - Expression expression = expression(); - if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) { - throw error(AbstractParser.message("unterminated.template.expression"), token); - } - argumentList.add(expression); + if (hasSubstitutions) { + TokenType lastLiteralType; + do { + Expression expression = templateLiteralExpression(yield, await); + argumentList.add(expression); - lastLiteralType = type; - addTemplateLiteralString(rawStrings, cookedStrings); - } while (lastLiteralType == TEMPLATE_MIDDLE); - } + lastLiteralType = type; + addTemplateLiteralString(rawStrings, cookedStrings); + } while (lastLiteralType == TEMPLATE_MIDDLE); + } - final LiteralNode rawStringArray = LiteralNode.newInstance(templateToken, finish, rawStrings); - final LiteralNode cookedStringArray = LiteralNode.newInstance(templateToken, finish, cookedStrings); - final RuntimeNode templateObject = new RuntimeNode(templateToken, finish, RuntimeNode.Request.GET_TEMPLATE_OBJECT, rawStringArray, cookedStringArray); - argumentList.set(0, templateObject); - return optimizeList(argumentList); + final LiteralNode rawStringArray = LiteralNode.newInstance(templateToken, finish, rawStrings); + final LiteralNode cookedStringArray = LiteralNode.newInstance(templateToken, finish, cookedStrings); + final RuntimeNode templateObject = new RuntimeNode(templateToken, finish, RuntimeNode.Request.GET_TEMPLATE_OBJECT, rawStringArray, cookedStringArray); + argumentList.set(0, templateObject); + return optimizeList(argumentList); + } finally { + lexer.pauseOnRightBrace = previousPauseOnRightBrace; + } } private void addTemplateLiteralString(final ArrayList rawStrings, final ArrayList cookedStrings) { final long stringToken = token; final String rawString = lexer.valueOfRawString(stringToken); - final String cookedString = (String) getValue(); + final String cookedString = lexer.valueOfTaggedTemplateString(stringToken); next(); + Expression cookedExpression; + if (cookedString == null) { + // A tagged template string with an invalid escape sequence has value 'undefined' + cookedExpression = newUndefinedLiteral(stringToken, finish); + } else { + cookedExpression = LiteralNode.newInstance(stringToken, finish, cookedString); + } rawStrings.add(LiteralNode.newInstance(stringToken, finish, rawString)); - cookedStrings.add(LiteralNode.newInstance(stringToken, finish, cookedString)); + cookedStrings.add(cookedExpression); } @@ -4975,8 +5548,10 @@ private void addTemplateLiteralString(final ArrayList rawStrings, fi * ModuleItemList */ private FunctionNode module(final String moduleName) { + boolean oldModule = isModule; boolean oldStrictMode = isStrictMode; try { + isModule = true; isStrictMode = true; // Module code is always strict mode code. (ES6 10.2.1) // Make a pseudo-token for the script holding its start and length. @@ -4990,32 +5565,33 @@ private FunctionNode module(final String moduleName) { functionToken, FunctionNode.Kind.MODULE, functionLine, - Collections.emptyList()); + Collections.emptyList(), 0); lc.push(script); final ParserContextModuleNode module = new ParserContextModuleNode(moduleName); - lc.push(module); - final ParserContextBlockNode body = newBlock(); - functionDeclarations = new ArrayList<>(); - moduleBody(); - addFunctionDeclarations(script); - functionDeclarations = null; - restoreBlock(body); + try { + moduleBody(module); + addFunctionDeclarations(script); + } finally { + functionDeclarations = null; + restoreBlock(body); + lc.pop(script); + } + body.setFlag(Block.NEEDS_SCOPE); final Block programBody = new Block(functionToken, finish, body.getFlags() | Block.IS_SYNTHETIC | Block.IS_BODY, body.getStatements()); - lc.pop(module); - lc.pop(script); script.setLastToken(token); expect(EOF); script.setModule(module.createModule()); - return createFunctionNode(script, functionToken, ident, Collections.emptyList(), FunctionNode.Kind.MODULE, functionLine, programBody); + return createFunctionNode(script, functionToken, ident, FunctionNode.Kind.MODULE, functionLine, programBody); } finally { isStrictMode = oldStrictMode; + isModule = oldModule; } } @@ -5034,20 +5610,20 @@ private FunctionNode module(final String moduleName) { * ExportDeclaration * StatementListItem */ - private void moduleBody() { + private void moduleBody(ParserContextModuleNode module) { loop: while (type != EOF) { switch (type) { case EOF: break loop; case IMPORT: - importDeclaration(); + importDeclaration(module); break; case EXPORT: - exportDeclaration(); + exportDeclaration(module); break; default: // StatementListItem - statement(true, false, false, false); + statement(true, 0, false, false, false); break; } } @@ -5073,38 +5649,55 @@ private void moduleBody() { * ImportedBinding : * BindingIdentifier */ - private void importDeclaration() { + private void importDeclaration(ParserContextModuleNode module) { + final long importToken = token; expect(IMPORT); - ParserContextModuleNode module = lc.getCurrentModule(); if (type == STRING || type == ESCSTRING) { // import ModuleSpecifier ; String moduleSpecifier = (String) getValue(); + long specifierToken = token; next(); + LiteralNode specifier = LiteralNode.newInstance(specifierToken, finish, moduleSpecifier); module.addModuleRequest(moduleSpecifier); + module.addImport(new ImportNode(importToken, Token.descPosition(importToken), finish, specifier)); } else { // import ImportClause FromClause ; List importEntries; + ImportClauseNode importClause; + final long startToken = token; if (type == MUL) { - importEntries = Collections.singletonList(nameSpaceImport()); + NameSpaceImportNode namespaceNode = nameSpaceImport(); + importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, namespaceNode); + importEntries = Collections.singletonList( + ImportEntry.importStarAsNameSpaceFrom(namespaceNode.getBindingIdentifier().getName())); } else if (type == LBRACE) { - importEntries = namedImports(); + NamedImportsNode namedImportsNode = namedImports(); + importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, namedImportsNode); + importEntries = convert(namedImportsNode); } else if (isBindingIdentifier()) { // ImportedDefaultBinding - IdentNode importedDefaultBinding = bindingIdentifier("ImportedBinding"); - ImportEntry defaultImport = ImportEntry.importDefault(importedDefaultBinding); + IdentNode importedDefaultBinding = bindingIdentifier("ImportedBinding", false, false); + ImportEntry defaultImport = ImportEntry.importDefault(importedDefaultBinding.getName()); if (type == COMMARIGHT) { next(); - importEntries = new ArrayList<>(); if (type == MUL) { - importEntries.add(nameSpaceImport()); + NameSpaceImportNode namespaceNode = nameSpaceImport(); + importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding, namespaceNode); + importEntries = new ArrayList<>(2); + importEntries.add(defaultImport); + importEntries.add(ImportEntry.importStarAsNameSpaceFrom(namespaceNode.getBindingIdentifier().getName())); } else if (type == LBRACE) { - importEntries.addAll(namedImports()); + NamedImportsNode namedImportsNode = namedImports(); + importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding, namedImportsNode); + importEntries = convert(namedImportsNode); + importEntries.add(0, defaultImport); } else { // expected NameSpaceImport or NamedImports throw error(AbstractParser.message("expected.named.import")); } } else { + importClause = new ImportClauseNode(startToken, Token.descPosition(startToken), finish, importedDefaultBinding); importEntries = Collections.singletonList(defaultImport); } } else { @@ -5112,13 +5705,15 @@ private void importDeclaration() { throw error(AbstractParser.message("expected.import")); } - String moduleSpecifier = fromClause(); + FromNode fromNode = fromClause(); + module.addImport(new ImportNode(importToken, Token.descPosition(importToken), finish, importClause, fromNode)); + String moduleSpecifier = fromNode.getModuleSpecifier().getValue(); module.addModuleRequest(moduleSpecifier); for (int i = 0; i < importEntries.size(); i++) { module.addImportEntry(importEntries.get(i).withFrom(moduleSpecifier)); } } - expect(SEMICOLON); + endOfLine(); } /** @@ -5127,16 +5722,15 @@ private void importDeclaration() { * * @return imported binding identifier */ - private ImportEntry nameSpaceImport() { + private NameSpaceImportNode nameSpaceImport() { + final long startToken = token; assert type == MUL; next(); - long asToken = token; - String as = (String) expectValue(IDENT); - if (!"as".equals(as)) { - throw error(AbstractParser.message("expected.as"), asToken); - } - IdentNode localNameSpace = bindingIdentifier("ImportedBinding"); - return ImportEntry.importStarAsNameSpaceFrom(localNameSpace); + + expect(AS); + + IdentNode localNameSpace = bindingIdentifier("ImportedBinding", false, false); + return new NameSpaceImportNode(startToken, Token.descPosition(startToken), finish, localNameSpace); } /** @@ -5153,23 +5747,27 @@ private ImportEntry nameSpaceImport() { * ImportedBinding : * BindingIdentifier */ - private List namedImports() { + private NamedImportsNode namedImports() { + final long startToken = token; assert type == LBRACE; next(); - List importEntries = new ArrayList<>(); + ArrayList importSpecifiers = new ArrayList<>(); while (type != RBRACE) { boolean bindingIdentifier = isBindingIdentifier(); long nameToken = token; IdentNode importName = getIdentifierName(); - if (type == IDENT && "as".equals(getValue())) { + if (type == AS) { next(); - IdentNode localName = bindingIdentifier("ImportedBinding"); - importEntries.add(ImportEntry.importSpecifier(importName, localName)); - } else if (!bindingIdentifier) { + IdentNode localName = bindingIdentifier("ImportedBinding", false, false); + importSpecifiers.add(new ImportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, importName)); + //importEntries.add(ImportEntry.importSpecifier(importName.getName(), localName.getName())); + } else if (bindingIdentifier) { + verifyStrictIdent(importName, "ImportedBinding"); + importSpecifiers.add(new ImportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, importName, null)); + //importEntries.add(ImportEntry.importSpecifier(importName.getName())); + } else { // expected BindingIdentifier throw error(AbstractParser.message("expected.binding.identifier"), nameToken); - } else { - importEntries.add(ImportEntry.importSpecifier(importName)); } if (type == COMMARIGHT) { next(); @@ -5178,23 +5776,24 @@ private List namedImports() { } } expect(RBRACE); - return importEntries; + return new NamedImportsNode(startToken, Token.descPosition(startToken), finish, optimizeList(importSpecifiers)); } /** * FromClause : * from ModuleSpecifier */ - private String fromClause() { + private FromNode fromClause() { + int fromStart = start; long fromToken = token; - String name = (String) expectValue(IDENT); - if (!"from".equals(name)) { - throw error(AbstractParser.message("expected.from"), fromToken); - } + expect(FROM); + if (type == STRING || type == ESCSTRING) { String moduleSpecifier = (String) getValue(); + long specifierToken = token; next(); - return moduleSpecifier; + LiteralNode specifier = LiteralNode.newInstance(specifierToken, finish, moduleSpecifier); + return new FromNode(fromToken, fromStart, finish, specifier); } else { throw error(expectMessage(STRING)); } @@ -5213,32 +5812,31 @@ private String fromClause() { * export default ClassDeclaration[Default] * export default [lookahead !in {function, class}] AssignmentExpression[In] ; */ - private void exportDeclaration() { + private void exportDeclaration(ParserContextModuleNode module) { + final long exportToken = token; expect(EXPORT); - ParserContextModuleNode module = lc.getCurrentModule(); switch (type) { case MUL: { next(); - String moduleRequest = fromClause(); - expect(SEMICOLON); + FromNode from = fromClause(); + String moduleRequest = from.getModuleSpecifier().getValue(); module.addModuleRequest(moduleRequest); module.addStarExportEntry(ExportEntry.exportStarFrom(moduleRequest)); + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, from)); + endOfLine(); break; } case LBRACE: { - List exportEntries = exportClause(); - if (type == IDENT && "from".equals(getValue())) { - String moduleRequest = fromClause(); + ExportClauseNode exportClause = exportClause(); + if (type == FROM) { + FromNode from = fromClause(); + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, exportClause, from)); + String moduleRequest = from.getModuleSpecifier().getValue(); module.addModuleRequest(moduleRequest); - for (int i = 0; i < exportEntries.size(); i++) { - module.addIndirectExportEntry(exportEntries.get(i).withFrom(moduleRequest)); - } } else { - for (int i = 0; i < exportEntries.size(); i++) { - module.addLocalExportEntry(exportEntries.get(i)); - } + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, exportClause)); } - expect(SEMICOLON); + endOfLine(); break; } case DEFAULT: @@ -5251,31 +5849,36 @@ private void exportDeclaration() { switch (type) { case FUNCTION: assignmentExpression = functionExpression(false, true); - ident = ((FunctionNode) assignmentExpression).getIdent(); + FunctionNode functionNode = (FunctionNode) assignmentExpression; + ident = functionNode.isAnonymous() ? null : functionNode.getIdent(); declaration = true; break; case CLASS: - assignmentExpression = classDeclaration(true); - ident = ((ClassNode) assignmentExpression).getIdent(); + assignmentExpression = classDeclaration(false, false, true); + ident = getClassDeclarationName(assignmentExpression); declaration = true; break; default: - assignmentExpression = assignmentExpression(false); + if (isAsync() && lookaheadIsAsyncFunction()) { + assignmentExpression = asyncFunctionExpression(false, true); + ident = ((FunctionNode) assignmentExpression).getIdent(); + declaration = true; + break; + } + assignmentExpression = assignmentExpression(true, false, false); ident = null; declaration = false; break; } + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, assignmentExpression, true)); if (ident != null) { -// lc.appendStatementToCurrentNode(new VarNode(lineNumber, Token.recast(rhsToken, LET), finish, ident, assignmentExpression)); + lc.appendStatementToCurrentNode(new VarNode(lineNumber, Token.recast(rhsToken, LET), finish, ident, assignmentExpression).setFlag(VarNode.IS_EXPORT)); module.addLocalExportEntry(ExportEntry.exportDefault(ident.getName())); - final VarNode varNode = new VarNode(line, TokenType.FUNCTION.ordinal(), finish, ident, assignmentExpression, 0); -// functionDeclarations.add(varNode); - lc.prependStatementToCurrentNode(varNode); } else { - ident = createIdentNode(Token.recast(rhsToken, IDENT), finish, Module.DEFAULT_EXPORT_BINDING_NAME); - lc.appendStatementToCurrentNode(new VarNode(lineNumber, Token.recast(rhsToken, LET), finish, ident, assignmentExpression)); + ident = new IdentNode(Token.recast(rhsToken, IDENT), finish, Module.DEFAULT_EXPORT_BINDING_NAME); + lc.appendStatementToCurrentNode(new VarNode(lineNumber, Token.recast(rhsToken, LET), finish, ident, assignmentExpression).setFlag(VarNode.IS_EXPORT)); if (!declaration) { - expect(SEMICOLON); + endOfLine(); } module.addLocalExportEntry(ExportEntry.exportDefault()); } @@ -5288,25 +5891,45 @@ private void exportDeclaration() { variableStatement(type); for (Statement statement : statements.subList(previousEnd, statements.size())) { if (statement instanceof VarNode) { + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, (VarNode) statement)); module.addLocalExportEntry(ExportEntry.exportSpecifier(((VarNode) statement).getName().getName())); } } break; case CLASS: { - ClassNode classDeclaration = classDeclaration(false); - module.addLocalExportEntry(ExportEntry.exportSpecifier(classDeclaration.getIdent().getName())); + Expression classDeclaration = classDeclaration(false, false, false); + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, classDeclaration, false)); + IdentNode classIdent = getClassDeclarationName(classDeclaration); + module.addLocalExportEntry(ExportEntry.exportSpecifier(classIdent.getName())); break; } case FUNCTION: { FunctionNode functionDeclaration = (FunctionNode) functionExpression(true, true); + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, functionDeclaration, false)); module.addLocalExportEntry(ExportEntry.exportSpecifier(functionDeclaration.getIdent().getName())); break; } default: + if (isAsync() && lookaheadIsAsyncFunction()) { + FunctionNode functionDeclaration = (FunctionNode) asyncFunctionExpression(true, true); + module.addExport(new ExportNode(exportToken, Token.descPosition(exportToken), finish, functionDeclaration, false)); + module.addLocalExportEntry(ExportEntry.exportSpecifier(functionDeclaration.getIdent().getName())); + break; + } throw error(AbstractParser.message("invalid.export"), token); } } + private static IdentNode getClassDeclarationName(Expression classDeclaration) { + if (classDeclaration instanceof ClassNode) { + return ((ClassNode)classDeclaration).getIdent(); + } else { + Expression expression = ((ExpressionStatement)((BlockExpression)classDeclaration).getBlock().getLastStatement()).getExpression(); + assert expression instanceof IdentNode || (expression instanceof ClassNode && ((ClassNode) expression).getIdent() == null); + return expression instanceof IdentNode ? (IdentNode)expression : null; + } + } + /** * ExportClause : * { } @@ -5321,18 +5944,35 @@ private void exportDeclaration() { * * @return a list of ExportSpecifiers */ - private List exportClause() { + private ExportClauseNode exportClause() { + final long startToken = token; assert type == LBRACE; next(); - List exports = new ArrayList<>(); + ArrayList exports = new ArrayList<>(); + long reservedWordToken = 0L; while (type != RBRACE) { - IdentNode localName = getIdentifierName(); - if (type == IDENT && "as".equals(getValue())) { + long nameToken = token; + IdentNode localName; + if (isIdentifier()) { + localName = identifier(false, false); + } else if (isReservedWord()) { + // Reserved words are allowed iff the ExportClause is followed by a FromClause. + // Remember the first reserved word and throw an error if this is not the case. + if (reservedWordToken == 0L) { + reservedWordToken = token; + } + localName = getIdentifierName(); + } else { + throw error(expectMessage(IDENT)); + } + if (type == AS) { next(); IdentNode exportName = getIdentifierName(); - exports.add(ExportEntry.exportSpecifier(exportName.getName(), localName.getName())); + exports.add(new ExportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, exportName)); + //exports.add(ExportEntry.exportSpecifier(exportName.getName(), localName.getName())); } else { - exports.add(ExportEntry.exportSpecifier(localName.getName())); + exports.add(new ExportSpecifierNode(nameToken, Token.descPosition(nameToken), finish, localName, null)); + //exports.add(ExportEntry.exportSpecifier(localName.getName())); } if (type == COMMARIGHT) { next(); @@ -5341,7 +5981,16 @@ private List exportClause() { } } expect(RBRACE); - return exports; + + if (reservedWordToken != 0L && type != FROM) { + throw error(expectMessage(IDENT, reservedWordToken), reservedWordToken); + } + + return new ExportClauseNode(startToken, Token.descPosition(startToken), finish, optimizeList(exports)); + } + + private boolean isReservedWord() { + return type.getKind() == TokenKind.KEYWORD || type.getKind() == TokenKind.FUTURE || type.getKind() == TokenKind.FUTURESTRICT; } @Override @@ -5370,7 +6019,9 @@ private static void markEval(final ParserContext lc) { // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip // parsing a nested function. functionBody() contains code to compensate for the lack of invoking // this method when the parser skips a nested function. - body.setFlag(Block.NEEDS_SCOPE); + if (body != null) { // may be null while parsing the parameter list + body.setFlag(Block.NEEDS_SCOPE); + } fn.setFlag(FunctionNode.HAS_SCOPE_BLOCK); } } @@ -5395,17 +6046,6 @@ private static void markSuperCall(final ParserContext lc) { } } - private ParserContextFunctionNode getCurrentNonArrowFunction() { - final Iterator iter = lc.getFunctions(); - while (iter.hasNext()) { - final ParserContextFunctionNode fn = iter.next(); - if (fn.getKind() != FunctionNode.Kind.ARROW) { - return fn; - } - } - return null; - } - private static void markThis(final ParserContext lc) { final Iterator iter = lc.getFunctions(); while (iter.hasNext()) { @@ -5431,6 +6071,110 @@ private static void markNewTarget(final ParserContext lc) { } private boolean inGeneratorFunction() { - return lc.getCurrentFunction().getKind() == FunctionNode.Kind.GENERATOR; + if (!ES6_GENERATOR_FUNCTION) { + return false; + } + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + return currentFunction != null && currentFunction.getKind() == FunctionNode.Kind.GENERATOR; + } + + private boolean inAsyncFunction() { + if (!ES8_ASYNC_FUNCTION) { + return false; + } + ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); + return currentFunction != null && currentFunction.isAsync(); + } + + private boolean isAwait() { + return ES8_ASYNC_FUNCTION && isES8() && type == AWAIT; + } + + private boolean isAsync() { + return ES8_ASYNC_FUNCTION && isES8() && type == ASYNC; + } + + private boolean lookaheadIsAsyncArrowParameterListStart() { + assert isAsync(); + // find [no LineTerminator here] (IDENT || LPAREN) + int i = 1; + for (;;) { + TokenType t = T(k + i++); + if (t == LPAREN) { + break; + } else if (t == IDENT) { + break; + } else if (t.isContextualKeyword()) { + break; + } else if (t == COMMENT) { + continue; + } else { + return false; + } + } + return true; + } + + private boolean lookaheadIsAsyncFunction() { + assert isAsync(); + // find [no LineTerminator here] FUNCTION + for (int i = 1;; i++) { + long currentToken = getToken(k + i); + TokenType t = Token.descType(currentToken); + switch (t) { + case COMMENT: + continue; + case FUNCTION: + return true; + default: + return false; + } + } + } + + private boolean lookaheadIsAsyncMethod() { + assert isAsync(); + // find [no LineTerminator here] PropertyName + // find [no LineTerminator here] * + for (int i = 1;; i++) { + long currentToken = getToken(k + i); + TokenType t = Token.descType(currentToken); + if (t == COMMENT) { + continue; + } else { + return isPropertyName(currentToken) || t == MUL; + } + } + } + + /** + * Parse and return an expression. + * Errors will be thrown and the error manager will contain information if parsing should fail. + * + * @return expression node resulting from successful parse + */ + public Expression parseExpression() { + try { + prepareLexer(0, source.getLength()); + scanFirstToken(); + + return expression(); + } catch (final Exception e) { + handleParseException(e); + + return null; + } + } + + private static List convert(NamedImportsNode namedImportsNode) { + List importEntries = new ArrayList<>(namedImportsNode.getImportSpecifiers().size()); + for (ImportSpecifierNode s : namedImportsNode.getImportSpecifiers()) { + if (s.getIdentifier() != null) { + importEntries.add(ImportEntry.importSpecifier(s.getIdentifier().getName(), s.getBindingIdentifier().getName())); + } else { + importEntries.add(ImportEntry.importSpecifier(s.getBindingIdentifier().getName())); + } + } + return importEntries; } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContext.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContext.java index 26013930f7..4347fa7012 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContext.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContext.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -199,7 +215,7 @@ public ParserContextLoopNode getContinueTo(final String labelName) { public ParserContextBlockNode getFunctionBody(final ParserContextFunctionNode functionNode) { for (int i = sp - 1; i >= 0; i--) { if (stack[i] == functionNode) { - return (functionNode.getKind() == FunctionNode.Kind.MODULE) ? (ParserContextBlockNode) stack[i + 2] : (ParserContextBlockNode) stack[i + 1]; + return (ParserContextBlockNode) stack[i + 1]; } } throw new AssertionError(functionNode.getName() + " not on context stack"); @@ -298,9 +314,15 @@ public Iterator getFunctions() { return new NodeIterator<>(ParserContextFunctionNode.class); } - public ParserContextModuleNode getCurrentModule() { - final Iterator iter = new NodeIterator<>(ParserContextModuleNode.class, getCurrentFunction()); - return iter.hasNext() ? iter.next() : null; + public ParserContextFunctionNode getCurrentNonArrowFunction() { + final Iterator iter = getFunctions(); + while (iter.hasNext()) { + final ParserContextFunctionNode fn = iter.next(); + if (fn.getKind() != FunctionNode.Kind.ARROW) { + return fn; + } + } + return null; } private class NodeIterator implements Iterator { diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBaseNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBaseNode.java index 89e2d8f962..a4f95d44f2 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBaseNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBaseNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBlockNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBlockNode.java index 084ecf225b..51d8e592e8 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBlockNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBlockNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBreakableNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBreakableNode.java index 7c6d2875d6..8f7b9f1d93 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBreakableNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextBreakableNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java index 8babaf0684..19046f1afe 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextFunctionNode.java @@ -1,35 +1,58 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import com.oracle.js.parser.ir.Block; +import com.oracle.js.parser.ir.Expression; +import com.oracle.js.parser.ir.ExpressionStatement; import com.oracle.js.parser.ir.FunctionNode; import com.oracle.js.parser.ir.IdentNode; import com.oracle.js.parser.ir.Module; +import com.oracle.js.parser.ir.ParameterNode; +import com.oracle.js.parser.ir.VarNode; /** * ParserContextNode that represents a function that is currently being parsed @@ -48,13 +71,13 @@ class ParserContextFunctionNode extends ParserContextBaseNode { /** Line number for function declaration */ private final int line; - /** - * Function node kind, see FunctionNode.Kind - */ + /** Function node kind, see FunctionNode.Kind */ private final FunctionNode.Kind kind; - /** List of parameter identifiers for function */ + /** List of parameter identifiers (for simple and rest parameters). */ private List parameters; + /** Optional parameter initialization block (replaces parameter list). */ + private ParserContextBlockNode parameterBlock; /** Token for function start */ private final long token; @@ -65,9 +88,12 @@ class ParserContextFunctionNode extends ParserContextBaseNode { /** Opaque node for parser end state, see {@link Parser} */ private Object endParserState; + private int length; + private int parameterCount; private HashSet parameterBoundNames; private IdentNode duplicateParameterBinding; private boolean simpleParameterList = true; + private boolean containsDefaultParameter; private Module module; @@ -81,7 +107,7 @@ class ParserContextFunctionNode extends ParserContextBaseNode { * @param parameters The parameters of the function */ ParserContextFunctionNode(final long token, final IdentNode ident, final String name, final Namespace namespace, final int line, final FunctionNode.Kind kind, - final List parameters) { + final List parameters, final int length) { this.ident = ident; this.namespace = namespace; this.line = line; @@ -89,6 +115,9 @@ class ParserContextFunctionNode extends ParserContextBaseNode { this.name = name; this.parameters = parameters; this.token = token; + this.length = length; + this.parameterCount = parameters == null ? 0 : parameters.size(); + assert calculateLength(parameters) == length; } /** @@ -166,6 +195,9 @@ public FunctionNode.Kind getKind() { * @return The parameters of the function */ public List getParameters() { + if (parameters == null) { + return Collections.emptyList(); + } return parameters; } @@ -229,7 +261,53 @@ public boolean isSubclassConstructor() { return getFlag(FunctionNode.IS_SUBCLASS_CONSTRUCTOR) != 0; } - boolean addParameterBinding(IdentNode bindingIdentifier) { + public int getLength() { + return length; + } + + public int getParameterCount() { + return parameterCount; + } + + /** + * Add simple or rest parameter. + */ + public void addParameter(IdentNode param) { + addParameterBinding(param); + if (parameterBlock != null) { + addParameterInit(param, getParameterCount()); + } else { + if (parameters == null) { + parameters = new ArrayList<>(); + } + parameters.add(param); + } + recordParameter(false, param.isRestParameter(), false); + } + + /** + * Update number of parameters, length, and simple parameter list flag. + */ + private void recordParameter(boolean isDefault, boolean isRest, boolean isPattern) { + if (!isDefault && !isRest) { + if (!containsDefaultParameter) { + length++; + } + } else { + containsDefaultParameter = true; + } + if ((isDefault || isRest || isPattern) && simpleParameterList) { + recordNonSimpleParameterList(); + } + parameterCount++; + } + + private void recordNonSimpleParameterList() { + this.simpleParameterList = false; + setFlag(FunctionNode.HAS_NON_SIMPLE_PARAMETER_LIST); + } + + private boolean addParameterBinding(IdentNode bindingIdentifier) { if (Parser.isArguments(bindingIdentifier)) { setFlag(FunctionNode.DEFINES_ARGUMENTS); } @@ -253,10 +331,6 @@ public boolean isSimpleParameterList() { return simpleParameterList; } - public void setSimpleParameterList(boolean simpleParameterList) { - this.simpleParameterList = simpleParameterList; - } - public Module getModule() { return module; } @@ -264,4 +338,75 @@ public Module getModule() { public void setModule(Module module) { this.module = module; } + + public boolean isAsync() { + return getFlag(FunctionNode.IS_ASYNC) != 0; + } + + public ParserContextBlockNode getParameterBlock() { + return parameterBlock; + } + + public void addDefaultParameter(VarNode varNode) { + ensureParameterBlock(); + parameterBlock.appendStatement(varNode); + addParameterBinding(varNode.getName()); + recordParameter(true, false, false); + } + + public void addParameterBindingDeclaration(VarNode varNode) { + ensureParameterBlock(); + parameterBlock.appendStatement(varNode); + addParameterBinding(varNode.getName()); + } + + public void addParameterInitialization(int lineNumber, Expression assignment, boolean isDefault) { + ensureParameterBlock(); + parameterBlock.appendStatement(new ExpressionStatement(lineNumber, assignment.getToken(), assignment.getFinish(), assignment)); + recordParameter(isDefault, false, true); + } + + private void ensureParameterBlock() { + if (parameterBlock == null) { + initParameterBlock(); + } + } + + private void initParameterBlock() { + parameterBlock = new ParserContextBlockNode(token); + parameterBlock.setFlag(Block.IS_PARAMETER_BLOCK); + + if (parameters != null) { + for (int i = 0; i < parameters.size(); i++) { + IdentNode paramIdent = parameters.get(i); + addParameterInit(paramIdent, i); + } + } + parameters = Collections.emptyList(); + } + + private void addParameterInit(IdentNode param, int index) { + long paramToken = param.getToken(); + int paramFinish = param.getFinish(); + ParameterNode paramValue; + if (param.isRestParameter()) { + paramValue = new ParameterNode(paramToken, paramFinish, index, true); + } else { + paramValue = new ParameterNode(paramToken, paramFinish, index); + } + parameterBlock.appendStatement(new VarNode(line, Token.recast(paramToken, TokenType.LET), paramFinish, param, paramValue, VarNode.IS_LET)); + } + + private static int calculateLength(final List parameters) { + int length = 0; + if (parameters != null) { + for (IdentNode param : parameters) { + if (param.isRestParameter()) { + break; + } + length++; + } + } + return length; + } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLabelNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLabelNode.java index f15da88a9a..95c4f33e03 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLabelNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLabelNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLoopNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLoopNode.java index 183df9bdd4..523a9a6b59 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLoopNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextLoopNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextModuleNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextModuleNode.java index 9f8828b9a4..5a22fc81ab 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextModuleNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextModuleNode.java @@ -1,32 +1,53 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import com.oracle.js.parser.ir.ExportNode; +import com.oracle.js.parser.ir.ExportSpecifierNode; +import com.oracle.js.parser.ir.ImportNode; import com.oracle.js.parser.ir.Module; import com.oracle.js.parser.ir.Module.ExportEntry; import com.oracle.js.parser.ir.Module.ImportEntry; @@ -44,6 +65,10 @@ class ParserContextModuleNode extends ParserContextBaseNode { private List localExportEntries = new ArrayList<>(); private List indirectExportEntries = new ArrayList<>(); private List starExportEntries = new ArrayList<>(); + private Map importedLocalNames = new HashMap<>(); + + private List imports = new ArrayList<>(); + private List exports = new ArrayList<>(); /** * Constructor. @@ -63,12 +88,21 @@ public String getModuleName() { return name; } + public void addImport(ImportNode importNode) { + imports.add(importNode); + } + + public void addExport(ExportNode exportNode) { + exports.add(exportNode); + } + public void addModuleRequest(String moduleRequest) { requestedModules.add(moduleRequest); } public void addImportEntry(ImportEntry importEntry) { importEntries.add(importEntry); + importedLocalNames.put(importEntry.getLocalName(), importEntry); } public void addLocalExportEntry(ExportEntry exportEntry) { @@ -84,6 +118,34 @@ public void addStarExportEntry(ExportEntry exportEntry) { } public Module createModule() { - return new Module(requestedModules, importEntries, localExportEntries, indirectExportEntries, starExportEntries); + for (ExportNode export : exports) { + if (export.getExportClause() != null) { + for (ExportSpecifierNode s : export.getExportClause().getExportSpecifiers()) { + String localName = s.getIdentifier().getName(); + ExportEntry ee; + if (s.getExportIdentifier() != null) { + ee = ExportEntry.exportSpecifier(s.getExportIdentifier().getName(), localName); + } else { + ee = ExportEntry.exportSpecifier(localName); + } + if (export.getFrom() == null) { + ImportEntry ie = importedLocalNames.get(localName); + if (ie == null) { + addLocalExportEntry(ee); + } else if (ie.getImportName().equals(Module.STAR_NAME)) { + // This is a re-export of an imported module namespace object. + addLocalExportEntry(ee); + } else { + // This is a re-export of a single name. + addIndirectExportEntry(ExportEntry.exportIndirect(ee.getExportName(), ie.getModuleRequest(), ie.getImportName())); + } + } else { + addIndirectExportEntry(ee.withFrom(export.getFrom().getModuleSpecifier().getValue())); + } + } + } + } + + return new Module(requestedModules, importEntries, localExportEntries, indirectExportEntries, starExportEntries, imports, exports); } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextNode.java index 660c0be10e..ccd97707f9 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextSwitchNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextSwitchNode.java index 294f1e636e..9b2e1739de 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextSwitchNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserContextSwitchNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserException.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserException.java index 712c4372bb..3e662d22b7 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserException.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ParserException.java @@ -1,31 +1,46 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; - /** * ECMAScript parser exceptions. */ @@ -164,4 +179,8 @@ public int getPosition() { public JSErrorType getErrorType() { return errorType; } + + public boolean isIncompleteSource() { + return Token.descType(token) == TokenType.EOF; + } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/RecompilableScriptFunctionData.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/RecompilableScriptFunctionData.java index 60815f168a..4dd917c328 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/RecompilableScriptFunctionData.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/RecompilableScriptFunctionData.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Scanner.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Scanner.java index 80b84e88e2..38021bc771 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Scanner.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Scanner.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -32,7 +48,7 @@ */ public class Scanner { /** Characters to scan. */ - protected final String content; + protected final char[] content; /** Position in content. */ protected int position; @@ -60,7 +76,7 @@ public class Scanner { * @param start position index in content where to start * @param length length of input */ - protected Scanner(final String content, final int line, final int start, final int length) { + protected Scanner(final char[] content, final int line, final int start, final int length) { this.content = content; this.position = start; this.limit = start + length; @@ -150,7 +166,7 @@ protected final boolean atEOF() { */ protected final char charAt(final int i) { // Get a character from the content, '\0' if beyond the end of file. - return i < limit ? content.charAt(i) : '\0'; + return i < limit ? content[i] : '\0'; } /** diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ScriptEnvironment.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ScriptEnvironment.java index 9aef8ceef5..0480a4146f 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ScriptEnvironment.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ScriptEnvironment.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -52,6 +68,9 @@ public final class ScriptEnvironment { /** Enable ECMAScript 6 features. */ final boolean es6; + /** Enable ECMAScript 8 (2017) features. */ + final boolean es8; + /** * Behavior when encountering a function declaration in a lexical context where only statements * are acceptable (function declarations are source elements, but not statements). @@ -91,10 +110,8 @@ public enum FunctionStatementBehavior { /** is this environment in strict mode? */ final boolean strict; - final boolean functionDeclarationHoisting; - - private ScriptEnvironment(boolean strict, boolean es6, boolean earlyLvalueError, boolean emptyStatements, boolean syntaxExtensions, boolean scripting, boolean shebang, boolean constAsVar, - boolean functionDeclarationHoisting, FunctionStatementBehavior functionStatementBehavior, PrintWriter dumpOnError) { + private ScriptEnvironment(boolean strict, boolean es6, boolean es8, boolean earlyLvalueError, boolean emptyStatements, boolean syntaxExtensions, boolean scripting, boolean shebang, + boolean constAsVar, FunctionStatementBehavior functionStatementBehavior, PrintWriter dumpOnError) { this.namespace = new Namespace(); this.err = dumpOnError; @@ -108,7 +125,7 @@ private ScriptEnvironment(boolean strict, boolean es6, boolean earlyLvalueError, this.scripting = scripting; this.shebang = shebang; this.es6 = es6; - this.functionDeclarationHoisting = functionDeclarationHoisting; + this.es8 = es8; } /** @@ -143,11 +160,11 @@ public static final class Builder { private boolean earlyLvalueError = true; private boolean emptyStatements; private boolean es6 = true; + private boolean es8 = true; private boolean syntaxExtensions = true; private boolean scripting; private boolean shebang; private boolean strict; - private boolean functionDeclarationHoisting; private FunctionStatementBehavior functionStatementBehavior = FunctionStatementBehavior.ERROR; private PrintWriter dumpOnError; @@ -174,6 +191,11 @@ public Builder es6(boolean es6) { return this; } + public Builder es8(boolean es8) { + this.es8 = es8; + return this; + } + public Builder syntaxExtensions(boolean syntaxExtensions) { this.syntaxExtensions = syntaxExtensions; return this; @@ -204,14 +226,9 @@ public Builder dumpOnError(PrintWriter dumpOnError) { return this; } - public Builder functionDeclarationHoisting(boolean functionDeclarationHoisting) { - this.functionDeclarationHoisting = functionDeclarationHoisting; - return this; - } - public ScriptEnvironment build() { - return new ScriptEnvironment(strict, es6, earlyLvalueError, emptyStatements, syntaxExtensions, scripting, shebang, constAsVar, - functionDeclarationHoisting, functionStatementBehavior, dumpOnError); + return new ScriptEnvironment(strict, es6, es8, earlyLvalueError, emptyStatements, syntaxExtensions, scripting, shebang, constAsVar, + functionStatementBehavior, dumpOnError); } } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Source.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Source.java index 86a0571c4a..914e883a39 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Source.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Source.java @@ -1,45 +1,50 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.IOError; import java.io.IOException; -import java.io.InputStream; import java.io.Reader; -import java.net.MalformedURLException; -import java.net.URISyntaxException; import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; @@ -60,8 +65,8 @@ public final class Source { private final String name; /** - * Base directory the File or base part of the URL. Used to implement __DIR__. - * Used to load scripts relative to the 'directory' or 'base' URL of current script. + * Base path or URL of this source. Used to implement __DIR__, which can be + * used to load scripts relative to the location of the current script. * This will be null when it can't be computed. */ private final String base; @@ -85,23 +90,6 @@ private Source(final String name, final String base, final Data data) { this.data = data; } - private static Source sourceFor(final String name, final String base, final URLData data) throws IOException { - try { - final Source newSource = new Source(name, base, data); - - // All sources in cache must be fully loaded - data.load(); - - return newSource; - } catch (final RuntimeException e) { - final Throwable cause = e.getCause(); - if (cause instanceof IOException) { - throw (IOException) cause; - } - throw e; - } - } - // Wrapper to manage lazy loading private interface Data { @@ -111,17 +99,17 @@ private interface Data { long lastModified(); - String data(); + CharSequence data(); boolean isEvalCode(); } private static final class RawData implements Data { - private final String source; + private final CharSequence source; private final boolean evalCode; private int hash; - private RawData(final String source, final boolean evalCode) { + private RawData(final CharSequence source, final boolean evalCode) { this.source = Objects.requireNonNull(source); this.evalCode = evalCode; } @@ -153,7 +141,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return data(); + return data().toString(); } @Override @@ -171,151 +159,18 @@ public long lastModified() { return 0; } - public String data() { - return source; - } - @Override - public boolean isEvalCode() { - return evalCode; - } - } - - private static class URLData implements Data { - private final URL url; - protected final Charset cs; - private int hash; - protected String source; - protected int length; - protected long lastModified; - - private URLData(final URL url, final Charset cs) { - this.url = Objects.requireNonNull(url); - this.cs = cs; - } - - @Override - public int hashCode() { - int h = hash; - if (h == 0) { - h = hash = url.hashCode(); - } - return h; - } - - @Override - public boolean equals(final Object other) { - if (this == other) { - return true; - } - if (!(other instanceof URLData)) { - return false; - } - - final URLData otherData = (URLData) other; - - if (url.equals(otherData.url)) { - // Make sure both have meta data loaded - try { - if (isDeferred()) { - // Data in cache is always loaded, and we only compare to cached data. - assert !otherData.isDeferred(); - loadMeta(); - } else if (otherData.isDeferred()) { - otherData.loadMeta(); - } - } catch (final IOException e) { - throw new RuntimeException(e); - } - - // Compare meta data - return this.length == otherData.length && this.lastModified == otherData.lastModified; - } - return false; - } - - @Override - public String toString() { - return data(); - } - - @Override - public URL url() { - return url; - } - - @Override - public int length() { - return length; - } - - @Override - public long lastModified() { - return lastModified; - } - - public String data() { - assert !isDeferred(); + public CharSequence data() { return source; } @Override public boolean isEvalCode() { - return false; - } - - boolean isDeferred() { - return source == null; - } - - protected void load() throws IOException { - if (source == null) { - final URLConnection c = url.openConnection(); - try (InputStream in = c.getInputStream()) { - source = cs == null ? readFully(in) : readFully(in, cs); - length = source.length(); - lastModified = c.getLastModified(); - } - } - } - - protected void loadMeta() throws IOException { - if (length == 0 && lastModified == 0) { - final URLConnection c = url.openConnection(); - length = c.getContentLength(); - lastModified = c.getLastModified(); - } - } - } - - private static class FileData extends URLData { - private final File file; - - private FileData(final File file, final Charset cs) { - super(getURLFromFile(file), cs); - this.file = file; - - } - - @Override - protected void loadMeta() { - if (length == 0 && lastModified == 0) { - length = (int) file.length(); - lastModified = file.lastModified(); - } - } - - @Override - protected void load() throws IOException { - if (source == null) { - source = cs == null ? readFully(file) : readFully(file, cs); - length = source.length(); - lastModified = file.lastModified(); - } + return evalCode; } } - private String data() { + private CharSequence data() { return data.data(); } @@ -323,11 +178,11 @@ private String data() { * Returns a Source instance * * @param name source name - * @param content contents as string + * @param content contents as {@link CharSequence} * @param isEval does this represent code from 'eval' call? * @return source instance */ - public static Source sourceFor(final String name, final String content, final boolean isEval) { + public static Source sourceFor(final String name, final CharSequence content, final boolean isEval) { return new Source(name, baseName(name), new RawData(content, isEval)); } @@ -342,103 +197,6 @@ public static Source sourceFor(final String name, final String content) { return sourceFor(name, content, false); } - /** - * Returns a Source instance - * - * @param name source name - * @param url url from which source can be loaded - * - * @return source instance - * - * @throws IOException if source cannot be loaded - */ - public static Source sourceFor(final String name, final URL url) throws IOException { - return sourceFor(name, url, null); - } - - /** - * Returns a Source instance - * - * @param name source name - * @param url url from which source can be loaded - * @param cs Charset used to convert bytes to chars - * - * @return source instance - * - * @throws IOException if source cannot be loaded - */ - public static Source sourceFor(final String name, final URL url, final Charset cs) throws IOException { - return sourceFor(name, baseURL(url), new URLData(url, cs)); - } - - /** - * Returns a Source instance - * - * @param name source name - * @param file file from which source can be loaded - * - * @return source instance - * - * @throws IOException if source cannot be loaded - */ - public static Source sourceFor(final String name, final File file) throws IOException { - return sourceFor(name, file, null); - } - - /** - * Returns a Source instance - * - * @param name source name - * @param path path from which source can be loaded - * - * @return source instance - * - * @throws IOException if source cannot be loaded - */ - public static Source sourceFor(final String name, final Path path) throws IOException { - File file = null; - try { - file = path.toFile(); - } catch (final UnsupportedOperationException uoe) { - } - - if (file != null) { - return sourceFor(name, file); - } else { - return sourceFor(name, Files.newBufferedReader(path)); - } - } - - /** - * Returns a Source instance - * - * @param name source name - * @param file file from which source can be loaded - * @param cs Charset used to convert bytes to chars - * - * @return source instance - * - * @throws IOException if source cannot be loaded - */ - public static Source sourceFor(final String name, final File file, final Charset cs) throws IOException { - final File absFile = file.getAbsoluteFile(); - return sourceFor(name, dirName(absFile, null), new FileData(file, cs)); - } - - /** - * Returns a Source instance. - * - * @param name source name - * @param reader reader from which source can be loaded - * - * @return source instance - * - * @throws IOException if source cannot be loaded - */ - public static Source sourceFor(final String name, final Reader reader) throws IOException { - return new Source(name, baseName(name), new RawData(reader)); - } - @Override public boolean equals(final Object obj) { if (this == obj) { @@ -499,7 +257,7 @@ public String getBase() { * @return Source content portion. */ public String getString(final int start, final int len) { - return data().substring(start, start + len); + return data().subSequence(start, start + len).toString(); } /** @@ -554,7 +312,7 @@ public boolean isEvalCode() { * @return Index of first character of line. */ private int findBOLN(final int position) { - final String d = data(); + final CharSequence d = data(); for (int i = position - 1; i > 0; i--) { final char ch = d.charAt(i); @@ -572,7 +330,7 @@ private int findBOLN(final int position) { * @return Index of last character of line. */ private int findEOLN(final int position) { - final String d = data(); + final CharSequence d = data(); final int length = d.length(); for (int i = position; i < length; i++) { final char ch = d.charAt(i); @@ -595,7 +353,7 @@ private int findEOLN(final int position) { * @return Line number. */ public int getLine(final int position) { - final String d = data(); + final CharSequence d = data(); // Line count starts at 1. int line = 1; @@ -625,19 +383,19 @@ public int getColumn(final int position) { * @param position Position of character in source content. * @return Line text. */ - public String getSourceLine(final int position) { + public CharSequence getSourceLine(final int position) { // Find end of previous line. final int first = findBOLN(position); // Find end of this line. final int last = findEOLN(position); - return data().substring(first, last + 1); + return data().subSequence(first, last + 1); } /** - * Get the content of this source as a {@link String}. + * Get the content of this source as a {@link CharSequence}. */ - public String getContent() { + public CharSequence getContent() { return data(); } @@ -672,60 +430,6 @@ public static String readFully(final Reader reader) throws IOException { return sb.toString(); } - /** - * Read all of the source until end of file. - * - * @param file source file - * @return source as content - * @throws IOException if source could not be read - */ - public static String readFully(final File file) throws IOException { - if (!file.isFile()) { - throw new IOException(file + " is not a file"); - } - return byteArrayToString(Files.readAllBytes(file.toPath())); - } - - /** - * Read all of the source until end of file. - * - * @param file source file - * @param cs Charset used to convert bytes to chars - * @return source as content - * @throws IOException if source could not be read - */ - public static String readFully(final File file, final Charset cs) throws IOException { - if (!file.isFile()) { - throw new IOException(file + " is not a file"); - } - - final byte[] buf = Files.readAllBytes(file.toPath()); - return (cs != null) ? new String(buf, cs) : byteArrayToString(buf); - } - - /** - * Read all of the source until end of stream from the given URL. - * - * @param url URL to read content from - * @return source as content - * @throws IOException if source could not be read - */ - public static String readFully(final URL url) throws IOException { - return readFully(url.openStream()); - } - - /** - * Read all of the source until end of file. - * - * @param url URL to read content from - * @param cs Charset used to convert bytes to chars - * @return source as content - * @throws IOException if source could not be read - */ - public static String readFully(final URL url, final Charset cs) throws IOException { - return readFully(url.openStream(), cs); - } - /** * Get a Base64-encoded SHA1 digest for this source. * @@ -738,7 +442,7 @@ public String getDigest() { private byte[] getDigestBytes() { byte[] ldigest = digest; if (ldigest == null) { - final String content = data(); + final CharSequence content = data(); final byte[] bytes = new byte[content.length() * 2]; for (int i = 0; i < content.length(); i++) { @@ -767,41 +471,6 @@ private byte[] getDigestBytes() { return ldigest; } - /** - * Get the base url. This is currently used for testing only - * @param url a URL - * @return base URL for url - */ - public static String baseURL(final URL url) { - if (url.getProtocol().equals("file")) { - try { - final Path path = Paths.get(url.toURI()); - final Path parent = path.getParent(); - return (parent != null) ? (parent + File.separator) : null; - } catch (final SecurityException | URISyntaxException | IOError e) { - return null; - } - } - - // FIXME: is there a better way to find 'base' URL of a given URL? - String path = url.getPath(); - if (path.isEmpty()) { - return null; - } - path = path.substring(0, path.lastIndexOf('/') + 1); - final int port = url.getPort(); - try { - return new URL(url.getProtocol(), url.getHost(), port, path).toString(); - } catch (final MalformedURLException e) { - return null; - } - } - - private static String dirName(final File file, final String defaultBaseName) { - final String res = file.getParent(); - return (res != null) ? (res + File.separator) : defaultBaseName; - } - // fake directory like name private static String baseName(final String name) { int idx = name.lastIndexOf('/'); @@ -811,65 +480,8 @@ private static String baseName(final String name) { return (idx != -1) ? name.substring(0, idx + 1) : null; } - public static String readFully(final InputStream is, final Charset cs) throws IOException { - return (cs != null) ? new String(readBytes(is), cs) : readFully(is); - } - - public static String readFully(final InputStream is) throws IOException { - return byteArrayToString(readBytes(is)); - } - - private static String byteArrayToString(final byte[] bytes) { - Charset cs = StandardCharsets.UTF_8; - int start = 0; - // BOM detection. - if (bytes.length > 1 && bytes[0] == (byte) 0xFE && bytes[1] == (byte) 0xFF) { - start = 2; - cs = StandardCharsets.UTF_16BE; - } else if (bytes.length > 1 && bytes[0] == (byte) 0xFF && bytes[1] == (byte) 0xFE) { - if (bytes.length > 3 && bytes[2] == 0 && bytes[3] == 0) { - start = 4; - cs = Charset.forName("UTF-32LE"); - } else { - start = 2; - cs = StandardCharsets.UTF_16LE; - } - } else if (bytes.length > 2 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) { - start = 3; - cs = StandardCharsets.UTF_8; - } else if (bytes.length > 3 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == (byte) 0xFE && bytes[3] == (byte) 0xFF) { - start = 4; - cs = Charset.forName("UTF-32BE"); - } - - return new String(bytes, start, bytes.length - start, cs); - } - - static byte[] readBytes(final InputStream is) throws IOException { - final byte[] arr = new byte[BUF_SIZE]; - try { - try (ByteArrayOutputStream buf = new ByteArrayOutputStream()) { - int numBytes; - while ((numBytes = is.read(arr, 0, arr.length)) > 0) { - buf.write(arr, 0, numBytes); - } - return buf.toByteArray(); - } - } finally { - is.close(); - } - } - @Override public String toString() { return getName(); } - - private static URL getURLFromFile(final File file) { - try { - return file.toURI().toURL(); - } catch (final SecurityException | MalformedURLException ignored) { - return null; - } - } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Token.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Token.java index d92c47b696..f5fca76a24 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Token.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/Token.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -55,6 +71,7 @@ private Token() { * @return Token descriptor. */ public static long toDesc(final TokenType type, final int position, final int length) { + assert length >= 0; assert position <= LENGTH_MASK && length <= LENGTH_MASK; return (long)position << POSITION_SHIFT | (long)length << LENGTH_SHIFT | @@ -169,14 +186,4 @@ public static String toString(final Source source, final long token) { return Token.toString(source, token, false); } - /** - * String conversion of token - version without source given - * - * @param token the token - * - * @return token as string - */ - public static String toString(final long token) { - return Token.toString(null, token, false); - } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenKind.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenKind.java index 8bf5b4a783..b31b8d254a 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenKind.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenKind.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -46,5 +62,7 @@ public enum TokenKind { /** Token reserved for future usage. */ FUTURE, /** Token reserved for future in strict mode. */ - FUTURESTRICT + FUTURESTRICT, + /** Identifier or contextual keyword. */ + CONTEXTUAL, } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenLookup.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenLookup.java index 336800c495..5808433c57 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenLookup.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenLookup.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -106,15 +122,15 @@ private TokenLookup() { /** * Lookup keyword. * - * @param content parse content char array + * @param content parse content char sequence * @param position index of position to start looking - * @param length max length to scan + * @param length max length to scan * * @return token type for keyword */ - public static TokenType lookupKeyword(final String content, final int position, final int length) { + public static TokenType lookupKeyword(final char[] content, final int position, final int length) { // First character of keyword. - final char first = content.charAt(position); + final char first = content[position]; // Must be lower case character. if ('a' <= first && first <= 'z') { @@ -133,7 +149,7 @@ public static TokenType lookupKeyword(final String content, final int position, final String name = tokenType.getName(); int i; for (i = 0; i < length; i++) { - if (content.charAt(position + i) != name.charAt(i)) { + if (content[position + i] != name.charAt(i)) { break; } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenStream.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenStream.java index 37a004162f..8c61b7c9bd 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenStream.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenStream.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; @@ -111,22 +127,6 @@ public boolean isFull() { return count == buffer.length; } - /** - * Get the number of tokens in the buffer. - * @return Number of tokens. - */ - public int count() { - return count; - } - - /** - * Get the index of the first token in the stream. - * @return Index of first buffered token in the stream. - */ - public int first() { - return base; - } - /** * Get the index of the last token in the stream. * @return Index of last buffered token in the stream. @@ -135,20 +135,6 @@ public int last() { return base + count - 1; } - /** - * Remove the last token in the stream. - */ - public void removeLast() { - if (count != 0) { - count--; - in--; - - if (in < 0) { - in = buffer.length - 1; - } - } - } - /** * Put a token descriptor to the stream. * @param token Token descriptor to add. @@ -210,25 +196,4 @@ public void grow() { void reset() { in = out = count = base = 0; } - - /** - * Insert a new token at the specified index - * - * @param currentTokenIndex - * @param newToken - */ - public void insert(int currentTokenIndex, long newToken) - { - // Allocate new buffer. - final long[] newBuffer = new long[buffer.length *2]; - - System.arraycopy(buffer, 0, newBuffer, 0, currentTokenIndex); - newBuffer[currentTokenIndex] = newToken; - System.arraycopy(buffer, currentTokenIndex, newBuffer, currentTokenIndex+1, in-currentTokenIndex); - count++; - - // Update buffer. - buffer = newBuffer; - - } } diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java index e115a75915..0dd193702f 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/TokenType.java @@ -1,32 +1,49 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser; import static com.oracle.js.parser.TokenKind.BINARY; import static com.oracle.js.parser.TokenKind.BRACKET; +import static com.oracle.js.parser.TokenKind.CONTEXTUAL; import static com.oracle.js.parser.TokenKind.FUTURE; import static com.oracle.js.parser.TokenKind.FUTURESTRICT; import static com.oracle.js.parser.TokenKind.IR; @@ -108,96 +125,105 @@ public enum TokenType { // ECMA 7.6.1.1 Keywords, 7.6.1.2 Future Reserved Words. // All other Java keywords are commented out. -// ABSTRACT (FUTURE, "abstract"), -// BOOLEAN (FUTURE, "boolean"), - BREAK (KEYWORD, "break"), -// BYTE (FUTURE, "byte"), - CASE (KEYWORD, "case"), - CATCH (KEYWORD, "catch"), -// CHAR (FUTURE, "char"), - CLASS (FUTURE, "class"), - CONST (KEYWORD, "const"), - CONTINUE (KEYWORD, "continue"), - DEBUGGER (KEYWORD, "debugger"), - DEFAULT (KEYWORD, "default"), - DELETE (UNARY, "delete", 15, false), - DO (KEYWORD, "do"), -// DOUBLE (FUTURE, "double"), -// EACH (KEYWORD, "each"), // Contextual. - ELSE (KEYWORD, "else"), - ENUM (FUTURE, "enum"), - EXPORT (FUTURE, "export"), - EXTENDS (FUTURE, "extends"), - FALSE (LITERAL, "false"), -// FINAL (FUTURE, "final"), - FINALLY (KEYWORD, "finally"), -// FLOAT (FUTURE, "float"), - FOR (KEYWORD, "for"), - FUNCTION (KEYWORD, "function"), -// GET (KEYWORD, "get"), // Contextual. -// GOTO (FUTURE, "goto"), - IF (KEYWORD, "if"), - IMPLEMENTS (FUTURESTRICT, "implements"), - IMPORT (FUTURE, "import"), - IN (BINARY, "in", 10, true), - INSTANCEOF (BINARY, "instanceof", 10, true), -// INT (FUTURE, "int"), - INTERFACE (FUTURESTRICT, "interface"), - LET (FUTURESTRICT, "let"), -// LONG (FUTURE, "long"), -// NATIVE (FUTURE, "native"), - NEW (UNARY, "new", 18, false), - NULL (LITERAL, "null"), - PACKAGE (FUTURESTRICT, "package"), - PRIVATE (FUTURESTRICT, "private"), - PROTECTED (FUTURESTRICT, "protected"), - PUBLIC (FUTURESTRICT, "public"), - RETURN (KEYWORD, "return"), -// SET (KEYWORD, "set"), // Contextual. -// SHORT (FUTURE, "short"), - STATIC (FUTURESTRICT, "static"), - SUPER (FUTURE, "super"), - SWITCH (KEYWORD, "switch"), -// SYNCHRONIZED (FUTURE, "synchronized"), - THIS (KEYWORD, "this"), - THROW (KEYWORD, "throw"), -// THROWS (FUTURE, "throws"), -// TRANSIENT (FUTURE, "transient"), - TRUE (LITERAL, "true"), - TRY (KEYWORD, "try"), - TYPEOF (UNARY, "typeof", 15, false), - VAR (KEYWORD, "var"), - VOID (UNARY, "void", 15, false), -// VOLATILE (FUTURE, "volatile"), - WHILE (KEYWORD, "while"), - WITH (KEYWORD, "with"), - YIELD (FUTURESTRICT, "yield"), - - DECIMAL (LITERAL, null), - HEXADECIMAL (LITERAL, null), - OCTAL_LEGACY (LITERAL, null), - OCTAL (LITERAL, null), - BINARY_NUMBER (LITERAL, null), - FLOATING (LITERAL, null), - STRING (LITERAL, null), - ESCSTRING (LITERAL, null), - EXECSTRING (LITERAL, null), - IDENT (LITERAL, null), - REGEX (LITERAL, null), - XML (LITERAL, null), - OBJECT (LITERAL, null), - ARRAY (LITERAL, null), - TEMPLATE (LITERAL, null), - TEMPLATE_HEAD (LITERAL, null), - TEMPLATE_MIDDLE(LITERAL, null), - TEMPLATE_TAIL (LITERAL, null), - - COMMALEFT (IR, null), - DECPOSTFIX (IR, null), - INCPOSTFIX (IR, null), - SPREAD_ARGUMENT(IR, null), - SPREAD_ARRAY (IR, null), - YIELD_STAR (IR, null); +// ABSTRACT (FUTURE, "abstract"), + AS (CONTEXTUAL, "as"), + ASYNC (CONTEXTUAL, "async"), + AWAIT (CONTEXTUAL, "await"), +// BOOLEAN (FUTURE, "boolean"), + BREAK (KEYWORD, "break"), +// BYTE (FUTURE, "byte"), + CASE (KEYWORD, "case"), + CATCH (KEYWORD, "catch"), +// CHAR (FUTURE, "char"), + CLASS (FUTURE, "class"), + CONST (KEYWORD, "const"), + CONTINUE (KEYWORD, "continue"), + DEBUGGER (KEYWORD, "debugger"), + DEFAULT (KEYWORD, "default"), + DELETE (UNARY, "delete", 15, false), + DO (KEYWORD, "do"), +// DOUBLE (FUTURE, "double"), +// EACH (KEYWORD, "each"), // Contextual. + ELSE (KEYWORD, "else"), + ENUM (FUTURE, "enum"), + EXPORT (FUTURE, "export"), + EXTENDS (FUTURE, "extends"), + FALSE (LITERAL, "false"), +// FINAL (FUTURE, "final"), + FINALLY (KEYWORD, "finally"), +// FLOAT (FUTURE, "float"), + FOR (KEYWORD, "for"), + FROM (CONTEXTUAL, "from"), + FUNCTION (KEYWORD, "function"), + GET (CONTEXTUAL, "get"), +// GOTO (FUTURE, "goto"), + IF (KEYWORD, "if"), + IMPLEMENTS (FUTURESTRICT, "implements"), + IMPORT (FUTURE, "import"), + IN (BINARY, "in", 10, true), + INSTANCEOF (BINARY, "instanceof", 10, true), +// INT (FUTURE, "int"), + INTERFACE (FUTURESTRICT, "interface"), + LET (FUTURESTRICT, "let"), +// LONG (FUTURE, "long"), +// NATIVE (FUTURE, "native"), + NEW (UNARY, "new", 18, false), + NULL (LITERAL, "null"), + OF (CONTEXTUAL, "of"), + PACKAGE (FUTURESTRICT, "package"), + PRIVATE (FUTURESTRICT, "private"), + PROTECTED (FUTURESTRICT, "protected"), + PUBLIC (FUTURESTRICT, "public"), + RETURN (KEYWORD, "return"), + SET (CONTEXTUAL, "set"), +// SHORT (FUTURE, "short"), + STATIC (FUTURESTRICT, "static"), + SUPER (FUTURE, "super"), + SWITCH (KEYWORD, "switch"), +// SYNCHRONIZED (FUTURE, "synchronized"), + THIS (KEYWORD, "this"), + THROW (KEYWORD, "throw"), +// THROWS (FUTURE, "throws"), +// TRANSIENT (FUTURE, "transient"), + TRUE (LITERAL, "true"), + TRY (KEYWORD, "try"), + TYPEOF (UNARY, "typeof", 15, false), + VAR (KEYWORD, "var"), + VOID (UNARY, "void", 15, false), +// VOLATILE (FUTURE, "volatile"), + WHILE (KEYWORD, "while"), + WITH (KEYWORD, "with"), + YIELD (FUTURESTRICT, "yield"), + + DECIMAL (LITERAL, null), + NON_OCTAL_DECIMAL (LITERAL, null), // NonOctalDecimalIntegerLiteral + HEXADECIMAL (LITERAL, null), + OCTAL_LEGACY (LITERAL, null), + OCTAL (LITERAL, null), + BINARY_NUMBER (LITERAL, null), + BIGINT (LITERAL, null), // BigInt literal + FLOATING (LITERAL, null), + STRING (LITERAL, null), + ESCSTRING (LITERAL, null), + EXECSTRING (LITERAL, null), + IDENT (LITERAL, null), + REGEX (LITERAL, null), + XML (LITERAL, null), + OBJECT (LITERAL, null), + ARRAY (LITERAL, null), + TEMPLATE (LITERAL, null), + TEMPLATE_HEAD (LITERAL, null), + TEMPLATE_MIDDLE(LITERAL, null), + TEMPLATE_TAIL (LITERAL, null), + + COMMALEFT (IR, null), + DECPOSTFIX (IR, null), + INCPOSTFIX (IR, null), + SPREAD_ARGUMENT(IR, null), + SPREAD_ARRAY (IR, null), + SPREAD_OBJECT (IR, null), + YIELD_STAR (IR, null), + ASSIGN_INIT (IR, null); /** Next token kind in token lookup table. */ private TokenType next; @@ -250,12 +276,12 @@ public boolean needsParens(final TokenType other, final boolean isLeft) { /** * Determines if the type is a valid operator. * - * @param noIn {@code true} if IN operator should be ignored. + * @param in {@code false} if IN operator should be ignored. * * @return {@code true} if valid operator. */ - public boolean isOperator(final boolean noIn) { - return kind == BINARY && (!noIn || this != IN) && precedence != 0; + public boolean isOperator(final boolean in) { + return kind == BINARY && (in || this != IN) && precedence != 0; } public int getLength() { @@ -310,6 +336,7 @@ public String toString() { public boolean isAssignment() { switch (this) { case ASSIGN: + case ASSIGN_INIT: case ASSIGN_ADD: case ASSIGN_BIT_AND: case ASSIGN_BIT_OR: @@ -328,6 +355,14 @@ public boolean isAssignment() { } } + public boolean isContextualKeyword() { + return kind == TokenKind.CONTEXTUAL; + } + + public boolean isFutureStrict() { + return kind == TokenKind.FUTURESTRICT; + } + static { // Avoid cloning of enumeration. values = TokenType.values(); diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/AccessNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/AccessNode.java index 397ca4aab7..ede31a5717 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/AccessNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/AccessNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser.ir; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Assignment.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Assignment.java index b1c1eed948..9fd6a0f448 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Assignment.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Assignment.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser.ir; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BaseNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BaseNode.java index 567f0dc17c..c4e57de9ec 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BaseNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BaseNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser.ir; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BinaryNode.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BinaryNode.java index 68b85a77ed..472e2228c9 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BinaryNode.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/BinaryNode.java @@ -1,26 +1,42 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser.ir; @@ -216,6 +232,9 @@ public void toString(final StringBuilder sb, final boolean printType) { case DECPREFIX: sb.append("++"); break; + case ASSIGN_INIT: + sb.append(":="); + break; default: sb.append(tokenType.getName()); break; diff --git a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Block.java b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Block.java index a27fd884ad..6dc82a65d5 100644 --- a/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Block.java +++ b/bundles/com.oracle.js.parser/src/com/oracle/js/parser/ir/Block.java @@ -1,36 +1,51 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. + * The Universal Permissive License (UPL), Version 1.0 * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * (a) the Software, and * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package com.oracle.js.parser.ir; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; + +import org.graalvm.collections.EconomicMap; import com.oracle.js.parser.ir.visitor.NodeVisitor; import com.oracle.js.parser.ir.visitor.TranslatorNodeVisitor; @@ -44,17 +59,11 @@ public class Block extends Node implements BreakableNode, Terminal, Flags protected final List statements; /** Symbol table - keys must be returned in the order they were put in. */ - protected final Map symbols; + protected final EconomicMap symbols; private int blockScopedOrRedeclaredSymbols; private int declaredNames; - /** Entry label. */ - private final Label entryLabel; - - /** Break label. */ - private final Label breakLabel; - /** Does the block/function need a new scope? Is this synthetic? */ protected final int flags; @@ -93,6 +102,16 @@ public class Block extends Node implements BreakableNode, Terminal, Flags */ public static final int IS_SWITCH_BLOCK = 1 << 7; + /** + * Marks the variable declaration block for a for-of loop. + */ + public static final int IS_FOR_OF_BLOCK = 1 << 8; + + /** + * Is this an expression block (class or do expression) that should return its completion value. + */ + public static final int IS_EXPRESSION_BLOCK = 1 << 9; + /** * Constructor * @@ -105,26 +124,13 @@ public Block(final long token, final int finish, final int flags, final Statemen super(token, finish); assert start <= finish; - this.statements = Arrays.asList(statements); - this.symbols = new LinkedHashMap<>(); - this.entryLabel = new Label("block_entry"); - this.breakLabel = new Label("block_break"); + this.statements = statements.length == 0 ? Collections.emptyList() : Arrays.asList(statements); + this.symbols = EconomicMap.create(); final int len = statements.length; final int terminalFlags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0; this.flags = terminalFlags | flags; } - /** - * Constructs a new block - * - * @param token The first token of the block - * @param finish The index of the last character - * @param statements All statements in the block - */ - public Block(final long token, final int finish, final List statements) { - this(token, finish, IS_SYNTHETIC, statements); - } - /** * Constructor * @@ -137,13 +143,11 @@ public Block(final long token, final int finish, final int flags, final List statements, final int flags, final Map symbols) { + private Block(final Block block, final int finish, final List statements, final int flags, final EconomicMap symbols) { super(block, finish); this.statements = statements; this.flags = flags; - this.symbols = new LinkedHashMap<>(symbols); - this.entryLabel = new Label(block.entryLabel); - this.breakLabel = new Label(block.breakLabel); + this.symbols = EconomicMap.create(symbols); this.declaredNames = block.declaredNames; this.blockScopedOrRedeclaredSymbols = block.blockScopedOrRedeclaredSymbols; @@ -179,11 +183,11 @@ public R accept(LexicalContext lc, TranslatorNodeVisitor getSymbols() { - return Collections.unmodifiableList(new ArrayList<>(symbols.values())); + public Iterable getSymbols() { + return symbols.getValues(); } /** @@ -196,6 +200,21 @@ public Symbol getExistingSymbol(final String name) { return symbols.get(name); } + /** + * Test if a symbol with this name is defined in the current block. + * @param name the name of the symbol + */ + public boolean hasSymbol(final String name) { + return symbols.containsKey(name); + } + + /** + * Get the number of symbols defined in this block. + */ + public int getSymbolCount() { + return symbols.size(); + } + /** * Test if this block represents a catch block in a try statement. * This is used by the Splitter as catch blocks are not be subject to splitting. @@ -203,7 +222,7 @@ public Symbol getExistingSymbol(final String name) { * @return true if this block represents a catch block in a try statement. */ public boolean isCatchBlock() { - return statements.size() == 1 && statements.get(0) instanceof CatchNode; + return getLastStatement() instanceof CatchNode; } @Override @@ -229,19 +248,6 @@ public boolean isTerminal() { return getFlag(IS_TERMINAL); } - /** - * Get the entry label for this block - * @return the entry label - */ - public Label getEntryLabel() { - return entryLabel; - } - - @Override - public Label getBreakLabel() { - return breakLabel; - } - /** * Get the list of statements in this block * @@ -313,10 +319,10 @@ public Block setStatements(final LexicalContext lc, final List statem */ public void putSymbol(final LexicalContext lc, final Symbol symbol) { symbols.put(symbol.getName(), symbol); - if (symbol.isBlockScoped() || symbol.isVarRedeclaredHere()) { + if ((symbol.isBlockScoped() || symbol.isVarRedeclaredHere()) && !symbol.isImportBinding()) { blockScopedOrRedeclaredSymbols++; } - if (symbol.isBlockScoped() || symbol.isVarDeclaredHere()) { + if ((symbol.isBlockScoped() || (symbol.isVar() && symbol.isVarDeclaredHere())) && !symbol.isImportBinding()) { declaredNames++; } } @@ -362,23 +368,14 @@ public boolean isBreakableWithoutLabel() { return false; } - @Override - public List