Skip to content

Commit

Permalink
Add preliminary parser and code printing support for the import.meta …
Browse files Browse the repository at this point in the history
…Stage 3 proposal.

See:
https://github.com/tc39/proposal-import-meta
https://tc39.es/proposal-import-meta/

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=258185594
  • Loading branch information
tjgq committed Jul 17, 2019
1 parent 0db7072 commit d6a732d
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 6 deletions.
4 changes: 4 additions & 0 deletions src/com/google/javascript/jscomp/AstValidator.java
Expand Up @@ -277,6 +277,10 @@ public void validateExpression(Node n) {
validateFeature(Feature.NEW_TARGET, n);
validateChildless(n);
return;
case IMPORT_META:
validateFeature(Feature.IMPORT_META, n);
validateChildless(n);
return;
case SUPER:
validateFeature(Feature.SUPER, n);
validateChildless(n);
Expand Down
4 changes: 4 additions & 0 deletions src/com/google/javascript/jscomp/CodeGenerator.java
Expand Up @@ -487,6 +487,10 @@ protected void add(Node n, Context context) {
add(")");
break;

case IMPORT_META:
add("import.meta");
break;

// CLASS -> NAME,EXPR|EMPTY,BLOCK
case CLASS:
{
Expand Down
1 change: 1 addition & 0 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -1256,6 +1256,7 @@ public static int precedence(Token type) {
case GETELEM:
case GETPROP:
case NEW_TARGET:
case IMPORT_META:
// Data values
case ARRAYLIT:
case ARRAY_PATTERN:
Expand Down
1 change: 1 addition & 0 deletions src/com/google/javascript/jscomp/TypeCheck.java
Expand Up @@ -657,6 +657,7 @@ public void visit(NodeTraversal t, Node n, Node parent) {

case SUPER:
case NEW_TARGET:
case IMPORT_META:
case AWAIT:
ensureTyped(n);
break;
Expand Down
9 changes: 9 additions & 0 deletions src/com/google/javascript/jscomp/parsing/IRFactory.java
Expand Up @@ -93,6 +93,7 @@
import com.google.javascript.jscomp.parsing.parser.trees.IdentifierExpressionTree;
import com.google.javascript.jscomp.parsing.parser.trees.IfStatementTree;
import com.google.javascript.jscomp.parsing.parser.trees.ImportDeclarationTree;
import com.google.javascript.jscomp.parsing.parser.trees.ImportMetaExpressionTree;
import com.google.javascript.jscomp.parsing.parser.trees.ImportSpecifierTree;
import com.google.javascript.jscomp.parsing.parser.trees.IndexSignatureTree;
import com.google.javascript.jscomp.parsing.parser.trees.InterfaceDeclarationTree;
Expand Down Expand Up @@ -2642,6 +2643,12 @@ Node processDynamicImport(DynamicImportTree dynamicImportNode) {
return newNode(Token.DYNAMIC_IMPORT, argument);
}

Node processImportMeta(ImportMetaExpressionTree tree) {
maybeWarnForFeature(tree, Feature.MODULES);
maybeWarnForFeature(tree, Feature.IMPORT_META);
return newNode(Token.IMPORT_META);
}

Node processTypeName(TypeNameTree tree) {
Node typeNode;
if (tree.segments.size() == 1) {
Expand Down Expand Up @@ -3153,6 +3160,8 @@ public Node process(ParseTree node) {
return processImportSpec(node.asImportSpecifier());
case DYNAMIC_IMPORT_EXPRESSION:
return processDynamicImport(node.asDynamicImportExpression());
case IMPORT_META_EXPRESSION:
return processImportMeta(node.asImportMetaExpression());

case ARRAY_PATTERN:
return processArrayPattern(node.asArrayPattern());
Expand Down
Expand Up @@ -180,8 +180,9 @@ public enum Feature {
// https://github.com/tc39/proposal-optional-catch-binding
OPTIONAL_CATCH_BINDING("Optional catch binding", LangVersion.ES2019),

// Stage 3 proposal likely to be part of ES2020
// Stage 3 proposals likely to be part of ES2020
DYNAMIC_IMPORT("Dynamic module import", LangVersion.ES_UNSUPPORTED),
IMPORT_META("import.meta", LangVersion.ES_UNSUPPORTED),

// ES6 typed features that are not at all implemented in browsers
ACCESSIBILITY_MODIFIER("accessibility modifier", LangVersion.TYPESCRIPT),
Expand Down
32 changes: 27 additions & 5 deletions src/com/google/javascript/jscomp/parsing/parser/Parser.java
Expand Up @@ -70,6 +70,7 @@
import com.google.javascript.jscomp.parsing.parser.trees.IdentifierExpressionTree;
import com.google.javascript.jscomp.parsing.parser.trees.IfStatementTree;
import com.google.javascript.jscomp.parsing.parser.trees.ImportDeclarationTree;
import com.google.javascript.jscomp.parsing.parser.trees.ImportMetaExpressionTree;
import com.google.javascript.jscomp.parsing.parser.trees.ImportSpecifierTree;
import com.google.javascript.jscomp.parsing.parser.trees.IndexSignatureTree;
import com.google.javascript.jscomp.parsing.parser.trees.InterfaceDeclarationTree;
Expand Down Expand Up @@ -392,9 +393,12 @@ private ParseTree parseAmbientNamespaceElement() {
return parseAmbientDeclarationHelper();
}

// https://people.mozilla.org/~jorendorff/es6-draft.html#sec-imports
private boolean peekImportDeclaration() {
return peek(TokenType.IMPORT) && !peek(1, TokenType.OPEN_PAREN);
return peek(TokenType.IMPORT)
&& (peekIdOrKeyword(1)
|| peek(1, TokenType.STRING)
|| peek(1, TokenType.OPEN_CURLY)
|| peek(1, TokenType.STAR));
}

private ParseTree parseImportDeclaration() {
Expand Down Expand Up @@ -2933,7 +2937,7 @@ private boolean peekExpression() {
case YIELD:
return true;
case IMPORT:
return peekImportCall();
return peekImportCall() || peekImportDot();
default:
return false;
}
Expand Down Expand Up @@ -3515,6 +3519,10 @@ private boolean peekImportCall() {
return peek(TokenType.IMPORT) && peek(1, TokenType.OPEN_PAREN);
}

private boolean peekImportDot() {
return peek(TokenType.IMPORT) && peek(1, TokenType.PERIOD);
}

// 11.2 Left hand side expression
//
// Also inlines the call expression productions
Expand Down Expand Up @@ -3571,7 +3579,9 @@ private boolean peekCallSuffix() {
private ParseTree parseMemberExpressionNoNew() {
SourcePosition start = getTreeStartLocation();
ParseTree operand;
if (peekAsyncFunctionStart()) {
if (peekImportDot()) {
operand = parseImportDotMeta();
} else if (peekAsyncFunctionStart()) {
operand = parseAsyncFunctionExpression();
} else if (peekFunction()) {
operand = parseFunctionExpression();
Expand Down Expand Up @@ -3635,6 +3645,14 @@ private ParseTree parseNewDotSomething() {
return new NewTargetExpressionTree(getTreeLocation(start));
}

private ParseTree parseImportDotMeta() {
SourcePosition start = getTreeStartLocation();
eat(TokenType.IMPORT);
eat(TokenType.PERIOD);
eatPredefinedString("meta");
return new ImportMetaExpressionTree(getTreeLocation(start));
}

private ArgumentListTree parseArguments() {
// ArgumentList :
// AssignmentOrSpreadExpression
Expand Down Expand Up @@ -4016,7 +4034,11 @@ private boolean peekId(int index) {
}

private boolean peekIdOrKeyword() {
TokenType type = peekType();
return peekIdOrKeyword(0);
}

private boolean peekIdOrKeyword(int index) {
TokenType type = peekType(index);
return TokenType.IDENTIFIER == type || Keywords.isKeyword(type);
}

Expand Down
@@ -0,0 +1,26 @@
/*
* Copyright 2019 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.javascript.jscomp.parsing.parser.trees;

import com.google.javascript.jscomp.parsing.parser.util.SourceRange;

/** Represents 'new.target'. */
public class ImportMetaExpressionTree extends ParseTree {
public ImportMetaExpressionTree(SourceRange location) {
super(ParseTreeType.IMPORT_META_EXPRESSION, location);
}
}
Expand Up @@ -105,6 +105,10 @@ public DynamicImportTree asDynamicImportExpression() {
return (DynamicImportTree) this;
}

public ImportMetaExpressionTree asImportMetaExpression() {
return (ImportMetaExpressionTree) this;
}

public LabelledStatementTree asLabelledStatement() { return (LabelledStatementTree) this; }
public LiteralExpressionTree asLiteralExpression() { return (LiteralExpressionTree) this; }
public MemberExpressionTree asMemberExpression() { return (MemberExpressionTree) this; }
Expand Down
Expand Up @@ -120,4 +120,5 @@ public enum ParseTreeType {
NEW_TARGET_EXPRESSION,
AWAIT_EXPRESSION,
DYNAMIC_IMPORT_EXPRESSION,
IMPORT_META_EXPRESSION,
}
5 changes: 5 additions & 0 deletions src/com/google/javascript/rhino/IR.java
Expand Up @@ -676,6 +676,10 @@ public static Node typeof(Node expr) {
return unaryOp(Token.TYPEOF, expr);
}

public static Node importMeta() {
return new Node(Token.IMPORT_META);
}

// helper methods

private static Node binaryOp(Token token, Node expr1, Node expr2) {
Expand Down Expand Up @@ -795,6 +799,7 @@ public static boolean mayBeExpression(Node n) {
case GETELEM:
case GT:
case HOOK:
case IMPORT_META:
case IN:
case INC:
case INSTANCEOF:
Expand Down
1 change: 1 addition & 0 deletions src/com/google/javascript/rhino/Token.java
Expand Up @@ -193,6 +193,7 @@ public enum Token {

DEFAULT_VALUE, // Formal parameter or destructuring element with a default value
NEW_TARGET, // new.target
IMPORT_META, // import.meta

// Used by type declaration ASTs
STRING_TYPE,
Expand Down
7 changes: 7 additions & 0 deletions test/com/google/javascript/jscomp/AstValidatorTest.java
Expand Up @@ -237,6 +237,13 @@ public void testNewTargetIsValidExpression() {
expectValid(n, Check.EXPRESSION);
}

@Test
public void testImportMetaIsValidExpression() {
setAcceptedLanguage(LanguageMode.UNSUPPORTED);
Node n = new Node(Token.IMPORT_META);
expectValid(n, Check.EXPRESSION);
}

@Test
public void testCastOnLeftSideOfAssign() {
JSDocInfoBuilder jsdoc = new JSDocInfoBuilder(false);
Expand Down
8 changes: 8 additions & 0 deletions test/com/google/javascript/jscomp/CodePrinterTest.java
Expand Up @@ -2746,6 +2746,14 @@ public void testNewTarget() {
assertPrint("function f() {\nnew\n.\ntarget;\n}", "function f(){new.target}");
}

@Test
public void testImportMeta() {
useUnsupportedFeatures = true;
assertPrintSame("import.meta");
assertPrintSame("import.meta.url");
assertPrintSame("console.log(import.meta.url)");
}

@Test
public void testGeneratorYield() {
assertPrintSame("function*f(){yield 1}");
Expand Down
50 changes: 50 additions & 0 deletions test/com/google/javascript/jscomp/parsing/ParserTest.java
Expand Up @@ -32,6 +32,7 @@
import com.google.javascript.jscomp.parsing.ParserRunner.ParseResult;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.FeatureSet.Feature;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.JSTypeExpression;
import com.google.javascript.rhino.Node;
Expand Down Expand Up @@ -5179,6 +5180,55 @@ public void testAwaitDynamicImport() {
}
}

@Test
public void testImportMeta() {
mode = LanguageMode.UNSUPPORTED;
expectFeatures(Feature.MODULES, Feature.IMPORT_META);

Node tree = parse("import.meta");
assertNode(tree.getFirstFirstChild()).isEqualTo(IR.exprResult(IR.importMeta()));
}

@Test
public void testImportMeta_es5() {
mode = LanguageMode.ECMASCRIPT5;
expectFeatures(Feature.MODULES, Feature.IMPORT_META);

parseWarning(
"import.meta",
requiresLanguageModeMessage(LanguageMode.ECMASCRIPT6, Feature.MODULES),
unsupportedFeatureMessage(Feature.IMPORT_META));
}

@Test
public void testImportMeta_es6() {
mode = LanguageMode.ECMASCRIPT6;
expectFeatures(Feature.MODULES, Feature.IMPORT_META);

parseWarning("import.meta", unsupportedFeatureMessage(Feature.IMPORT_META));
}

@Test
public void testImportMeta_inExpression() {
mode = LanguageMode.UNSUPPORTED;
expectFeatures(Feature.MODULES, Feature.IMPORT_META);

Node propTree = parse("import.meta.url");
assertNode(propTree.getFirstFirstChild())
.isEqualTo(IR.exprResult(IR.getprop(IR.importMeta(), "url")));

Node callTree = parse("f(import.meta.url)");
assertNode(callTree.getFirstFirstChild())
.isEqualTo(IR.exprResult(IR.call(IR.name("f"), IR.getprop(IR.importMeta(), "url"))));
}

@Test
public void testImportMeta_asDotProperty() {
Node tree = parse("x.import.meta");
assertNode(tree.getFirstChild())
.isEqualTo(IR.exprResult(IR.getprop(IR.name("x"), "import", "meta")));
}

private void assertNodeHasJSDocInfoWithJSType(Node node, JSType jsType) {
JSDocInfo info = node.getJSDocInfo();
assertWithMessage("Node has no JSDocInfo: %s", node).that(info).isNotNull();
Expand Down

0 comments on commit d6a732d

Please sign in to comment.