blob: 3aebe3ce9855ecf3727f4268fea58f5963c6870a [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// SExpressionUnstringifier implements the inverse operation to
// [SExpressionStringifier].
library sexpr_unstringifier;
import 'package:compiler/implementation/dart2jslib.dart' as dart2js
show Constant, IntConstant, NullConstant, StringConstant,
DoubleConstant, TrueConstant, FalseConstant, MessageKind;
import 'package:compiler/implementation/dart_types.dart' as dart_types
show DartType;
import 'package:compiler/implementation/elements/elements.dart'
show Entity, Element, Elements, Local, TypeVariableElement, ErroneousElement,
TypeDeclarationElement, ExecutableElement;
import 'package:compiler/implementation/elements/modelx.dart'
show ErroneousElementX, TypeVariableElementX;
import 'package:compiler/implementation/tree/tree.dart'show LiteralDartString;
import 'package:compiler/implementation/universe/universe.dart'
show Selector, SelectorKind;
import 'package:compiler/implementation/cps_ir/cps_ir_nodes.dart';
/// Used whenever a node constructed by [SExpressionUnstringifier] needs a
/// named entity.
class DummyEntity extends Entity {
final String name;
DummyEntity(this.name);
}
/// Used whenever a node constructed by [SExpressionUnstringifier] needs a
/// local.
class DummyLocal extends DummyEntity implements Local {
DummyLocal(String name) : super(name);
ExecutableElement get executableContext => null;
}
/// Used whenever a node constructed by [SExpressionUnstringifier] requires
/// an [Element] or [FunctionElement]. Extends [ErroneousElementX] since there
/// is currently a large amount of overhead when extending the base abstract
/// classes, and erroneous elements conveniently also skip several assertion
/// checks in CPS IR nodes that are irrelevant to us.
class DummyElement extends ErroneousElementX
implements TypeVariableElement {
DummyElement(String name)
: super(dart2js.MessageKind.GENERIC, {}, name, null);
final dart_types.DartType bound = null;
final TypeDeclarationElement typeDeclaration = null;
}
/// Used whenever a node constructed by [SExpressionUnstringifier] requires
/// a named type.
class DummyNamedType extends dart_types.DartType {
final String name;
final kind = null;
final element = null;
DummyNamedType(this.name);
subst(arguments, parameters) => null;
unalias(compiler) => null;
accept(visitor, argument) => null;
String toString() => name;
}
/// Represents a list of tokens, but is basically a partial view into a list
/// with appropriate convenience methods.
class Tokens {
final List<String> _list;
int _index; // Current index into the list.
Tokens(List<String> this._list) : _index = 0;
String get current => _list[_index];
String get next => _list[_index + 1];
String read([String expected]) {
if (expected != null) {
assert(current == expected);
}
return _list[_index++];
}
/// Consumes the preamble to a new node, consisting of an opening parenthesis
/// and a tag.
void consumeStart([String tag]) {
read("(");
if (tag != null) {
read(tag);
}
}
void consumeEnd() {
read(")");
}
bool get hasNext => _index < _list.length;
String toString() => _list.sublist(_index).toString();
}
/// Constructs a minimal in-memory representation of the IR represented
/// by the given string. Many fields are currently simply set to null.
class SExpressionUnstringifier {
// Expressions
static const String BRANCH = "Branch";
static const String CONCATENATE_STRINGS = "ConcatenateStrings";
static const String DECLARE_FUNCTION = "DeclareFunction";
static const String INVOKE_CONSTRUCTOR = "InvokeConstructor";
static const String INVOKE_CONTINUATION = "InvokeContinuation";
static const String INVOKE_CONTINUATION_RECURSIVE = "InvokeContinuation*";
static const String INVOKE_STATIC = "InvokeStatic";
static const String INVOKE_SUPER_METHOD = "InvokeSuperMethod";
static const String INVOKE_METHOD = "InvokeMethod";
static const String LET_PRIM = "LetPrim";
static const String LET_CONT = "LetCont";
static const String LET_CONT_RECURSIVE = "LetCont*";
static const String SET_CLOSURE_VARIABLE = "SetClosureVariable";
static const String TYPE_OPERATOR = "TypeOperator";
// Primitives
static const String CONSTANT = "Constant";
static const String CREATE_FUNCTION = "CreateFunction";
static const String GET_CLOSURE_VARIABLE = "GetClosureVariable";
static const String LITERAL_LIST = "LiteralList";
static const String LITERAL_MAP = "LiteralMap";
static const String REIFY_TYPE_VAR = "ReifyTypeVar";
static const String THIS = "This";
// Other
static const String FUNCTION_DEFINITION = "FunctionDefinition";
static const String IS_TRUE = "IsTrue";
final Map<String, Definition> name2variable =
<String, Definition>{ "return": new Continuation.retrn() };
// Operator names used for canonicalization. In theory, we could simply use
// Elements.isOperatorName() on the parsed tokens; however, comparisons are
// done using identical() for performance reasons, which are reliable only for
// compile-time literal strings.
static Set<String> OPERATORS = new Set<String>.from(
[ '~', '==', '[]', '*', '/', '%', '~/', '+', '<<', 'unary-'
, '>>', '>=', '>', '<=', '<', '&', '^', '|', '[]=', '-'
]);
// The tokens currently being parsed.
Tokens tokens;
FunctionDefinition unstringify(String s) {
tokens = tokenize(s);
FunctionDefinition def = parseFunctionDefinition();
assert(!tokens.hasNext);
return def;
}
/// Returns a new named dummy selector with a roughly appropriate kind.
Selector dummySelector(String name, int argumentCount) {
SelectorKind kind;
if (name == "[]") {
kind = SelectorKind.INDEX;
} else if (Elements.isOperatorName(name)) {
kind = SelectorKind.OPERATOR;
} else {
kind = SelectorKind.CALL;
}
return new Selector(kind, name, null, argumentCount);
}
/// Returns the tokens in s. Note that string literals are not necessarily
/// preserved; for instance, "(literalString)" is transformed to
/// " ( literalString ) ".
Tokens tokenize(String s) =>
new Tokens(
s.replaceAll("(", " ( ")
.replaceAll(")", " ) ")
.replaceAll(new RegExp(r"[ \t\n]+"), " ")
.trim()
.split(" ")
.map(canonicalizeOperators)
.toList());
/// Canonicalizes strings containing operator names.
String canonicalizeOperators(String token) {
String opname = OPERATORS.lookup(token);
if (opname != null) {
return opname;
}
return token;
}
Expression parseExpression() {
assert(tokens.current == "(");
switch (tokens.next) {
case BRANCH:
return parseBranch();
case CONCATENATE_STRINGS:
return parseConcatenateStrings();
case DECLARE_FUNCTION:
return parseDeclareFunction();
case INVOKE_CONSTRUCTOR:
return parseInvokeConstructor();
case INVOKE_CONTINUATION:
return parseInvokeContinuation(false);
case INVOKE_CONTINUATION_RECURSIVE:
return parseInvokeContinuation(true);
case INVOKE_METHOD:
return parseInvokeMethod();
case INVOKE_STATIC:
return parseInvokeStatic();
case INVOKE_SUPER_METHOD:
return parseInvokeSuperMethod();
case LET_PRIM:
return parseLetPrim();
case LET_CONT:
return parseLetCont(false);
case LET_CONT_RECURSIVE:
return parseLetCont(true);
case SET_CLOSURE_VARIABLE:
return parseSetClosureVariable();
case TYPE_OPERATOR:
return parseTypeOperator();
default:
assert(false);
}
return null;
}
/// def1 def2 ... defn cont )
/// Note that cont is *not* included in the returned list and not consumed.
List<Definition> parseDefinitionList() {
List<Definition> defs = <Definition>[];
while (tokens.next != ")") {
Definition def = name2variable[tokens.read()];
assert(def != null);
defs.add(def);
}
return defs;
}
/// (prim1 prim2 ... primn)
List<Primitive> parsePrimitiveList() {
tokens.consumeStart();
List<Primitive> prims = <Primitive>[];
while (tokens.current != ")") {
Primitive prim = name2variable[tokens.read()];
assert(prim != null);
prims.add(prim);
}
tokens.consumeEnd();
return prims;
}
/// (FunctionDefinition name (args cont) body)
FunctionDefinition parseFunctionDefinition() {
tokens.consumeStart(FUNCTION_DEFINITION);
// name
Element element = new DummyElement("");
if (tokens.current != "(") {
// This is a named function.
element = new DummyElement(tokens.read());
}
// (args cont)
List<Parameter> parameters = <Parameter>[];
tokens.consumeStart();
while (tokens.next != ")") {
String paramName = tokens.read();
Parameter param = new Parameter(new DummyElement(paramName));
name2variable[paramName] = param;
parameters.add(param);
}
String contName = tokens.read("return");
Continuation cont = name2variable[contName];
assert(cont != null);
tokens.consumeEnd();
// body
Expression body = parseExpression();
tokens.consumeEnd();
return new FunctionDefinition(element, cont, parameters, body, null, null);
}
/// (IsTrue arg)
Condition parseCondition() {
// Handles IsTrue only for now.
tokens.consumeStart(IS_TRUE);
Definition value = name2variable[tokens.read()];
assert(value != null);
tokens.consumeEnd();
return new IsTrue(value);
}
/// (Branch condition cont cont)
Branch parseBranch() {
tokens.consumeStart(BRANCH);
Condition cond = parseCondition();
Continuation trueCont = name2variable[tokens.read()];
Continuation falseCont = name2variable[tokens.read()];
assert(trueCont != null && falseCont != null);
tokens.consumeEnd();
return new Branch(cond, trueCont, falseCont);
}
/// (ConcatenateStrings args cont)
ConcatenateStrings parseConcatenateStrings() {
tokens.consumeStart(CONCATENATE_STRINGS);
List<Definition> args = parseDefinitionList();
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
tokens.consumeEnd();
return new ConcatenateStrings(cont, args);
}
/// (DeclareFunction name = function in body)
DeclareFunction parseDeclareFunction() {
tokens.consumeStart(DECLARE_FUNCTION);
// name =
Local local = new DummyLocal(tokens.read());
tokens.read("=");
// function in
FunctionDefinition def = parseFunctionDefinition();
tokens.read("in");
// body
Expression body = parseExpression();
tokens.consumeEnd();
return new DeclareFunction(local, def)..plug(body);
}
/// (InvokeConstructor name args cont)
InvokeConstructor parseInvokeConstructor() {
tokens.consumeStart(INVOKE_CONSTRUCTOR);
String constructorName = tokens.read();
List<String> split = constructorName.split(".");
assert(split.length < 3);
dart_types.DartType type = new DummyNamedType(split[0]);
Element element = new DummyElement((split.length == 1) ? "" : split[1]);
List<Definition> args = parseDefinitionList();
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
tokens.consumeEnd();
Selector selector = dummySelector(constructorName, args.length);
return new InvokeConstructor(type, element, selector, cont, args);
}
/// (InvokeContinuation name args)
InvokeContinuation parseInvokeContinuation(bool recursive) {
tokens.consumeStart(recursive
? INVOKE_CONTINUATION_RECURSIVE : INVOKE_CONTINUATION);
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
List<Definition> args = <Definition>[];
while (tokens.current != ")") {
Definition def = name2variable[tokens.read()];
assert(def != null);
args.add(def);
}
tokens.consumeEnd();
return new InvokeContinuation(cont, args, recursive: recursive);
}
/// (InvokeMethod receiver method args cont)
InvokeMethod parseInvokeMethod() {
tokens.consumeStart(INVOKE_METHOD);
Definition receiver = name2variable[tokens.read()];
assert(receiver != null);
String methodName = tokens.read();
List<Definition> args = parseDefinitionList();
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
tokens.consumeEnd();
Selector selector = dummySelector(methodName, args.length);
return new InvokeMethod(receiver, selector, cont, args);
}
/// (InvokeStatic method args cont)
InvokeStatic parseInvokeStatic() {
tokens.consumeStart(INVOKE_STATIC);
String methodName = tokens.read();
List<Definition> args = parseDefinitionList();
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
Entity entity = new DummyEntity(methodName);
Selector selector = dummySelector(methodName, args.length);
tokens.consumeEnd();
return new InvokeStatic(entity, selector, cont, args);
}
/// (InvokeSuperMethod method args cont)
InvokeSuperMethod parseInvokeSuperMethod() {
tokens.consumeStart(INVOKE_SUPER_METHOD);
String methodName = tokens.read();
List<Definition> args = parseDefinitionList();
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
tokens.consumeEnd();
Selector selector = dummySelector(methodName, args.length);
return new InvokeSuperMethod(selector, cont, args);
}
/// (LetCont (cont args) (cont_body)) body
LetCont parseLetCont(bool recursive) {
tokens.consumeStart(recursive ? LET_CONT_RECURSIVE : LET_CONT);
// (name args) (cont_body))
tokens.consumeStart();
String name = tokens.read();
List<Parameter> params = <Parameter>[];
while (tokens.current != ")") {
String paramName = tokens.read();
Parameter param = new Parameter(new DummyElement(paramName));
name2variable[paramName] = param;
params.add(param);
}
tokens.consumeEnd();
Continuation cont = new Continuation(params);
name2variable[name] = cont;
cont.isRecursive = recursive;
cont.body = parseExpression();
tokens.consumeEnd();
// body
Expression body = parseExpression();
return new LetCont(cont, body);
}
/// (SetClosureVariable name value body)
SetClosureVariable parseSetClosureVariable() {
tokens.consumeStart(SET_CLOSURE_VARIABLE);
Local local = new DummyLocal(tokens.read());
Primitive value = name2variable[tokens.read()];
assert(value != null);
Expression body = parseExpression();
tokens.consumeEnd();
return new SetClosureVariable(local, value)
..plug(body);
}
/// (TypeOperator operator recv type cont)
TypeOperator parseTypeOperator() {
tokens.consumeStart(TYPE_OPERATOR);
String operator = tokens.read();
Primitive recv = name2variable[tokens.read()];
assert(recv != null);
dart_types.DartType type = new DummyNamedType(tokens.read());
Continuation cont = name2variable[tokens.read()];
assert(cont != null);
tokens.consumeEnd();
return new TypeOperator(operator, recv, type, cont);
}
/// (LetPrim name (primitive)) body
LetPrim parseLetPrim() {
tokens.consumeStart(LET_PRIM);
// name
String name = tokens.read();
// (primitive)
Primitive primitive = parsePrimitive();
name2variable[name] = primitive;
tokens.consumeEnd();
// body
Expression body = parseExpression();
return new LetPrim(primitive)..plug(body);
}
Primitive parsePrimitive() {
assert(tokens.current == "(");
switch (tokens.next) {
case CONSTANT:
return parseConstant();
case CREATE_FUNCTION:
return parseCreateFunction();
case GET_CLOSURE_VARIABLE:
return parseGetClosureVariable();
case LITERAL_LIST:
return parseLiteralList();
case LITERAL_MAP:
return parseLiteralMap();
case REIFY_TYPE_VAR:
return parseReifyTypeVar();
case THIS:
return parseThis();
default:
assert(false);
}
return null;
}
/// (Constant (constant))
Constant parseConstant() {
tokens.consumeStart(CONSTANT);
String tag = tokens.read();
// NullConstant.
if (tag == "null") {
tokens.consumeEnd();
return new Constant(null, new dart2js.NullConstant());
}
// BoolConstant.
if (tag == "true") {
tokens.consumeEnd();
return new Constant(null, new dart2js.TrueConstant());
} else if (tag == "false") {
tokens.consumeEnd();
return new Constant(null, new dart2js.FalseConstant());
}
// StringConstant.
if (tag == "StringConstant") {
tokens.consumeStart();
List<String> strings = <String>[];
do {
strings.add(tokens.read());
} while (tokens.current != ")");
tokens.consumeEnd();
String string = strings.join(" ");
assert(string.startsWith('"') && string.endsWith('"'));
dart2js.StringConstant value = new dart2js.StringConstant(
new LiteralDartString(string.substring(1, string.length - 1)));
tokens.consumeEnd();
return new Constant(null, value);
}
// IntConstant.
int intValue = int.parse(tag, onError: (_) => null);
if (intValue != null) {
tokens.consumeEnd();
return new Constant(null, new dart2js.IntConstant(intValue));
}
// DoubleConstant.
double doubleValue = double.parse(tag, (_) => null);
if (doubleValue != null) {
tokens.consumeEnd();
return new Constant(null, new dart2js.DoubleConstant(doubleValue));
}
assert(false);
return null;
}
/// (CreateFunction (definition))
CreateFunction parseCreateFunction() {
tokens.consumeStart(CREATE_FUNCTION);
FunctionDefinition def = parseFunctionDefinition();
tokens.consumeEnd();
return new CreateFunction(def);
}
/// (GetClosureVariable name)
GetClosureVariable parseGetClosureVariable() {
tokens.consumeStart(GET_CLOSURE_VARIABLE);
Local local = new DummyLocal(tokens.read());
tokens.consumeEnd();
return new GetClosureVariable(local);
}
/// (LiteralList values)
LiteralList parseLiteralList() {
tokens.consumeStart(LITERAL_LIST);
List<Primitive> values = parsePrimitiveList();
tokens.consumeEnd();
return new LiteralList(null, values);
}
/// (LiteralMap keys values)
LiteralMap parseLiteralMap() {
tokens.consumeStart(LITERAL_MAP);
List<Primitive> keys = parsePrimitiveList();
List<Primitive> values = parsePrimitiveList();
tokens.consumeEnd();
return new LiteralMap(null, keys, values);
}
/// (ReifyTypeVar type)
ReifyTypeVar parseReifyTypeVar() {
tokens.consumeStart(REIFY_TYPE_VAR);
TypeVariableElement type = new DummyElement(tokens.read());
tokens.consumeEnd();
return new ReifyTypeVar(type);
}
/// (This)
This parseThis() {
tokens.consumeStart(THIS);
tokens.consumeEnd();
return new This();
}
}