Chromium Code Reviews| Index: pkg/front_end/lib/src/libraries_reader.dart |
| diff --git a/pkg/front_end/lib/src/libraries_reader.dart b/pkg/front_end/lib/src/libraries_reader.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f67b4ffdf85628904b22e135b018fa9436ec6e12 |
| --- /dev/null |
| +++ b/pkg/front_end/lib/src/libraries_reader.dart |
| @@ -0,0 +1,205 @@ |
| +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) { |
|
Siggi Cherem (dart-lang)
2016/12/14 20:48:43
cool to see that we can use summaries for this - b
Paul Berry
2016/12/14 23:17:25
That's wonderful to hear (see my other comment)!
|
| + 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. |
| + dynamic _value; |
| + |
| + /// Indicates whether the value has been evaluated yet. |
| + bool isMaterialized = false; |
|
Siggi Cherem (dart-lang)
2016/12/14 20:48:43
minor nit: do we expect to have `null` values? If
Paul Berry
2016/12/14 23:17:25
Fair enough. Following your suggestion below gets
|
| + |
| + _ConstVariable(this.expr, this.context); |
| + |
| + /// Evaluate the constant (if necessary) and return it. |
| + dynamic get value { |
|
Siggi Cherem (dart-lang)
2016/12/14 20:48:43
similarly, if `null` is not a valid value, then:
Paul Berry
2016/12/14 23:17:25
Done.
|
| + if (!isMaterialized) _materialize(); |
| + return _value; |
| + } |
| + |
| + /// 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 and store it in [_value]. |
| + void _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); |
| + _value = stack[0]; |
| + isMaterialized = true; |
| + } |
| +} |