mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 03:27:43 +00:00
dart2js: add initial support for lookup-maps
R=herhut@google.com, sra@google.com Review URL: https://codereview.chromium.org//1320503003 .
This commit is contained in:
parent
e30f7638e2
commit
2c660aa2f0
|
@ -339,6 +339,10 @@ abstract class Backend {
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Called after the queue is closed. [onQueueEmpty] may be called multiple
|
||||
/// times, but [onQueueClosed] is only called once.
|
||||
void onQueueClosed() {}
|
||||
|
||||
/// Called after [element] has been resolved.
|
||||
// TODO(johnniwinther): Change [TreeElements] to [Registry] or a dependency
|
||||
// node. [elements] is currently unused by the implementation.
|
||||
|
|
|
@ -112,7 +112,6 @@ class CodegenRegistry extends Registry {
|
|||
|
||||
void registerCompileTimeConstant(ConstantValue constant) {
|
||||
backend.registerCompileTimeConstant(constant, this);
|
||||
backend.constants.addCompileTimeConstantForEmission(constant);
|
||||
}
|
||||
|
||||
void registerTypeVariableBoundsSubtypeCheck(DartType subtype,
|
||||
|
@ -146,6 +145,7 @@ class CodegenRegistry extends Registry {
|
|||
|
||||
void registerTypeConstant(ClassElement element) {
|
||||
backend.customElementsAnalysis.registerTypeConstant(element, world);
|
||||
backend.lookupMapAnalysis.registerTypeConstant(element);
|
||||
}
|
||||
|
||||
void registerStaticInvocation(Element element) {
|
||||
|
|
|
@ -1240,6 +1240,7 @@ abstract class Compiler implements DiagnosticListener {
|
|||
}
|
||||
emptyQueue(world);
|
||||
world.queueIsClosed = true;
|
||||
backend.onQueueClosed();
|
||||
assert(compilationFailed || world.checkNoEnqueuedInvokedInstanceMethods());
|
||||
}
|
||||
|
||||
|
|
|
@ -233,6 +233,8 @@ class JavaScriptBackend extends Backend {
|
|||
new Uri(scheme: 'dart', path: '_js_embedded_names');
|
||||
static final Uri DART_ISOLATE_HELPER =
|
||||
new Uri(scheme: 'dart', path: '_isolate_helper');
|
||||
static final Uri PACKAGE_LOOKUP_MAP =
|
||||
new Uri(scheme: 'package', path: 'lookup_map/lookup_map.dart');
|
||||
|
||||
static const String INVOKE_ON = '_getCachedInvocation';
|
||||
static const String START_ROOT_ISOLATE = 'startRootIsolate';
|
||||
|
@ -608,6 +610,9 @@ class JavaScriptBackend extends Backend {
|
|||
/// constructors for custom elements.
|
||||
CustomElementsAnalysis customElementsAnalysis;
|
||||
|
||||
/// Codegen support for tree-shaking entries of `LookupMap`.
|
||||
LookupMapAnalysis lookupMapAnalysis;
|
||||
|
||||
/// Support for classifying `noSuchMethod` implementations.
|
||||
NoSuchMethodRegistry noSuchMethodRegistry;
|
||||
|
||||
|
@ -641,6 +646,7 @@ class JavaScriptBackend extends Backend {
|
|||
compiler, namer, generateSourceMap, useStartupEmitter);
|
||||
typeVariableHandler = new TypeVariableHandler(compiler);
|
||||
customElementsAnalysis = new CustomElementsAnalysis(this);
|
||||
lookupMapAnalysis = new LookupMapAnalysis(this);
|
||||
noSuchMethodRegistry = new NoSuchMethodRegistry(this);
|
||||
constantCompilerTask = new JavaScriptConstantTask(compiler);
|
||||
resolutionCallbacks = new JavaScriptResolutionCallbacks(this);
|
||||
|
@ -949,11 +955,23 @@ class JavaScriptBackend extends Backend {
|
|||
}
|
||||
}
|
||||
|
||||
void registerCompileTimeConstant(ConstantValue constant, Registry registry) {
|
||||
void registerCompileTimeConstant(ConstantValue constant, Registry registry,
|
||||
{bool addForEmission: true}) {
|
||||
registerCompileTimeConstantInternal(constant, registry);
|
||||
for (ConstantValue dependency in constant.getDependencies()) {
|
||||
registerCompileTimeConstant(dependency, registry);
|
||||
|
||||
if (!registry.isForResolution &&
|
||||
lookupMapAnalysis.isLookupMap(constant)) {
|
||||
// Note: internally, this registration will temporarily remove the
|
||||
// constant dependencies and add them later on-demand.
|
||||
lookupMapAnalysis.registerLookupMapReference(constant);
|
||||
}
|
||||
|
||||
for (ConstantValue dependency in constant.getDependencies()) {
|
||||
registerCompileTimeConstant(dependency, registry,
|
||||
addForEmission: false);
|
||||
}
|
||||
|
||||
if (addForEmission) constants.addCompileTimeConstantForEmission(constant);
|
||||
}
|
||||
|
||||
void registerCompileTimeConstantInternal(ConstantValue constant,
|
||||
|
@ -971,6 +989,11 @@ class JavaScriptBackend extends Backend {
|
|||
} else if (constant.isType) {
|
||||
enqueueInResolution(getCreateRuntimeType(), registry);
|
||||
registry.registerInstantiation(typeImplementation.rawType);
|
||||
TypeConstantValue typeConstant = constant;
|
||||
DartType representedType = typeConstant.representedType;
|
||||
if (representedType != const DynamicType()) {
|
||||
lookupMapAnalysis.registerTypeConstant(representedType.element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -996,7 +1019,7 @@ class JavaScriptBackend extends Backend {
|
|||
Registry registry) {
|
||||
assert(registry.isForResolution);
|
||||
ConstantValue constant = constants.getConstantValueForMetadata(metadata);
|
||||
registerCompileTimeConstant(constant, registry);
|
||||
registerCompileTimeConstant(constant, registry, addForEmission: false);
|
||||
metadataConstants.add(new Dependency(constant, annotatedElement));
|
||||
}
|
||||
|
||||
|
@ -1129,6 +1152,13 @@ class JavaScriptBackend extends Backend {
|
|||
}
|
||||
|
||||
customElementsAnalysis.registerInstantiatedClass(cls, enqueuer);
|
||||
if (!enqueuer.isResolutionQueue) {
|
||||
lookupMapAnalysis.registerInstantiatedClass(cls);
|
||||
}
|
||||
}
|
||||
|
||||
void registerInstantiatedType(InterfaceType type, Registry registry) {
|
||||
lookupMapAnalysis.registerInstantiatedType(type, registry);
|
||||
}
|
||||
|
||||
void registerUseInterceptor(Enqueuer enqueuer) {
|
||||
|
@ -1438,7 +1468,6 @@ class JavaScriptBackend extends Backend {
|
|||
constants.getConstantValueForVariable(element);
|
||||
if (initialValue != null) {
|
||||
registerCompileTimeConstant(initialValue, work.registry);
|
||||
constants.addCompileTimeConstantForEmission(initialValue);
|
||||
// We don't need to generate code for static or top-level
|
||||
// variables. For instance variables, we may need to generate
|
||||
// the checked setter.
|
||||
|
@ -2133,6 +2162,8 @@ class JavaScriptBackend extends Backend {
|
|||
jsBuiltinEnum = find(library, 'JsBuiltin');
|
||||
} else if (uri == Uris.dart_html) {
|
||||
htmlLibraryIsLoaded = true;
|
||||
} else if (uri == PACKAGE_LOOKUP_MAP) {
|
||||
lookupMapAnalysis.initRuntimeClass(find(library, 'LookupMap'));
|
||||
}
|
||||
annotations.onLibraryScanned(library);
|
||||
});
|
||||
|
@ -2569,7 +2600,8 @@ class JavaScriptBackend extends Backend {
|
|||
registerCompileTimeConstant(
|
||||
dependency.constant,
|
||||
new CodegenRegistry(compiler,
|
||||
dependency.annotatedElement.analyzableElement.treeElements));
|
||||
dependency.annotatedElement.analyzableElement.treeElements),
|
||||
addForEmission: false);
|
||||
}
|
||||
metadataConstants.clear();
|
||||
}
|
||||
|
@ -2577,6 +2609,8 @@ class JavaScriptBackend extends Backend {
|
|||
return true;
|
||||
}
|
||||
|
||||
void onQueueClosed() => lookupMapAnalysis.onQueueClosed();
|
||||
|
||||
void onElementResolved(Element element, TreeElements elements) {
|
||||
if ((element.isFunction || element.isGenerativeConstructor) &&
|
||||
annotations.noInline(element)) {
|
||||
|
|
|
@ -169,7 +169,6 @@ class CustomElementsAnalysisJoin {
|
|||
ConstantValue constant = makeTypeConstant(classElement);
|
||||
backend.registerCompileTimeConstant(
|
||||
constant, compiler.globalDependencies);
|
||||
backend.constants.addCompileTimeConstantForEmission(constant);
|
||||
}
|
||||
}
|
||||
activeClasses.addAll(newActiveClasses);
|
||||
|
|
|
@ -88,6 +88,7 @@ import '../world.dart' show
|
|||
import 'codegen/task.dart';
|
||||
import 'constant_system_javascript.dart';
|
||||
import 'patch_resolver.dart';
|
||||
import 'lookup_map_analysis.dart' show LookupMapAnalysis;
|
||||
|
||||
part 'backend.dart';
|
||||
part 'checked_mode_helpers.dart';
|
||||
|
|
335
pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
Normal file
335
pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
Normal file
|
@ -0,0 +1,335 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
/// Analysis to determine how to generate code for `LookupMap`s.
|
||||
library compiler.src.js_backend.lookup_map_analysis;
|
||||
|
||||
import '../common/registry.dart' show Registry;
|
||||
import '../compiler.dart' show Compiler;
|
||||
import '../constants/values.dart' show
|
||||
ConstantValue,
|
||||
ConstructedConstantValue,
|
||||
ListConstantValue,
|
||||
NullConstantValue,
|
||||
TypeConstantValue;
|
||||
import '../dart_types.dart' show DartType;
|
||||
import '../elements/elements.dart' show Elements, Element, ClassElement,
|
||||
FieldElement, FunctionElement, FunctionSignature;
|
||||
import '../enqueue.dart' show Enqueuer;
|
||||
import 'js_backend.dart' show JavaScriptBackend;
|
||||
import '../dart_types.dart' show DynamicType, InterfaceType;
|
||||
|
||||
/// An analysis and optimization to remove unused entries from a `LookupMap`.
|
||||
///
|
||||
/// `LookupMaps` are defined in `package:lookup_map/lookup_map.dart`. They are
|
||||
/// simple maps that contain constant expressions as keys, and that only support
|
||||
/// the lookup operation.
|
||||
///
|
||||
/// This analysis and optimization will tree-shake the contents of the maps by
|
||||
/// looking at the program and finding which keys are clearly unused. Not all
|
||||
/// constants can be approximated statically, so this optimization is limited to
|
||||
/// the following keys:
|
||||
///
|
||||
/// * Const expressions that can only be created via const constructors. This
|
||||
/// excludes primitives, strings, and any const type that overrides the ==
|
||||
/// operator.
|
||||
///
|
||||
/// * Type literals.
|
||||
///
|
||||
/// Type literals are more complex than const expressions because they can be
|
||||
/// created in multiple ways. We can approximate the possible set of keys if we
|
||||
/// follow these rules:
|
||||
///
|
||||
/// * Include all type-literals used explicitly in the code (excluding
|
||||
/// obviously the uses that can be removed from LookupMaps)
|
||||
///
|
||||
/// * Include every reflectable type-literal if a mirror API is used to create
|
||||
/// types (e.g. ClassMirror.reflectedType).
|
||||
///
|
||||
/// * Include all allocated types if the program contains `e.runtimeType`
|
||||
/// expressions.
|
||||
///
|
||||
/// * Include all generic-type arguments, if the program uses type
|
||||
/// variables in expressions such as `class A<T> { Type get extract => T }`.
|
||||
///
|
||||
// TODO(sigmund): add support for const expressions, currently this
|
||||
// implementation only supports Type literals. To support const expressions we
|
||||
// need to change some of the invariants below (e.g. we can no longer use the
|
||||
// ClassElement of a type to refer to keys we need to discover).
|
||||
// TODO(sigmund): detect uses of mirrors
|
||||
class LookupMapAnalysis {
|
||||
/// Reference to [JavaScriptBackend] to be able to enqueue work when we
|
||||
/// discover that a key in a map is potentially used.
|
||||
final JavaScriptBackend backend;
|
||||
|
||||
/// The resolved [ClassElement] associated with `LookupMap`.
|
||||
ClassElement typeLookupMapClass;
|
||||
|
||||
/// The resolved [FieldElement] for `LookupMap._entries`.
|
||||
FieldElement entriesField;
|
||||
|
||||
/// The resolved [FieldElement] for `LookupMap._key`.
|
||||
FieldElement keyField;
|
||||
|
||||
/// The resolved [FieldElement] for `LookupMap._value`.
|
||||
FieldElement valueField;
|
||||
|
||||
/// Constant instances of `LookupMap` and information about them tracked by
|
||||
/// this analysis.
|
||||
final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {};
|
||||
|
||||
/// Types that we have discovered to be in use in the program.
|
||||
final _inUse = new Set<ClassElement>();
|
||||
|
||||
/// Pending work to do if we discover that a new type is in use. For each type
|
||||
/// that we haven't seen, we record the list of lookup-maps that use such type
|
||||
/// as a key.
|
||||
final _pending = <ClassElement, List<_LookupMapInfo>>{};
|
||||
|
||||
/// Whether the backend is currently processing the codegen queue.
|
||||
// TODO(sigmund): is there a better way to do this. Do we need to plumb the
|
||||
// enqueuer on each callback?
|
||||
bool get _inCodegen => backend.compiler.phase == Compiler.PHASE_COMPILING;
|
||||
|
||||
LookupMapAnalysis(this.backend);
|
||||
|
||||
/// Whether this analysis and optimization is enabled.
|
||||
bool get _isEnabled {
|
||||
// `lookupMap==off` kept here to make it easy to test disabling this feature
|
||||
if (const String.fromEnvironment('lookupMap') == 'off') return false;
|
||||
return typeLookupMapClass != null;
|
||||
}
|
||||
|
||||
/// Initializes this analysis by providing the resolver information of
|
||||
/// `LookupMap`.
|
||||
void initRuntimeClass(ClassElement cls) {
|
||||
cls.computeType(backend.compiler);
|
||||
entriesField = cls.lookupMember('_entries');
|
||||
keyField = cls.lookupMember('_key');
|
||||
valueField = cls.lookupMember('_value');
|
||||
// TODO(sigmund): Maybe inline nested maps make the output code smaller?
|
||||
typeLookupMapClass = cls;
|
||||
}
|
||||
|
||||
/// Whether [constant] is an instance of a `LookupMap`.
|
||||
bool isLookupMap(ConstantValue constant) =>
|
||||
_isEnabled &&
|
||||
constant is ConstructedConstantValue &&
|
||||
constant.type.asRaw().element.isSubclassOf(typeLookupMapClass);
|
||||
|
||||
/// Registers an instance of a lookup-map with the analysis.
|
||||
void registerLookupMapReference(ConstantValue lookupMap) {
|
||||
if (!_isEnabled || !_inCodegen) return;
|
||||
assert(isLookupMap(lookupMap));
|
||||
_lookupMaps.putIfAbsent(lookupMap,
|
||||
() => new _LookupMapInfo(lookupMap, this).._updateUsed());
|
||||
}
|
||||
|
||||
/// Records that [type] is used in the program, and updates every map that
|
||||
/// has it as a key.
|
||||
void _addUse(ClassElement type) {
|
||||
if (_inUse.add(type)) {
|
||||
_pending[type]?.forEach((info) => info._markUsed(type));
|
||||
_pending.remove(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback from the enqueuer, invoked when [element] is instantiated.
|
||||
void registerInstantiatedClass(ClassElement element) {
|
||||
if (!_isEnabled || !_inCodegen) return;
|
||||
// TODO(sigmund): only add if .runtimeType is ever used
|
||||
_addUse(element);
|
||||
}
|
||||
|
||||
/// Callback from the enqueuer, invoked when [type] is instantiated.
|
||||
void registerInstantiatedType(InterfaceType type, Registry registry) {
|
||||
if (!_isEnabled || !_inCodegen) return;
|
||||
// TODO(sigmund): only add if .runtimeType is ever used
|
||||
_addUse(type.element);
|
||||
// TODO(sigmund): only do this when type-argument expressions are used?
|
||||
_addGenerics(type, registry);
|
||||
}
|
||||
|
||||
/// Records generic type arguments in [type], in case they are retrieved and
|
||||
/// returned using a type-argument expression.
|
||||
void _addGenerics(InterfaceType type, Registry registry) {
|
||||
if (!type.isGeneric) return;
|
||||
for (var arg in type.typeArguments) {
|
||||
if (arg is InterfaceType) {
|
||||
_addUse(arg.element);
|
||||
// Note: this call was needed to generate correct code for
|
||||
// type_lookup_map/generic_type_test
|
||||
// TODO(sigmund): can we get rid of this?
|
||||
backend.registerInstantiatedConstantType(
|
||||
backend.typeImplementation.rawType, registry);
|
||||
_addGenerics(arg, registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback from the codegen enqueuer, invoked when a type constant
|
||||
/// corresponding to the [element] is used in the program.
|
||||
void registerTypeConstant(Element element) {
|
||||
if (!_isEnabled || !_inCodegen) return;
|
||||
assert(element.isClass);
|
||||
_addUse(element);
|
||||
}
|
||||
|
||||
/// Callback from the backend, invoked when reaching the end of the enqueuing
|
||||
/// process, but before emitting the code. At this moment we have discovered
|
||||
/// all types used in the program and we can tree-shake anything that is
|
||||
/// unused.
|
||||
void onQueueClosed() {
|
||||
if (!_isEnabled || !_inCodegen) return;
|
||||
|
||||
_lookupMaps.values.forEach((info) {
|
||||
assert (!info.emitted);
|
||||
info.emitted = true;
|
||||
info._prepareForEmission();
|
||||
});
|
||||
|
||||
// When --verbose is passed, we show the total number and set of keys that
|
||||
// were tree-shaken from lookup maps.
|
||||
Compiler compiler = backend.compiler;
|
||||
if (compiler.verbose) {
|
||||
var sb = new StringBuffer();
|
||||
int count = 0;
|
||||
for (var info in _lookupMaps.values) {
|
||||
for (var key in info.unusedEntries.keys) {
|
||||
if (count != 0) sb.write(',');
|
||||
sb.write(key.unparse());
|
||||
count++;
|
||||
}
|
||||
}
|
||||
compiler.log(count == 0
|
||||
? 'lookup-map: nothing was tree-shaken'
|
||||
: 'lookup-map: found $count unused keys ($sb)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal information about the entries on a lookup-map.
|
||||
class _LookupMapInfo {
|
||||
/// The original reference to the constant value.
|
||||
///
|
||||
/// This reference will be mutated in place to remove it's entries when the
|
||||
/// map is first seen during codegen, and to restore them (or a subset of
|
||||
/// them) when we have finished discovering which entries are used. This has
|
||||
/// the side-effect that `orignal.getDependencies()` will be empty during
|
||||
/// most of codegen until we are ready to emit the constants. However,
|
||||
/// restoring the entries before emitting code lets us keep the emitter logic
|
||||
/// agnostic of this optimization.
|
||||
final ConstructedConstantValue original;
|
||||
|
||||
/// Reference to the lookup map analysis to be able to refer to data shared
|
||||
/// accross infos.
|
||||
final LookupMapAnalysis analysis;
|
||||
|
||||
/// Whether we have already emitted this constant.
|
||||
bool emitted = false;
|
||||
|
||||
/// Whether the `LookupMap` constant was built using the `LookupMap.pair`
|
||||
/// constructor.
|
||||
bool singlePair;
|
||||
|
||||
/// Entries in the lookup map whose keys have not been seen in the rest of the
|
||||
/// program.
|
||||
Map<ClassElement, ConstantValue> unusedEntries =
|
||||
<ClassElement, ConstantValue>{};
|
||||
|
||||
/// Entries that have been used, and thus will be part of the generated code.
|
||||
Map<ClassElement, ConstantValue> usedEntries =
|
||||
<ClassElement, ConstantValue>{};
|
||||
|
||||
/// Internal helper to memoize the mapping between map class elements and
|
||||
/// their corresponding type constants.
|
||||
Map<ClassElement, TypeConstantValue> _typeConstants =
|
||||
<ClassElement, TypeConstantValue>{};
|
||||
|
||||
/// Creates and initializes the information containing all keys of the
|
||||
/// original map marked as unused.
|
||||
_LookupMapInfo(this.original, this.analysis) {
|
||||
ConstantValue key = original.fields[analysis.keyField];
|
||||
singlePair = !key.isNull;
|
||||
|
||||
if (singlePair) {
|
||||
TypeConstantValue typeKey = key;
|
||||
ClassElement cls = typeKey.representedType.element;
|
||||
_typeConstants[cls] = typeKey;
|
||||
unusedEntries[cls] = original.fields[analysis.valueField];
|
||||
|
||||
// Note: we modify the constant in-place, see comment in [original].
|
||||
original.fields[analysis.keyField] = new NullConstantValue();
|
||||
original.fields[analysis.valueField] = new NullConstantValue();
|
||||
} else {
|
||||
ListConstantValue list = original.fields[analysis.entriesField];
|
||||
List<ConstantValue> keyValuePairs = list.entries;
|
||||
for (int i = 0; i < keyValuePairs.length; i += 2) {
|
||||
TypeConstantValue type = keyValuePairs[i];
|
||||
ClassElement cls = type.representedType.element;
|
||||
if (cls == null || !cls.isClass) {
|
||||
// TODO(sigmund): report an error
|
||||
continue;
|
||||
}
|
||||
_typeConstants[cls] = type;
|
||||
unusedEntries[cls] = keyValuePairs[i + 1];
|
||||
}
|
||||
|
||||
// Note: we modify the constant in-place, see comment in [original].
|
||||
original.fields[analysis.entriesField] =
|
||||
new ListConstantValue(list.type, []);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check every key in unusedEntries and mark it as used if the analysis has
|
||||
/// already discovered them. This is meant to be called once to finalize
|
||||
/// initialization after constructing an instance of this class. Afterwards,
|
||||
/// we call [_markUsed] on each individual key as it gets discovered.
|
||||
void _updateUsed() {
|
||||
// Note: we call toList because `_markUsed` modifies the map.
|
||||
for (ClassElement type in unusedEntries.keys.toList()) {
|
||||
if (analysis._inUse.contains(type)) {
|
||||
_markUsed(type);
|
||||
} else {
|
||||
analysis._pending.putIfAbsent(type, () => []).add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Marks that [type] is a key that has been seen, and thus, the corresponding
|
||||
/// entry in this map should be considered reachable.
|
||||
void _markUsed(ClassElement type) {
|
||||
assert(!emitted);
|
||||
assert(unusedEntries.containsKey(type));
|
||||
assert(!usedEntries.containsKey(type));
|
||||
ConstantValue constant = unusedEntries.remove(type);
|
||||
usedEntries[type] = constant;
|
||||
analysis.backend.registerCompileTimeConstant(constant,
|
||||
analysis.backend.compiler.globalDependencies,
|
||||
addForEmission: false);
|
||||
}
|
||||
|
||||
/// Restores [original] to contain all of the entries marked as possibly used.
|
||||
void _prepareForEmission() {
|
||||
ListConstantValue originalEntries = original.fields[analysis.entriesField];
|
||||
DartType listType = originalEntries.type;
|
||||
List<ConstantValue> keyValuePairs = <ConstantValue>[];
|
||||
usedEntries.forEach((key, value) {
|
||||
keyValuePairs.add(_typeConstants[key]);
|
||||
keyValuePairs.add(value);
|
||||
});
|
||||
|
||||
// Note: we are restoring the entries here, see comment in [original].
|
||||
if (singlePair) {
|
||||
assert (keyValuePairs.length == 0 || keyValuePairs.length == 2);
|
||||
if (keyValuePairs.length == 2) {
|
||||
original.fields[analysis.keyField] = keyValuePairs[0];
|
||||
original.fields[analysis.valueField] = keyValuePairs[1];
|
||||
}
|
||||
} else {
|
||||
original.fields[analysis.entriesField] =
|
||||
new ListConstantValue(listType, keyValuePairs);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1924,7 +1924,6 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor {
|
|||
generateConstant(node.constant, node.sourceInformation);
|
||||
|
||||
registry.registerCompileTimeConstant(node.constant);
|
||||
backend.constants.addCompileTimeConstantForEmission(node.constant);
|
||||
}
|
||||
|
||||
visitNot(HNot node) {
|
||||
|
|
|
@ -11,6 +11,8 @@ dependencies:
|
|||
sdk_library_metadata:
|
||||
path: ../../sdk/lib/_internal/sdk_library_metadata
|
||||
dart2js_info: ^0.0.2
|
||||
lookup_map:
|
||||
path: ../lookup_map
|
||||
|
||||
|
||||
# Uncomment if running gclient, so you can depend directly on the downloaded
|
||||
|
|
6
pkg/lookup_map/AUTHORS
Normal file
6
pkg/lookup_map/AUTHORS
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Below is a list of people and organizations that have contributed
|
||||
# to the project. Names should be added to the list like so:
|
||||
#
|
||||
# Name/Organization <email address>
|
||||
|
||||
Google Inc.
|
4
pkg/lookup_map/CHANGELOG.md
Normal file
4
pkg/lookup_map/CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Changelog
|
||||
|
||||
## 0.0.1
|
||||
- Initial version of `LookupMap`
|
26
pkg/lookup_map/LICENSE
Normal file
26
pkg/lookup_map/LICENSE
Normal file
|
@ -0,0 +1,26 @@
|
|||
Copyright 2015, the Dart project authors. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
pkg/lookup_map/README.md
Normal file
29
pkg/lookup_map/README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Lookup maps
|
||||
|
||||
This package contains the definition of `LookupMap`: a simple, but very
|
||||
restricted map. The map can only hold constant keys and the only way to use the
|
||||
map is to retrieve values with a key you already have. Expect for lookup, any
|
||||
other operation in `Map` (like forEach, keys, values, length, etc) is not
|
||||
available.
|
||||
|
||||
Constant `LookupMap`s are understood by dart2js and can be tree-shaken
|
||||
internally: if a key is not used elsewhere in the program, its entry can be
|
||||
deleted from the map during compilation without changing the program's behavior.
|
||||
Currently dart2js supports tree-shaking keys that are Type literals, and any
|
||||
const expression that can only be created with a const constructor. This means
|
||||
that primitives, Strings, and constant objects that override the `==` operator
|
||||
cannot be tree-shaken.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
`LookupMap` is unlikely going to be useful for individual developers writing
|
||||
code by hand. It is mainly intended as a helper utility for frameworks that need
|
||||
to autogenerate data and associate it with a type in the program. For example,
|
||||
this can be used by a dependency injection system to record how to create
|
||||
instances of a given type. A dependency injection framework can store in a
|
||||
`LookupMap` all the information it needs for every injectable type in every
|
||||
library and package. When compiling a specific application, dart2js can
|
||||
tree-shake the data of types that are not used by the application. Similarly,
|
||||
this can also be used by serialization/deserialization packages that can store
|
||||
in a `LookupMap` the deserialization logic for a given type.
|
93
pkg/lookup_map/lib/lookup_map.dart
Normal file
93
pkg/lookup_map/lib/lookup_map.dart
Normal file
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
/// Defines [LookupMap], a simple map that can be optimized by dart2js.
|
||||
library lookup_map;
|
||||
|
||||
/// [LookupMap] is a simple, but very restricted map. The map can only hold
|
||||
/// constant keys and the only way to use the map is to retrieve values with a
|
||||
/// key you already have. Expect for lookup, any other operation in [Map] (like
|
||||
/// forEach, keys, values, length, etc) is not available.
|
||||
///
|
||||
/// Constant [LookupMap]s are understood by dart2js and can be tree-shaken
|
||||
/// internally: if a key is not used elsewhere in the program, its entry can be
|
||||
/// deleted from the map during compilation without changing the program's
|
||||
/// behavior. Currently dart2js supports tree-shaking keys that are `Type`
|
||||
/// literals, and any const expression that can only be created with a const
|
||||
/// constructor. This means that primitives, Strings, and constant objects that
|
||||
/// override the `==` operator cannot be tree-shaken.
|
||||
///
|
||||
/// Note: [LookupMap] is unlikely going to be useful for individual developers
|
||||
/// writing code by hand. It is mainly intended as a helper utility for
|
||||
/// frameworks that need to autogenerate data and associate it with a type in
|
||||
/// the program. For example, this can be used by a dependency injection system
|
||||
/// to record how to create instances of a given type. A dependency injection
|
||||
/// framework can store in a [LookupMap] all the information it needs for every
|
||||
/// injectable type in every library and package. When compiling a specific
|
||||
/// application, dart2js can tree-shake the data of types that are not used by
|
||||
/// the application. Similarly, this can also be used by
|
||||
/// serialization/deserialization packages that can store in a [LookupMap] the
|
||||
/// deserialization logic for a given type.
|
||||
class LookupMap<K, V> {
|
||||
/// The key for [LookupMap]s with a single key/value pair.
|
||||
final K _key;
|
||||
|
||||
/// The value for [LookupMap]s with a single key/value pair.
|
||||
final V _value;
|
||||
|
||||
/// List of alternating key-value pairs in the map.
|
||||
final List _entries;
|
||||
|
||||
/// Other maps to which this map delegates lookup operations if the key is not
|
||||
/// found on [entries]. See [LookupMap]'s constructor for details.
|
||||
final List<LookupMap<K, V>> _nestedMaps;
|
||||
|
||||
/// Creates a lookup-map given a list of key-value pair [entries], and
|
||||
/// optionally additional entries from other [LookupMap]s.
|
||||
///
|
||||
/// When doing a lookup, if the key is not found on [entries]. The lookup will
|
||||
/// be performed in reverse order of the list of [nestedMaps], so a later
|
||||
/// entry for a key shadows previous entries. For example, in:
|
||||
///
|
||||
/// const map = const LookupMap(const [A, 1],
|
||||
/// const [const LookupMap(const [A, 2, B, 4]),
|
||||
/// const LookupMap(const [A, 3, B, 5]));
|
||||
///
|
||||
/// `map[A]` returns `1` and `map[B]` returns `5`.
|
||||
///
|
||||
/// Note: in the future we expect to change [entries] to be a const map
|
||||
/// instead of a list of key-value pairs.
|
||||
// TODO(sigmund): make entries a map once we fix TypeImpl.== (issue #17207).
|
||||
const LookupMap(List entries, [List<LookupMap<K, V>> nestedMaps = const []])
|
||||
: _key = null, _value = null, _entries = entries, _nestedMaps = nestedMaps;
|
||||
|
||||
/// Creates a lookup map with a single key-value pair.
|
||||
const LookupMap.pair(K key, V value)
|
||||
: _key = key, _value = value, _entries = const [], _nestedMaps = const [];
|
||||
|
||||
/// Return the data corresponding to [key].
|
||||
V operator[](K key) {
|
||||
var map = _flatMap[this];
|
||||
if (map == null) {
|
||||
map = {};
|
||||
_addEntriesTo(map);
|
||||
_flatMap[this] = map;
|
||||
}
|
||||
return map[key];
|
||||
}
|
||||
|
||||
/// Add to [map] entries from [nestedMaps] and from [entries] according to the
|
||||
/// precedense order described in [nestedMaps].
|
||||
_addEntriesTo(Map map) {
|
||||
_nestedMaps.forEach((m) => m._addEntriesTo(map));
|
||||
for (var i = 0; i < _entries.length; i += 2) {
|
||||
map[_entries[i]] = _entries[i + 1];
|
||||
}
|
||||
if (_key != null) map[_key] = _value;
|
||||
}
|
||||
}
|
||||
|
||||
/// An expando that stores a flatten version of a [LookupMap], this is
|
||||
/// computed and stored the first time the map is accessed.
|
||||
final _flatMap = new Expando('_flat_map');
|
3
pkg/lookup_map/pubspec.yaml
Normal file
3
pkg/lookup_map/pubspec.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
name: lookup_map
|
||||
description: a lookup-only map that can be tree-shaken by dart2js
|
||||
version: 0.0.1
|
261
tests/compiler/dart2js/lookup_map_test.dart
Normal file
261
tests/compiler/dart2js/lookup_map_test.dart
Normal file
|
@ -0,0 +1,261 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
library tests.dart2js.lookup_map_test;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
import 'compiler_helper.dart';
|
||||
|
||||
main() {
|
||||
test('live entries are kept', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
]);
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, contains("the-text-for-A"));
|
||||
});
|
||||
|
||||
test('live entries are kept - single-pair', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
const map = const LookupMap.pair(A, "the-text-for-A");
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, contains("the-text-for-A"));
|
||||
});
|
||||
|
||||
test('unused entries are removed', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
]);
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
|
||||
test('unused entries are removed - nested maps', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const map = const LookupMap(const [], const [
|
||||
const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
]),
|
||||
]);
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
|
||||
test('unused entries are removed - single-pair', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const map = const LookupMap.pair(A, "the-text-for-A");
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
|
||||
test('unused entries are removed - nested single-pair', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const map = const LookupMap(const [], const [
|
||||
const LookupMap.pair(A, "the-text-for-A"),
|
||||
const LookupMap.pair(B, "the-text-for-B"),
|
||||
]);
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
|
||||
test('works if entries are declared separate from map', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const entries = const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
];
|
||||
const map = const LookupMap(entries);
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
|
||||
test('escaping entries disable tree-shaking', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const entries = const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
];
|
||||
const map = const LookupMap(entries);
|
||||
main() {
|
||||
entries.forEach(print);
|
||||
print(map[A]);
|
||||
}
|
||||
""");
|
||||
expect(generated, contains("the-text-for-B"));
|
||||
});
|
||||
|
||||
test('uses include recursively reachable data', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
class C{}
|
||||
class D{}
|
||||
class E{}
|
||||
const map = const LookupMap(const [
|
||||
A, const ["the-text-for-A", B],
|
||||
B, const ["the-text-for-B", C],
|
||||
C, const ["the-text-for-C"],
|
||||
D, const ["the-text-for-D", E],
|
||||
E, const ["the-text-for-E"],
|
||||
]);
|
||||
main() => print(map[map[A][1]]);
|
||||
""");
|
||||
expect(generated, contains("the-text-for-A"));
|
||||
expect(generated, contains("the-text-for-B"));
|
||||
expect(generated, contains("the-text-for-C"));
|
||||
expect(generated, isNot(contains("the-text-for-D")));
|
||||
expect(generated, isNot(contains("the-text-for-E")));
|
||||
});
|
||||
|
||||
test('uses are found through newly discovered code', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{ A(B x);}
|
||||
class B{}
|
||||
class C{}
|
||||
class D{}
|
||||
class E{}
|
||||
createA() => new A(map[B][1]());
|
||||
createB() => new B();
|
||||
const map = const LookupMap(const [
|
||||
A, const ["the-text-for-A", createA],
|
||||
B, const ["the-text-for-B", createB],
|
||||
C, const ["the-text-for-C"],
|
||||
]);
|
||||
main() => print(map[A][1]());
|
||||
""");
|
||||
expect(generated, contains("the-text-for-A"));
|
||||
expect(generated, contains("the-text-for-B"));
|
||||
expect(generated, isNot(contains("the-text-for-C")));
|
||||
});
|
||||
|
||||
test('generic type allocations are considered used', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class M<T>{ get type => T; }
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
]);
|
||||
main() => print(map[new M<A>().type]);
|
||||
""");
|
||||
expect(generated, contains("the-text-for-A"));
|
||||
});
|
||||
|
||||
test('generics in type signatures are ignored', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
class M<T>{ get type => T; }
|
||||
_factory(M<B> t) => t;
|
||||
const map = const LookupMap(const [
|
||||
A, const ["the-text-for-A", _factory],
|
||||
B, "the-text-for-B",
|
||||
]);
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
|
||||
test('metadata is ignored', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{ const A(); }
|
||||
|
||||
@A()
|
||||
class M {}
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
]);
|
||||
main() => print(map[M]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-A")));
|
||||
});
|
||||
|
||||
test('shared constants used in metadata are ignored', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
const annot = const B(foo: A);
|
||||
|
||||
@B(foo: annot)
|
||||
class A{ const A(); }
|
||||
class B{ final Type foo; const B({this.foo}); }
|
||||
|
||||
class M {}
|
||||
const map = const LookupMap(const [
|
||||
A, const ["the-text-for-A", annot]
|
||||
]);
|
||||
main() => print(map[M]);
|
||||
""");
|
||||
expect(generated, isNot(contains("the-text-for-A")));
|
||||
});
|
||||
|
||||
// regression test for a failure when looking up `dynamic` in a generic.
|
||||
test('do not choke on dynamic types', () async {
|
||||
await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class M<T>{ get type => T; }
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
]);
|
||||
main() => print(map[new M<dynamic>().type]);
|
||||
""");
|
||||
});
|
||||
|
||||
|
||||
test('support subclassing LookupMap', () async {
|
||||
String generated = await compileAll(r"""
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
class S extends LookupMap {
|
||||
const S(list) : super(list);
|
||||
}
|
||||
const map = const S(const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
]);
|
||||
|
||||
main() => print(map[A]);
|
||||
""");
|
||||
expect(generated, contains("the-text-for-A"));
|
||||
expect(generated, isNot(contains("the-text-for-B")));
|
||||
});
|
||||
}
|
|
@ -134,6 +134,8 @@ class MockCompiler extends Compiler {
|
|||
}
|
||||
registerSource(Uris.dart_async,
|
||||
buildLibrarySource(asyncLibrarySource));
|
||||
registerSource(JavaScriptBackend.PACKAGE_LOOKUP_MAP,
|
||||
buildLibrarySource(DEFAULT_LOOKUP_MAP_LIBRARY));
|
||||
}
|
||||
|
||||
String get patchVersion {
|
||||
|
|
|
@ -398,3 +398,19 @@ const Map<String, String> DEFAULT_MIRRORS_LIBRARY = const <String, String>{
|
|||
'MirrorSystem': 'class MirrorSystem {}',
|
||||
'MirrorsUsed': 'class MirrorsUsed {}',
|
||||
};
|
||||
|
||||
const Map<String, String> DEFAULT_LOOKUP_MAP_LIBRARY = const <String, String>{
|
||||
'LookupMap': r'''
|
||||
class LookupMap<T> {
|
||||
final _key;
|
||||
final _value;
|
||||
final _entries;
|
||||
final _nestedMaps;
|
||||
|
||||
const LookupMap(this._entries, [this._nestedMaps = const []])
|
||||
: _key = null, _value = null;
|
||||
|
||||
const LookupMap.pair(this._key, this._value)
|
||||
: _entries = const [], _nestedMaps = const [];
|
||||
}''',
|
||||
};
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const map = const LookupMap(const [], const [
|
||||
const LookupMap.pair(A, "the-text-for-A"),
|
||||
const LookupMap.pair(B, "the-text-for-B"),
|
||||
]);
|
||||
|
||||
main() {
|
||||
Expect.equals(map[A], "the-text-for-A");
|
||||
}
|
16
tests/compiler/dart2js_extra/lookup_map/dead_entry_test.dart
Normal file
16
tests/compiler/dart2js_extra/lookup_map/dead_entry_test.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
]);
|
||||
|
||||
main() {
|
||||
Expect.equals(map[A], "the-text-for-A");
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class A{ A(B x);}
|
||||
class B{}
|
||||
class C{}
|
||||
class D{}
|
||||
class E{}
|
||||
createA() => new A(map[B][1]());
|
||||
createB() => new B();
|
||||
const map = const LookupMap(const [
|
||||
A, const ["the-text-for-A", createA],
|
||||
B, const ["the-text-for-B", createB],
|
||||
C, const ["the-text-for-C"],
|
||||
]);
|
||||
|
||||
main() {
|
||||
Expect.isTrue(map[A][1]() is A);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class A{}
|
||||
class B{}
|
||||
const entries = const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
];
|
||||
const map = const LookupMap(entries );
|
||||
|
||||
main() {
|
||||
Expect.equals(map[A], 'the-text-for-A');
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
const entries = const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
];
|
||||
const map = const LookupMap(entries);
|
||||
main() {
|
||||
entries.forEach(print);
|
||||
Expect.equals(map[A], 'the-text-for-A');
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class A{}
|
||||
class M<T>{ get type => T; }
|
||||
const map = const LookupMap(const [
|
||||
A, 'the-text-for-A',
|
||||
]);
|
||||
|
||||
main() {
|
||||
Expect.equals(map[new M<A>().type], 'the-text-for-A');
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
class A{}
|
||||
const map = const LookupMap.pair(A, "the-text-for-A");
|
||||
main() {
|
||||
Expect.equals(map[A], 'the-text-for-A');
|
||||
}
|
13
tests/compiler/dart2js_extra/lookup_map/live_entry_test.dart
Normal file
13
tests/compiler/dart2js_extra/lookup_map/live_entry_test.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
class A{}
|
||||
const map = const LookupMap(const [
|
||||
A, "the-text-for-A",
|
||||
]);
|
||||
main() {
|
||||
Expect.equals(map[A], 'the-text-for-A');
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
class A{}
|
||||
class B{}
|
||||
class C{}
|
||||
class D{}
|
||||
class E{}
|
||||
const map = const LookupMap(const [
|
||||
A, const ["the-text-for-A", B],
|
||||
B, const ["the-text-for-B", C],
|
||||
C, const ["the-text-for-C"],
|
||||
D, const ["the-text-for-D", E],
|
||||
E, const ["the-text-for-E"],
|
||||
]);
|
||||
main() {
|
||||
Expect.equals(map[map[A][1]][0], 'the-text-for-B');
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
import 'package:lookup_map/lookup_map.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
class A{}
|
||||
class B{}
|
||||
class S extends LookupMap {
|
||||
const S(list) : super(list);
|
||||
}
|
||||
const map = const S(const [
|
||||
A, "the-text-for-A",
|
||||
B, "the-text-for-B",
|
||||
]);
|
||||
|
||||
main() {
|
||||
Expect.equals(map[A], "the-text-for-A");
|
||||
}
|
Loading…
Reference in a new issue