blob: ded5d9feee7795a893229d44c6ff5c468a331153 [file] [log] [blame]
import 'package:analyzer/src/summary/idl.dart';
import 'package:front_end/src/base/library_info.dart';
/// Decodes the contents of the SDK's "libraries.dart" file.
///
/// Caller should pass in the unlinked summary of the libraries.dart file. This
/// function will materialize the "libraries" constant based on information in
/// the summary.
///
/// Note that this code is not intended to be fully general; it makes some
/// assumptions about the structure of the "libraries.dart" file (such as what
/// declarations are expected to be present in it, and the types of those
/// declarations).
Map<String, LibraryInfo> readLibraries(UnlinkedUnit librariesUnit) {
var constContext = new _ConstContext(librariesUnit.references);
for (var variable in librariesUnit.variables) {
if (!variable.isConst) continue;
constContext.topLevelConstants[variable.name] =
new _ConstVariable(variable.initializer.bodyExpr, constContext);
}
for (var cls in librariesUnit.classes) {
if (cls.name == 'Maturity') {
for (var field in cls.fields) {
if (!field.isConst) continue;
constContext.maturityConstants[field.name] =
new _ConstVariable(field.initializer.bodyExpr, constContext);
}
}
}
return constContext.topLevelConstants['libraries'].value;
}
/// Function type used to invoke a constructor based on dynamic information.
///
/// Caller supplies two callbacks ([positional] and [named]) which can be used
/// to query the arguments passed to the constructor. These callbacks will
/// return the requested argument if it was provided; otherwise they will return
/// the supplied default value.
typedef dynamic _Constructor(dynamic positional(int i, [dynamic defaultValue]),
dynamic named(String name, [dynamic defaultValue]));
/// Contextual information used to evaluate constants in the "libraries.dart"
/// file.
class _ConstContext {
/// Top level constants in the "libraries.dart" file.
final topLevelConstants = <String, _ConstVariable>{};
/// Static constants in "libraries.dart"'s "Maturity" class.
final maturityConstants = <String, _ConstVariable>{};
/// References from the unlinked summary of the "libraries.dart" file.
final List<UnlinkedReference> references;
_ConstContext(this.references);
}
/// Information necessary to evaluate a single constant from the
/// "libraries.dart" file.
class _ConstVariable {
/// The constant expression from the unlinked summary.
final UnlinkedExpr expr;
/// Contextual information necessary to evaluate the constant.
final _ConstContext context;
/// The evaluated value, or `null` if it hasn't been evaluated yet.
dynamic _value;
_ConstVariable(this.expr, this.context);
/// Evaluate the constant (if necessary) and return it.
dynamic get value => _value ??= _materialize();
/// Find the constructor referred to by [entityRef] and return a function
/// which may be used to invoke it.
_Constructor _findConstructor(EntityRef entityRef) {
// This method is not fully general; we only support the constructor
// invocations that we expect to find in LibraryInfo.
assert(entityRef.implicitFunctionTypeIndices.isEmpty);
assert(entityRef.paramReference == 0);
assert(entityRef.syntheticParams.isEmpty);
assert(entityRef.syntheticReturnType == null);
assert(entityRef.typeArguments.isEmpty);
var reference = context.references[entityRef.reference];
assert(reference.prefixReference == 0);
switch (reference.name) {
case 'LibraryInfo':
return (dynamic positional(int i, [dynamic defaultValue]),
dynamic named(String name, [dynamic defaultValue])) =>
new LibraryInfo(positional(0),
categories: named('categories', ''),
dart2jsPath: named('dart2jsPath'),
dart2jsPatchPath: named('dart2jsPatchPath'),
implementation: named('implementation', false),
documented: named('documented', true),
maturity: named('maturity', Maturity.UNSPECIFIED),
platforms: named('platforms', DART2JS_PLATFORM | VM_PLATFORM));
case 'Maturity':
return (dynamic positional(int i, [dynamic defaultValue]),
dynamic named(String name, [dynamic defaultValue])) =>
new Maturity(positional(0), positional(1), positional(2));
default:
throw new UnimplementedError(
'Unexpected constructor reference: ${reference.name}');
}
}
/// Compute the value referred to by [entityRef].
dynamic _findReference(EntityRef entityRef) {
// This method is not fully general; we only support the references that we
// expect to find in LibraryInfo.
assert(entityRef.implicitFunctionTypeIndices.isEmpty);
assert(entityRef.paramReference == 0);
assert(entityRef.syntheticParams.isEmpty);
assert(entityRef.syntheticReturnType == null);
assert(entityRef.typeArguments.isEmpty);
var reference = context.references[entityRef.reference];
if (reference.prefixReference == 0) {
return context.topLevelConstants[reference.name].value;
} else {
assert(reference.prefixReference != 0);
var prefixReference = context.references[reference.prefixReference];
assert(prefixReference.name == 'Maturity');
assert(prefixReference.prefixReference == 0);
return context.maturityConstants[reference.name].value;
}
}
/// Compute the value of the constant.
dynamic _materialize() {
var stack = [];
var stringIndex = 0;
var intIndex = 0;
var referenceIndex = 0;
List popItems(int count) {
var items = stack.sublist(stack.length - count, stack.length);
stack.length -= count;
return items;
}
for (var operation in expr.operations) {
switch (operation) {
case UnlinkedExprOperation.pushString:
stack.add(expr.strings[stringIndex++]);
break;
case UnlinkedExprOperation.invokeConstructor:
var namedArgumentList = popItems(expr.ints[intIndex++]);
var namedArguments = <String, dynamic>{};
for (var namedArgument in namedArgumentList) {
namedArguments[expr.strings[stringIndex++]] = namedArgument;
}
var positionalArguments = popItems(expr.ints[intIndex++]);
stack.add(_findConstructor(expr.references[referenceIndex++])(
(i, [defaultValue]) => i < positionalArguments.length
? positionalArguments[i]
: defaultValue,
(name, [defaultValue]) => namedArguments.containsKey(name)
? namedArguments[name]
: defaultValue));
break;
case UnlinkedExprOperation.makeUntypedMap:
var map = {};
var numKeyValuePairs = expr.ints[intIndex++];
var keyValueList = popItems(numKeyValuePairs * 2);
for (var i = 0; i < numKeyValuePairs; i++) {
map[keyValueList[2 * i]] = keyValueList[2 * i + 1];
}
stack.add(map);
break;
case UnlinkedExprOperation.pushReference:
stack.add(_findReference(expr.references[referenceIndex++]));
break;
case UnlinkedExprOperation.pushInt:
stack.add(expr.ints[intIndex++]);
break;
case UnlinkedExprOperation.pushFalse:
stack.add(false);
break;
case UnlinkedExprOperation.pushTrue:
stack.add(true);
break;
case UnlinkedExprOperation.bitOr:
var y = stack.removeLast();
var x = stack.removeLast();
stack.add(x | y);
break;
default:
throw new UnimplementedError(
'Unexpected expression in libraries.dart: $operation');
}
}
assert(stringIndex == expr.strings.length);
assert(intIndex == expr.ints.length);
assert(referenceIndex == expr.references.length);
assert(stack.length == 1);
if (stack[0] == null) {
throw new StateError('Unexpected null constant in libraries.dart');
}
return stack[0];
}
}