Skip to content

Commit

Permalink
Run async functions immediately.
Browse files Browse the repository at this point in the history
Migrated from https://codereview.chromium.org/2478703003/

Change-Id: I1d678c01ba5876490b12c676c500171328361d31
Reviewed-on: https://dart-review.googlesource.com/5263
Commit-Queue: Florian Loitsch <floitsch@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Vijay Menon <vsm@google.com>
Reviewed-by: William Hesse <whesse@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
  • Loading branch information
floitschG authored and commit-bot@chromium.org committed Feb 1, 2018
1 parent a421350 commit 67bac0b
Show file tree
Hide file tree
Showing 36 changed files with 699 additions and 117 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
This allows libraries with no library declarations (and therefore no name)
to have parts, and it allows tools to easily find the library of a part
file.
* Added support for starting `async` functions synchronously. All tools (VM,
dart2js, DDC) have now a flag `--sync-async` to enable this behavior.
Currently this behavior is opt-in. It will become the default.

#### Strong Mode

Expand Down
5 changes: 5 additions & 0 deletions pkg/compiler/lib/src/commandline_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ class Flags {
// https://gist.github.com/eernstg/4353d7b4f669745bed3a5423e04a453c.
static const String genericMethodSyntax = '--generic-method-syntax';

// Starts `async` functions synchronously.
//
// This is the Dart 2.0 behavior. This flag is only used during the migration.
static const String syncAsync = '--sync-async';

// Initializing-formal access is enabled by default and cannot be disabled.
// For backward compatibility the option is still accepted, but it is ignored.
static const String initializingFormalAccess = '--initializing-formal-access';
Expand Down
8 changes: 8 additions & 0 deletions pkg/compiler/lib/src/common_elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,8 @@ class CommonElements {

FunctionEntity get asyncHelperStart =>
_findAsyncHelperFunction("_asyncStart");
FunctionEntity get asyncHelperStartSync =>
_findAsyncHelperFunction("_asyncStartSync");
FunctionEntity get asyncHelperAwait =>
_findAsyncHelperFunction("_asyncAwait");
FunctionEntity get asyncHelperReturn =>
Expand Down Expand Up @@ -550,6 +552,12 @@ class CommonElements {
ConstructorEntity get syncCompleterConstructor =>
_env.lookupConstructor(_findAsyncHelperClass("Completer"), "sync");

ConstructorEntity get asyncAwaitCompleterConstructor =>
_env.lookupConstructor(asyncAwaitCompleter, "");

ClassEntity get asyncAwaitCompleter =>
_findAsyncHelperClass("_AsyncAwaitCompleter");

ClassEntity get asyncStarController =>
_findAsyncHelperClass("_AsyncStarStreamController");

Expand Down
1 change: 1 addition & 0 deletions pkg/compiler/lib/src/dart2js.dart
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ Future<api.CompilationResult> compile(List<String> argv,
new OptionHandler(Flags.allowMockCompilation, passThrough),
new OptionHandler(Flags.fastStartup, passThrough),
new OptionHandler(Flags.genericMethodSyntax, ignoreOption),
new OptionHandler(Flags.syncAsync, passThrough),
new OptionHandler(Flags.initializingFormalAccess, ignoreOption),
new OptionHandler('${Flags.minify}|-m', implyCompilation),
new OptionHandler(Flags.preserveUris, passThrough),
Expand Down
24 changes: 17 additions & 7 deletions pkg/compiler/lib/src/js_backend/backend.dart
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,8 @@ class JavaScriptBackend {
NativeBasicData nativeBasicData = compiler.frontendStrategy.nativeBasicData;
RuntimeTypesNeedBuilder rtiNeedBuilder =
compiler.frontendStrategy.createRuntimeTypesNeedBuilder();
BackendImpacts impacts = new BackendImpacts(commonElements);
BackendImpacts impacts =
new BackendImpacts(compiler.options, commonElements);
TypeVariableResolutionAnalysis typeVariableResolutionAnalysis =
new TypeVariableResolutionAnalysis(
compiler.frontendStrategy.elementEnvironment,
Expand Down Expand Up @@ -783,7 +784,8 @@ class JavaScriptBackend {
CompilerTask task, Compiler compiler, ClosedWorld closedWorld) {
ElementEnvironment elementEnvironment = closedWorld.elementEnvironment;
CommonElements commonElements = closedWorld.commonElements;
BackendImpacts impacts = new BackendImpacts(commonElements);
BackendImpacts impacts =
new BackendImpacts(compiler.options, commonElements);
_typeVariableCodegenAnalysis = new TypeVariableCodegenAnalysis(
closedWorld.elementEnvironment, this, commonElements, mirrorsData);
_mirrorsCodegenAnalysis = mirrorsResolutionAnalysis.close();
Expand Down Expand Up @@ -981,7 +983,8 @@ class JavaScriptBackend {
emitter.createEmitter(namer, closedWorld, codegenWorldBuilder, sorter);
// TODO(johnniwinther): Share the impact object created in
// createCodegenEnqueuer.
BackendImpacts impacts = new BackendImpacts(closedWorld.commonElements);
BackendImpacts impacts =
new BackendImpacts(compiler.options, closedWorld.commonElements);
if (compiler.options.disableRtiOptimization) {
_rtiSubstitutions = new TrivialRuntimeTypesSubstitutions(
closedWorld.elementEnvironment, closedWorld.dartTypes);
Expand Down Expand Up @@ -1167,22 +1170,29 @@ class JavaScriptBackend {
jsAst.Expression code,
SourceInformation bodySourceInformation,
SourceInformation exitSourceInformation) {
bool startAsyncSynchronously = compiler.options.startAsyncSynchronously;

AsyncRewriterBase rewriter = null;
jsAst.Name name = namer.methodPropertyName(element);
switch (element.asyncMarker) {
case AsyncMarker.ASYNC:
var startFunction = startAsyncSynchronously
? commonElements.asyncHelperStartSync
: commonElements.asyncHelperStart;
var completerConstructor = startAsyncSynchronously
? commonElements.asyncAwaitCompleterConstructor
: commonElements.syncCompleterConstructor;
rewriter = new AsyncRewriter(reporter, element,
asyncStart:
emitter.staticFunctionAccess(commonElements.asyncHelperStart),
asyncStart: emitter.staticFunctionAccess(startFunction),
asyncAwait:
emitter.staticFunctionAccess(commonElements.asyncHelperAwait),
asyncReturn:
emitter.staticFunctionAccess(commonElements.asyncHelperReturn),
asyncRethrow:
emitter.staticFunctionAccess(commonElements.asyncHelperRethrow),
wrapBody: emitter.staticFunctionAccess(commonElements.wrapBody),
completerFactory: emitter
.staticFunctionAccess(commonElements.syncCompleterConstructor),
completerFactory:
emitter.staticFunctionAccess(completerConstructor),
safeVariableName: namer.safeVariablePrefixForAsyncRewrite,
bodyName: namer.deriveAsyncBodyName(name));
break;
Expand Down
21 changes: 16 additions & 5 deletions pkg/compiler/lib/src/js_backend/backend_impact.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import '../common/names.dart';
import '../common_elements.dart' show CommonElements, ElementEnvironment;
import '../elements/types.dart' show InterfaceType;
import '../elements/entities.dart';
import '../options.dart' show CompilerOptions;
import '../universe/selector.dart';
import '../universe/world_impact.dart'
show WorldImpact, WorldImpactBuilder, WorldImpactBuilderImpl;
Expand Down Expand Up @@ -88,9 +89,10 @@ class BackendImpact {

/// The JavaScript backend dependencies for various features.
class BackendImpacts {
final CompilerOptions _options;
final CommonElements _commonElements;

BackendImpacts(this._commonElements);
BackendImpacts(this._options, this._commonElements);

BackendImpact _getRuntimeTypeArgument;

Expand Down Expand Up @@ -126,15 +128,24 @@ class BackendImpacts {
BackendImpact _asyncBody;

BackendImpact get asyncBody {
return _asyncBody ??= new BackendImpact(staticUses: [
_commonElements.asyncHelperStart,
var staticUses = [
_commonElements.asyncHelperAwait,
_commonElements.asyncHelperReturn,
_commonElements.asyncHelperRethrow,
_commonElements.syncCompleterConstructor,
_commonElements.streamIteratorConstructor,
_commonElements.wrapBody
]);
];
var instantiantedClasses = <ClassEntity>[];
if (_options.startAsyncSynchronously) {
staticUses.add(_commonElements.asyncAwaitCompleterConstructor);
staticUses.add(_commonElements.asyncHelperStartSync);
instantiantedClasses.add(_commonElements.asyncAwaitCompleter);
} else {
staticUses.add(_commonElements.syncCompleterConstructor);
staticUses.add(_commonElements.asyncHelperStart);
}
return _asyncBody ??= new BackendImpact(
staticUses: staticUses, instantiatedClasses: instantiantedClasses);
}

BackendImpact _syncStarBody;
Expand Down
7 changes: 7 additions & 0 deletions pkg/compiler/lib/src/options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,9 @@ class CompilerOptions implements DiagnosticOptions {
/// Strip option used by dart2dart.
final List<String> strips;

/// Whether to start `async` functions synchronously.
final bool startAsyncSynchronously;

/// Create an options object by parsing flags from [options].
factory CompilerOptions.parse(
{Uri entryPoint,
Expand Down Expand Up @@ -356,6 +359,7 @@ class CompilerOptions implements DiagnosticOptions {
useMultiSourceInfo: _hasOption(options, Flags.useMultiSourceInfo),
useNewSourceInfo: _hasOption(options, Flags.useNewSourceInfo),
useStartupEmitter: _hasOption(options, Flags.fastStartup),
startAsyncSynchronously: _hasOption(options, Flags.syncAsync),
verbose: _hasOption(options, Flags.verbose));
}

Expand Down Expand Up @@ -421,6 +425,7 @@ class CompilerOptions implements DiagnosticOptions {
bool useMultiSourceInfo: false,
bool useNewSourceInfo: false,
bool useStartupEmitter: false,
bool startAsyncSynchronously: false,
bool verbose: false}) {
// TODO(sigmund): should entrypoint be here? should we validate it is not
// null? In unittests we use the same compiler to analyze or build multiple
Expand Down Expand Up @@ -504,6 +509,7 @@ class CompilerOptions implements DiagnosticOptions {
useMultiSourceInfo: useMultiSourceInfo,
useNewSourceInfo: useNewSourceInfo,
useStartupEmitter: useStartupEmitter,
startAsyncSynchronously: startAsyncSynchronously,
verbose: verbose);
}

Expand Down Expand Up @@ -559,6 +565,7 @@ class CompilerOptions implements DiagnosticOptions {
this.useMultiSourceInfo: false,
this.useNewSourceInfo: false,
this.useStartupEmitter: false,
this.startAsyncSynchronously: false,
this.verbose: false})
: _shownPackageWarnings = shownPackageWarnings;

Expand Down
7 changes: 7 additions & 0 deletions pkg/dev_compiler/tool/ddc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ if [ "$1" = "-k" ]; then
shift
fi

SYNC_ASYNC=false
if [ "$1" = "--sync-async" ]; then
SYNC_ASYNC=true
shift
fi

BASENAME=$( basename "${1%.*}")
LIBROOT=$(cd $( dirname "${1%.*}") && pwd)

Expand Down Expand Up @@ -104,6 +110,7 @@ echo "
let sdk = require(\"dart_sdk\");
let main = require(\"./$BASENAME\").$BASENAME.main;
sdk.dart.ignoreWhitelistedErrors(false);
if ($SYNC_ASYNC) sdk.dart.setStartAsyncSynchronously();
try {
sdk._isolate_helper.startRootIsolate(main, []);
} catch(e) {
Expand Down
19 changes: 16 additions & 3 deletions pkg/dev_compiler/tool/input_sdk/patch/async_patch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import 'dart:_isolate_helper'

import 'dart:_foreign_helper' show JS, JSExportName;

import 'dart:_runtime' as dart;

typedef void _Callback();
typedef void _TakeCallback(_Callback callback);

Expand Down Expand Up @@ -67,7 +69,7 @@ async_<T>(Function() initGenerator) {
onError = zone.registerUnaryCallback(onError);
}
var asyncFuture = new _Future<T>();
scheduleMicrotask(() {
var body = () {
try {
iter = JS('', '#[Symbol.iterator]()', initGenerator());
var iteratorValue = JS('', '#.next(null)', iter);
Expand Down Expand Up @@ -97,9 +99,20 @@ async_<T>(Function() initGenerator) {
_Future._chainCoreFuture(onAwait(value), asyncFuture);
}
} catch (e, s) {
_completeWithErrorCallback(asyncFuture, e, s);
if (dart.startAsyncSynchronously) {
scheduleMicrotask(() {
_completeWithErrorCallback(asyncFuture, e, s);
});
} else {
_completeWithErrorCallback(asyncFuture, e, s);
}
}
});
};
if (dart.startAsyncSynchronously) {
body();
} else {
scheduleMicrotask(body);
}
return asyncFuture;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
/// stepping stone for proposed ES7 async/await, and uses ES6 Promises.
part of dart._runtime;

// TODO(vsm): Remove once this flag is the default.
bool startAsyncSynchronously = false;
void setStartAsyncSynchronously() {
startAsyncSynchronously = true;
}

final _jsIterator = JS('', 'Symbol("_jsIterator")');
final _current = JS('', 'Symbol("_current")');

Expand Down
6 changes: 5 additions & 1 deletion pkg/front_end/tool/_fasta/command_line.dart
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,13 @@ ProcessedOptions analyzeCommandLine(
final bool strongMode =
options.containsKey("--strong-mode") || options.containsKey("--strong");

final bool syncAsync = options.containsKey("--sync-async");

final String targetName = options["-t"] ?? options["--target"] ?? "vm";

final TargetFlags flags = new TargetFlags(strongMode: strongMode);
final TargetFlags flags =
new TargetFlags(strongMode: strongMode, syncAsync: syncAsync);

final Target target = getTarget(targetName, flags);
if (target == null) {
return throw new CommandLineProblem.deprecated(
Expand Down
8 changes: 6 additions & 2 deletions pkg/kernel/bin/transform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ ArgParser parser = new ArgParser()
..addOption('transformation',
abbr: 't',
help: 'The transformation to apply.',
defaultsTo: 'continuation');
defaultsTo: 'continuation')
..addFlag('sync-async',
help: 'Whether `async` functions start synchronously.',
defaultsTo: false);

main(List<String> arguments) async {
if (arguments.isNotEmpty && arguments[0] == '--batch') {
Expand All @@ -69,6 +72,7 @@ Future<CompilerOutcome> runTransformation(List<String> arguments) async {
var output = options['out'];
var format = options['format'];
var verbose = options['verbose'];
var syncAsync = options['sync-async'];

if (output == null) {
output = '${input.substring(0, input.lastIndexOf('.'))}.transformed.dill';
Expand All @@ -85,7 +89,7 @@ Future<CompilerOutcome> runTransformation(List<String> arguments) async {
final hierarchy = new ClassHierarchy(program);
switch (options['transformation']) {
case 'continuation':
program = cont.transformProgram(coreTypes, program);
program = cont.transformProgram(coreTypes, program, syncAsync);
break;
case 'resolve-mixins':
mix.transformLibraries(
Expand Down
12 changes: 12 additions & 0 deletions pkg/kernel/lib/core_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ class CoreTypes {
Class _stackTraceClass;
Class _streamClass;
Class _completerClass;
Class _asyncAwaitCompleterClass;
Class _futureOrClass;
Constructor _asyncAwaitCompleterConstructor;
Procedure _completerSyncConstructor;
Procedure _completerComplete;
Procedure _completerCompleteError;
Expand Down Expand Up @@ -162,11 +164,21 @@ class CoreTypes {
return _completerClass ??= _index.getClass('dart:async', 'Completer');
}

Class get asyncAwaitCompleterClass {
return _asyncAwaitCompleterClass ??=
_index.getClass('dart:async', '_AsyncAwaitCompleter');
}

Procedure get completerSyncConstructor {
return _completerSyncConstructor ??=
_index.getMember('dart:async', 'Completer', 'sync');
}

Constructor get asyncAwaitCompleterConstructor {
return _asyncAwaitCompleterConstructor ??=
_index.getMember('dart:async', '_AsyncAwaitCompleter', '');
}

Procedure get completerComplete {
return _completerComplete ??=
_index.getMember('dart:async', 'Completer', 'complete');
Expand Down
14 changes: 9 additions & 5 deletions pkg/kernel/lib/target/targets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ import 'vmreify.dart' show VmGenericTypesReifiedTarget;
final List<String> targetNames = targets.keys.toList();

class TargetFlags {
bool strongMode;
bool treeShake;
List<ProgramRoot> programRoots;
Uri kernelRuntime;
final bool strongMode;
final bool treeShake;

/// Whether `async` functions start synchronously.
final bool syncAsync;
final List<ProgramRoot> programRoots;
final Uri kernelRuntime;

TargetFlags(
{this.strongMode: false,
this.treeShake: false,
this.syncAsync: false,
this.programRoots: const <ProgramRoot>[],
this.kernelRuntime}) {}
this.kernelRuntime});
}

typedef Target _TargetBuilder(TargetFlags flags);
Expand Down
2 changes: 1 addition & 1 deletion pkg/kernel/lib/target/vm.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class VmTarget extends Target {
logger?.call("Transformed mixin applications");

// TODO(kmillikin): Make this run on a per-method basis.
transformAsync.transformLibraries(coreTypes, libraries);
transformAsync.transformLibraries(coreTypes, libraries, flags.syncAsync);
logger?.call("Transformed async methods");
}

Expand Down
Loading

0 comments on commit 67bac0b

Please sign in to comment.