JS: grouped all scope/name operations in JsParser

This commit is contained in:
Alexey Tsvetkov
2015-01-30 21:19:46 +03:00
parent bb634c6365
commit 47064ce03c
2 changed files with 114 additions and 68 deletions
@@ -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)
}