JS: grouped all scope/name operations in JsParser
This commit is contained in:
@@ -28,7 +28,6 @@ import java.io.IOException;
|
|||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
|
||||||
|
|
||||||
import static com.google.dart.compiler.util.AstUtil.toFunctionScope;
|
import static com.google.dart.compiler.util.AstUtil.toFunctionScope;
|
||||||
|
|
||||||
@@ -37,7 +36,8 @@ import static com.google.dart.compiler.util.AstUtil.toFunctionScope;
|
|||||||
*/
|
*/
|
||||||
public class JsParser {
|
public class JsParser {
|
||||||
|
|
||||||
private JsProgram program;
|
private final JsProgram program;
|
||||||
|
private final ScopeContext scopeContext;
|
||||||
|
|
||||||
public static List<JsStatement> parse(
|
public static List<JsStatement> parse(
|
||||||
SourceInfo rootSourceInfo,
|
SourceInfo rootSourceInfo,
|
||||||
@@ -45,22 +45,21 @@ public class JsParser {
|
|||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
boolean insideFunction
|
boolean insideFunction
|
||||||
) throws IOException, JsParserException {
|
) throws IOException, JsParserException {
|
||||||
return new JsParser(scope.getProgram()).parseImpl(rootSourceInfo, scope, r, errorReporter, insideFunction);
|
return new JsParser(scope).parseImpl(rootSourceInfo, r, errorReporter, insideFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Stack<JsScope> scopeStack = new Stack<JsScope>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* since source maps are not mapped to kotlin source maps
|
* since source maps are not mapped to kotlin source maps
|
||||||
*/
|
*/
|
||||||
private static final String SOURCE_NAME_STUB = "jsCode";
|
private static final String SOURCE_NAME_STUB = "jsCode";
|
||||||
|
|
||||||
private JsParser(JsProgram program) {
|
private JsParser(@NotNull JsScope scope) {
|
||||||
this.program = program;
|
scopeContext = new ScopeContext(scope);
|
||||||
|
program = scope.getProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<JsStatement> parseImpl(
|
List<JsStatement> parseImpl(
|
||||||
final SourceInfo rootSourceInfo, JsScope scope,
|
final SourceInfo rootSourceInfo,
|
||||||
Reader r,
|
Reader r,
|
||||||
ErrorReporter errorReporter,
|
ErrorReporter errorReporter,
|
||||||
boolean insideFunction
|
boolean insideFunction
|
||||||
@@ -73,12 +72,7 @@ public class JsParser {
|
|||||||
TokenStream ts = new TokenStream(r, SOURCE_NAME_STUB, rootSourceInfo.getLine());
|
TokenStream ts = new TokenStream(r, SOURCE_NAME_STUB, rootSourceInfo.getLine());
|
||||||
Parser parser = new Parser(new IRFactory(ts), insideFunction);
|
Parser parser = new Parser(new IRFactory(ts), insideFunction);
|
||||||
Node topNode = (Node) parser.parse(ts);
|
Node topNode = (Node) parser.parse(ts);
|
||||||
|
return mapStatements(topNode);
|
||||||
// Map the Rhino AST to ours.
|
|
||||||
pushScope(scope);
|
|
||||||
List<JsStatement> stmts = mapStatements(topNode);
|
|
||||||
popScope();
|
|
||||||
return stmts;
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
Context.exit();
|
Context.exit();
|
||||||
@@ -90,10 +84,6 @@ public class JsParser {
|
|||||||
return new JsParserException("Parser encountered internal error: " + msg, position);
|
return new JsParserException("Parser encountered internal error: " + msg, position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsScope getScope() {
|
|
||||||
return scopeStack.peek();
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsNode map(Node node) throws JsParserException {
|
private JsNode map(Node node) throws JsParserException {
|
||||||
switch (node.getType()) {
|
switch (node.getType()) {
|
||||||
case TokenStream.SCRIPT: {
|
case TokenStream.SCRIPT: {
|
||||||
@@ -222,7 +212,7 @@ public class JsParser {
|
|||||||
|
|
||||||
case TokenStream.NAME:
|
case TokenStream.NAME:
|
||||||
case TokenStream.BINDNAME:
|
case TokenStream.BINDNAME:
|
||||||
return mapName(node);
|
return scopeContext.globalNameFor(node.getString()).makeRef();
|
||||||
|
|
||||||
case TokenStream.RETURN:
|
case TokenStream.RETURN:
|
||||||
return mapReturn(node);
|
return mapReturn(node);
|
||||||
@@ -291,8 +281,7 @@ public class JsParser {
|
|||||||
//
|
//
|
||||||
if (unknown instanceof JsStringLiteral) {
|
if (unknown instanceof JsStringLiteral) {
|
||||||
JsStringLiteral lit = (JsStringLiteral) unknown;
|
JsStringLiteral lit = (JsStringLiteral) unknown;
|
||||||
String litName = lit.getValue();
|
return scopeContext.referenceFor(lit.getValue());
|
||||||
return new JsNameRef(litName);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw createParserException("Expecting a name reference", nameRefNode);
|
throw createParserException("Expecting a name reference", nameRefNode);
|
||||||
@@ -379,8 +368,7 @@ public class JsParser {
|
|||||||
String identifier = label.getString();
|
String identifier = label.getString();
|
||||||
assert identifier != null: "If label exists identifier should not be null";
|
assert identifier != null: "If label exists identifier should not be null";
|
||||||
|
|
||||||
JsFunctionScope scope = toFunctionScope(getScope());
|
JsName labelName = scopeContext.labelFor(identifier);
|
||||||
JsName labelName = scope.findLabel(identifier);
|
|
||||||
assert labelName != null: "Unknown label name: " + identifier;
|
assert labelName != null: "Unknown label name: " + identifier;
|
||||||
|
|
||||||
return labelName.makeRef();
|
return labelName.makeRef();
|
||||||
@@ -548,7 +536,7 @@ public class JsParser {
|
|||||||
//
|
//
|
||||||
Node fromIterVarName = fromIter.getFirstChild();
|
Node fromIterVarName = fromIter.getFirstChild();
|
||||||
String fromName = fromIterVarName.getString();
|
String fromName = fromIterVarName.getString();
|
||||||
JsName toName = getScope().declareName(fromName);
|
JsName toName = scopeContext.localNameFor(fromName);
|
||||||
toForIn = new JsForIn(toName);
|
toForIn = new JsForIn(toName);
|
||||||
Node fromIterInit = fromIterVarName.getFirstChild();
|
Node fromIterInit = fromIterVarName.getFirstChild();
|
||||||
if (fromIterInit != null) {
|
if (fromIterInit != null) {
|
||||||
@@ -611,29 +599,19 @@ public class JsParser {
|
|||||||
Node fromFnNameNode = fnNode.getFirstChild();
|
Node fromFnNameNode = fnNode.getFirstChild();
|
||||||
Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
|
Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
|
||||||
Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
|
Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
|
||||||
|
JsFunction toFn = scopeContext.enterFunction();
|
||||||
|
|
||||||
// Decide the function's name, if any.
|
// Decide the function's name, if any.
|
||||||
//
|
//
|
||||||
String fromFnName = fromFnNameNode.getString();
|
String fnNameIdent = fromFnNameNode.getString();
|
||||||
JsName toFnName = null;
|
if (fnNameIdent != null && fnNameIdent.length() > 0) {
|
||||||
if (fromFnName != null && fromFnName.length() > 0) {
|
scopeContext.globalNameFor(fnNameIdent);
|
||||||
toFnName = getScope().declareName(fromFnName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create it, and set the params.
|
|
||||||
//
|
|
||||||
JsFunction toFn = new JsFunction(getScope(), "jsCode");
|
|
||||||
|
|
||||||
// Creating a function also creates a new scope, which we push onto
|
|
||||||
// the scope stack.
|
|
||||||
//
|
|
||||||
pushScope(toFn.getScope());
|
|
||||||
|
|
||||||
while (fromParamNode != null) {
|
while (fromParamNode != null) {
|
||||||
String fromParamName = fromParamNode.getString();
|
String fromParamName = fromParamNode.getString();
|
||||||
// should this be unique? I think not since you can have dup args.
|
JsName name = scopeContext.localNameFor(fromParamName);
|
||||||
JsName paramName = toFn.getScope().declareName(fromParamName);
|
toFn.getParameters().add(new JsParameter(name));
|
||||||
toFn.getParameters().add(new JsParameter(paramName));
|
|
||||||
fromParamNode = fromParamNode.getNext();
|
fromParamNode = fromParamNode.getNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,10 +620,7 @@ public class JsParser {
|
|||||||
JsBlock toBody = mapBlock(fromBodyNode);
|
JsBlock toBody = mapBlock(fromBodyNode);
|
||||||
toFn.setBody(toBody);
|
toFn.setBody(toBody);
|
||||||
|
|
||||||
// Pop the new function's scope off of the scope stack.
|
scopeContext.exitFunction();
|
||||||
//
|
|
||||||
popScope();
|
|
||||||
|
|
||||||
return toFn;
|
return toFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,7 +648,7 @@ public class JsParser {
|
|||||||
//
|
//
|
||||||
Object obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP);
|
Object obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP);
|
||||||
assert (obj instanceof String);
|
assert (obj instanceof String);
|
||||||
toNameRef = new JsNameRef((String) obj);
|
toNameRef = scopeContext.referenceFor((String) obj);
|
||||||
}
|
}
|
||||||
toNameRef.setQualifier(toQualifier);
|
toNameRef.setQualifier(toQualifier);
|
||||||
|
|
||||||
@@ -725,22 +700,16 @@ public class JsParser {
|
|||||||
|
|
||||||
private JsLabel mapLabel(Node labelNode) throws JsParserException {
|
private JsLabel mapLabel(Node labelNode) throws JsParserException {
|
||||||
String fromName = labelNode.getFirstChild().getString();
|
String fromName = labelNode.getFirstChild().getString();
|
||||||
JsFunctionScope scope = toFunctionScope(getScope());
|
|
||||||
JsName toName = scope.enterLabel(fromName);
|
JsName toName = scopeContext.enterLabel(fromName);
|
||||||
|
|
||||||
Node fromStmt = labelNode.getFirstChild().getNext();
|
Node fromStmt = labelNode.getFirstChild().getNext();
|
||||||
JsLabel toLabel = new JsLabel(toName);
|
JsLabel toLabel = new JsLabel(toName);
|
||||||
toLabel.setStatement(mapStatement(fromStmt));
|
toLabel.setStatement(mapStatement(fromStmt));
|
||||||
scope.exitLabel();
|
|
||||||
return toLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
scopeContext.exitLabel();
|
||||||
* Creates a reference to a name that may or may not be obfuscatable, based on
|
|
||||||
* whether it matches a known name in the scope.
|
return toLabel;
|
||||||
*/
|
|
||||||
private JsNameRef mapName(Node node) {
|
|
||||||
String ident = node.getString();
|
|
||||||
return new JsNameRef(ident);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsNew mapNew(Node newNode) throws JsParserException {
|
private JsNew mapNew(Node newNode) throws JsParserException {
|
||||||
@@ -1069,8 +1038,7 @@ public class JsParser {
|
|||||||
// Map the catch variable.
|
// Map the catch variable.
|
||||||
//
|
//
|
||||||
Node fromCatchVarName = fromCatchNode.getFirstChild();
|
Node fromCatchVarName = fromCatchNode.getFirstChild();
|
||||||
JsCatch catchBlock = new JsCatch(
|
JsCatch catchBlock = scopeContext.enterCatch(fromCatchVarName.getString());
|
||||||
getScope(), fromCatchVarName.getString());
|
|
||||||
|
|
||||||
// Pre-advance to the next catch block, if any.
|
// Pre-advance to the next catch block, if any.
|
||||||
// We do this here to decide whether or not this is the last one.
|
// We do this here to decide whether or not this is the last one.
|
||||||
@@ -1102,6 +1070,7 @@ public class JsParser {
|
|||||||
// Attach it.
|
// Attach it.
|
||||||
//
|
//
|
||||||
toTry.getCatches().add(catchBlock);
|
toTry.getCatches().add(catchBlock);
|
||||||
|
scopeContext.exitCatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Node fromFinallyNode = fromCatchNodes.getNext();
|
Node fromFinallyNode = fromCatchNodes.getNext();
|
||||||
@@ -1153,7 +1122,7 @@ public class JsParser {
|
|||||||
// literals.
|
// literals.
|
||||||
//
|
//
|
||||||
String fromName = fromVar.getString();
|
String fromName = fromVar.getString();
|
||||||
JsName toName = getScope().declareName(fromName);
|
JsName toName = scopeContext.localNameFor(fromName);
|
||||||
JsVars.JsVar toVar = new JsVars.JsVar(toName);
|
JsVars.JsVar toVar = new JsVars.JsVar(toName);
|
||||||
|
|
||||||
Node fromInit = fromVar.getFirstChild();
|
Node fromInit = fromVar.getFirstChild();
|
||||||
@@ -1180,14 +1149,6 @@ public class JsParser {
|
|||||||
withNode);
|
withNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void popScope() {
|
|
||||||
scopeStack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pushScope(JsScope scope) {
|
|
||||||
scopeStack.push(scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isJsNumber(Node jsNode) {
|
private boolean isJsNumber(Node jsNode) {
|
||||||
int type = jsNode.getType();
|
int type = jsNode.getType();
|
||||||
return type == TokenStream.NUMBER || type == TokenStream.NUMBER;
|
return type == TokenStream.NUMBER || type == TokenStream.NUMBER;
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2015 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* 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.gwt.dev.js
|
||||||
|
|
||||||
|
import com.google.dart.compiler.backend.js.ast.*
|
||||||
|
|
||||||
|
import java.util.Stack
|
||||||
|
|
||||||
|
class ScopeContext(private val rootScope: JsScope) {
|
||||||
|
private val scopes = Stack<JsScope>();
|
||||||
|
|
||||||
|
{
|
||||||
|
scopes.push(rootScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enterFunction(): JsFunction {
|
||||||
|
val fn = JsFunction(currentScope, "<js function>")
|
||||||
|
enterScope(fn.getScope())
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exitFunction() {
|
||||||
|
assert(currentScope is JsFunctionScope)
|
||||||
|
exitScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enterCatch(ident: String): JsCatch {
|
||||||
|
val jsCatch = JsCatch(currentScope, ident)
|
||||||
|
enterScope(jsCatch.getScope())
|
||||||
|
return jsCatch
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exitCatch() {
|
||||||
|
assert(currentScope is JsCatchScope)
|
||||||
|
exitScope()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun enterLabel(ident: String): JsName =
|
||||||
|
(currentScope as JsFunctionScope).enterLabel(ident)
|
||||||
|
|
||||||
|
fun exitLabel() =
|
||||||
|
(currentScope as JsFunctionScope).exitLabel()
|
||||||
|
|
||||||
|
fun labelFor(ident: String): JsName? =
|
||||||
|
(currentScope as JsFunctionScope).findLabel(ident)
|
||||||
|
|
||||||
|
fun globalNameFor(ident: String): JsName =
|
||||||
|
currentScope.findName(ident) ?: rootScope.declareName(ident)
|
||||||
|
|
||||||
|
fun localNameFor(ident: String): JsName =
|
||||||
|
currentScope.findOwnNameOrDeclare(ident)
|
||||||
|
|
||||||
|
fun referenceFor(ident: String): JsNameRef =
|
||||||
|
JsNameRef(ident)
|
||||||
|
|
||||||
|
private fun enterScope(scope: JsScope) = scopes.push(scope)
|
||||||
|
|
||||||
|
private fun exitScope() = scopes.pop()
|
||||||
|
|
||||||
|
private val currentScope: JsScope
|
||||||
|
get() = scopes.peek()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides JsFunctionScope declareName as it's mapped to declareFreshName
|
||||||
|
*/
|
||||||
|
private fun JsScope.findOwnNameOrDeclare(ident: String): JsName =
|
||||||
|
when (this) {
|
||||||
|
is JsFunctionScope -> declareNameUnsafe(ident)
|
||||||
|
else -> declareName(ident)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user