[kernel] Partial support for conditional imports in CFE.

In order to un-block Flutter integration, we introduce partial support for
conditional imports into the CFE. There are two incomplete parts:

- The condition strings are evaluated by the Target.
  They should be looked up in the environment, but this introduces
  complications with modular compilation.

- Type inference and other static checks are performed with reference to
  the implementation (conditionally-imported) library rather than the
  interface library.

See issue #30143 for more details.

Bug:
Change-Id: I740b45e9d32796644837de4caefd8d6e8015f229
Reviewed-on: https://dart-review.googlesource.com/34721
Reviewed-by: Peter von der Ahé <ahe@google.com>
This commit is contained in:
Samir Jindel 2018-01-16 17:27:19 +00:00
parent b79696982d
commit a2ff93da67
6 changed files with 78 additions and 10 deletions

View file

@ -0,0 +1,14 @@
// Copyright (c) 2018, 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 fasta.configuration;
class Configuration {
final int charOffset;
final String dottedName;
final String condition;
final String importUri;
Configuration(
this.charOffset, this.dottedName, this.condition, this.importUri);
}

View file

@ -12,6 +12,8 @@ import 'kernel/kernel_builder.dart' show toKernelCombinators;
import 'combinator.dart' show Combinator;
import 'configuration.dart' show Configuration;
typedef void AddToScope(String name, Builder member);
class Import {
@ -29,12 +31,21 @@ class Import {
final List<Combinator> combinators;
final List<Configuration> configurations;
final int charOffset;
final int prefixCharOffset;
Import(this.importer, this.imported, this.deferred, this.prefix,
this.combinators, this.charOffset, this.prefixCharOffset)
Import(
this.importer,
this.imported,
this.deferred,
this.prefix,
this.combinators,
this.configurations,
this.charOffset,
this.prefixCharOffset)
: prefixBuilder = createPrefixBuilder(prefix, importer, imported,
combinators, deferred, charOffset, prefixCharOffset);

View file

@ -44,7 +44,9 @@ import '../quote.dart' show unescapeString;
import 'source_library_builder.dart' show SourceLibraryBuilder;
import 'unhandled_listener.dart' show NullValue, Unhandled, UnhandledListener;
import 'unhandled_listener.dart' show NullValue, UnhandledListener;
import '../configuration.dart' show Configuration;
enum MethodBody {
Abstract,
@ -143,7 +145,7 @@ class OutlineBuilder extends UnhandledListener {
void endExport(Token exportKeyword, Token semicolon) {
debugEvent("Export");
List<Combinator> combinators = pop();
Unhandled conditionalUris = pop();
List<Configuration> conditionalUris = pop();
int uriOffset = popCharOffset();
String uri = pop();
List<MetadataBuilder> metadata = pop();
@ -171,15 +173,38 @@ class OutlineBuilder extends UnhandledListener {
bool isDeferred = pop();
int prefixOffset = pop();
String prefix = pop(NullValue.Prefix);
Unhandled conditionalUris = pop();
List<Configuration> configurations = pop();
int uriOffset = popCharOffset();
String uri = pop();
String uri = pop(); // For a conditional import, this is the default URI.
List<MetadataBuilder> metadata = pop();
library.addImport(metadata, uri, conditionalUris, prefix, combinators,
library.addImport(metadata, uri, configurations, prefix, combinators,
isDeferred, importKeyword.charOffset, prefixOffset, uriOffset);
checkEmpty(importKeyword.charOffset);
}
@override
void endConditionalUris(int count) {
debugEvent("EndConditionalUris");
push(popList(count) ?? NullValue.ConditionalUris);
}
@override
void endConditionalUri(Token ifKeyword, Token leftParen, Token equalSign) {
debugEvent("EndConditionalUri");
int charOffset = popCharOffset();
String uri = pop();
if (equalSign != null) popCharOffset();
String condition = popIfNotNull(equalSign) ?? "true";
String dottedName = pop();
push(new Configuration(charOffset, dottedName, condition, uri));
}
@override
void handleDottedName(int count, Token firstIdentifier) {
debugEvent("DottedName");
push(popIdentifierList(count).join('.'));
}
@override
void handleRecoverImport(Token semicolon) {
debugEvent("RecoverImport");

View file

@ -30,7 +30,6 @@ import '../builder/builder.dart'
TypeBuilder,
TypeDeclarationBuilder,
TypeVariableBuilder,
Unhandled,
UnresolvedType;
import '../combinator.dart' show Combinator;
@ -58,6 +57,8 @@ import '../fasta_codes.dart'
import '../import.dart' show Import;
import '../configuration.dart' show Configuration;
import '../problems.dart' show unhandled;
import 'source_loader.dart' show SourceLoader;
@ -191,7 +192,7 @@ abstract class SourceLibraryBuilder<T extends TypeBuilder, R>
void addExport(
List<MetadataBuilder> metadata,
String uri,
Unhandled conditionalUris,
List<Configuration> conditionalUris,
List<Combinator> combinators,
int charOffset,
int uriOffset) {
@ -204,13 +205,24 @@ abstract class SourceLibraryBuilder<T extends TypeBuilder, R>
void addImport(
List<MetadataBuilder> metadata,
String uri,
Unhandled conditionalUris,
List<Configuration> configurations,
String prefix,
List<Combinator> combinators,
bool deferred,
int charOffset,
int prefixCharOffset,
int uriOffset) {
if (configurations != null) {
for (Configuration config in configurations) {
if (loader.target.backendTarget
.lookupImportCondition(config.dottedName) ==
config.condition) {
uri = config.importUri;
break;
}
}
}
imports.add(new Import(
this,
loader.read(resolve(this.uri, uri, uriOffset), charOffset,
@ -218,6 +230,7 @@ abstract class SourceLibraryBuilder<T extends TypeBuilder, R>
deferred,
prefix,
combinators,
configurations,
charOffset,
prefixCharOffset));
}

View file

@ -34,6 +34,7 @@ enum NullValue {
Combinators,
Comments,
ConditionalUris,
ConditionallySelectedImport,
ConstructorInitializerSeparator,
ConstructorInitializers,
ConstructorReferenceContinuationAfterTypeArguments,

View file

@ -152,6 +152,10 @@ abstract class Target {
/// extensions, but targets like dart2js only enable it on the core libraries.
bool enableNative(Uri uri) => false;
/// Returns whether the given condition Uri should evaluate to true for the
/// purposes of resolving conditional imports.
String lookupImportCondition(String condition) => "";
/// There are two variants of the `native` language extension. The VM expects
/// the native token to be followed by string, whereas dart2js and DDC do not.
// TODO(sigmund, ahe): ideally we should remove the `native` syntax, if not,