Reapply "Run async functions immediately."

Which was reverted in commit 032be73dbe.
Originally commited in commit 67bac0bce6.
Original review URL: https://dart-review.googlesource.com/5263

Change-Id: Ic7333c29e502a3924dc6aade8ffa46fc8aa5b04a
Reviewed-on: https://dart-review.googlesource.com/38120
Commit-Queue: Florian Loitsch <floitsch@google.com>
Reviewed-by: William Hesse <whesse@google.com>
This commit is contained in:
Florian Loitsch 2018-02-02 15:50:16 +00:00 committed by commit-bot@chromium.org
parent 7bd76a466a
commit 3b8e4d41a7
40 changed files with 710 additions and 117 deletions

View file

@ -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

View file

@ -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';

View file

@ -508,6 +508,8 @@ class CommonElements {
FunctionEntity get asyncHelperStart =>
_findAsyncHelperFunction("_asyncStart");
FunctionEntity get asyncHelperStartSync =>
_findAsyncHelperFunction("_asyncStartSync");
FunctionEntity get asyncHelperAwait =>
_findAsyncHelperFunction("_asyncAwait");
FunctionEntity get asyncHelperReturn =>
@ -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");

View file

@ -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),

View file

@ -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,
@ -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();
@ -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);
@ -1167,13 +1170,20 @@ 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:
@ -1181,8 +1191,8 @@ class JavaScriptBackend {
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;

View file

@ -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;
@ -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;
@ -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;

View file

@ -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,
@ -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));
}
@ -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
@ -504,6 +509,7 @@ class CompilerOptions implements DiagnosticOptions {
useMultiSourceInfo: useMultiSourceInfo,
useNewSourceInfo: useNewSourceInfo,
useStartupEmitter: useStartupEmitter,
startAsyncSynchronously: startAsyncSynchronously,
verbose: verbose);
}
@ -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;

View file

@ -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)
@ -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) {

View file

@ -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);
@ -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);
@ -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;
}

View file

@ -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")');

View file

@ -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(

View file

@ -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') {
@ -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';
@ -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(

View file

@ -79,7 +79,9 @@ class CoreTypes {
Class _stackTraceClass;
Class _streamClass;
Class _completerClass;
Class _asyncAwaitCompleterClass;
Class _futureOrClass;
Constructor _asyncAwaitCompleterConstructor;
Procedure _completerSyncConstructor;
Procedure _completerComplete;
Procedure _completerCompleteError;
@ -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');

View file

@ -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);

View file

@ -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");
}

View file

@ -44,7 +44,7 @@ class VmClosureConvertedTarget extends vm_target.VmTarget {
performTreeShaking(coreTypes, program);
}
cont.transformProgram(coreTypes, program);
cont.transformProgram(coreTypes, program, flags.syncAsync);
new SanitizeForVM().transform(program);

View file

@ -484,8 +484,8 @@ class ExpressionLifter extends Transformer {
}
visitFunctionNode(FunctionNode node) {
var nestedRewriter =
new RecursiveContinuationRewriter(continuationRewriter.helper);
var nestedRewriter = new RecursiveContinuationRewriter(
continuationRewriter.helper, continuationRewriter.syncAsync);
return node.accept(nestedRewriter);
}
}

View file

@ -12,29 +12,34 @@ import '../visitor.dart';
import 'async.dart';
void transformLibraries(CoreTypes coreTypes, List<Library> libraries) {
void transformLibraries(
CoreTypes coreTypes, List<Library> libraries, bool syncAsync) {
var helper = new HelperNodes.fromCoreTypes(coreTypes);
var rewriter = new RecursiveContinuationRewriter(helper);
var rewriter = new RecursiveContinuationRewriter(helper, syncAsync);
for (var library in libraries) {
rewriter.rewriteLibrary(library);
}
}
Program transformProgram(CoreTypes coreTypes, Program program) {
Program transformProgram(CoreTypes coreTypes, Program program, bool syncAsync) {
var helper = new HelperNodes.fromCoreTypes(coreTypes);
var rewriter = new RecursiveContinuationRewriter(helper);
var rewriter = new RecursiveContinuationRewriter(helper, syncAsync);
return rewriter.rewriteProgram(program);
}
class RecursiveContinuationRewriter extends Transformer {
final HelperNodes helper;
/// Whether `async` functions should start synchronously.
final bool syncAsync;
final VariableDeclaration asyncJumpVariable = new VariableDeclaration(
":await_jump_var",
initializer: new IntLiteral(0));
final VariableDeclaration asyncContextVariable =
new VariableDeclaration(":await_ctx_var");
RecursiveContinuationRewriter(this.helper);
RecursiveContinuationRewriter(this.helper, this.syncAsync);
Program rewriteProgram(Program node) {
return node.accept(this);
@ -52,14 +57,15 @@ class RecursiveContinuationRewriter extends Transformer {
switch (node.asyncMarker) {
case AsyncMarker.Sync:
case AsyncMarker.SyncYielding:
node.transformChildren(new RecursiveContinuationRewriter(helper));
node.transformChildren(
new RecursiveContinuationRewriter(helper, syncAsync));
return node;
case AsyncMarker.SyncStar:
return new SyncStarFunctionRewriter(helper, node).rewrite();
return new SyncStarFunctionRewriter(helper, node, syncAsync).rewrite();
case AsyncMarker.Async:
return new AsyncFunctionRewriter(helper, node).rewrite();
return new AsyncFunctionRewriter(helper, node, syncAsync).rewrite();
case AsyncMarker.AsyncStar:
return new AsyncStarFunctionRewriter(helper, node).rewrite();
return new AsyncStarFunctionRewriter(helper, node, syncAsync).rewrite();
}
}
}
@ -72,8 +78,9 @@ abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter {
int capturedTryDepth = 0; // Deepest yield point within a try-block.
int capturedCatchDepth = 0; // Deepest yield point within a catch-block.
ContinuationRewriterBase(HelperNodes helper, this.enclosingFunction)
: super(helper);
ContinuationRewriterBase(
HelperNodes helper, this.enclosingFunction, bool syncAsync)
: super(helper, syncAsync);
/// Given a container [type], which is an instantiation of the given
/// [containerClass] extract its element type.
@ -157,13 +164,14 @@ abstract class ContinuationRewriterBase extends RecursiveContinuationRewriter {
class SyncStarFunctionRewriter extends ContinuationRewriterBase {
final VariableDeclaration iteratorVariable;
SyncStarFunctionRewriter(HelperNodes helper, FunctionNode enclosingFunction)
SyncStarFunctionRewriter(
HelperNodes helper, FunctionNode enclosingFunction, syncAsync)
: iteratorVariable = new VariableDeclaration(':iterator')
..type = new InterfaceType(helper.syncIteratorClass, [
ContinuationRewriterBase.elementTypeFrom(
helper.iterableClass, enclosingFunction.returnType)
]),
super(helper, enclosingFunction);
super(helper, enclosingFunction, syncAsync);
FunctionNode rewrite() {
// :sync_op(:iterator) {
@ -256,8 +264,9 @@ abstract class AsyncRewriterBase extends ContinuationRewriterBase {
ExpressionLifter expressionRewriter;
AsyncRewriterBase(helper, enclosingFunction)
: super(helper, enclosingFunction) {}
AsyncRewriterBase(
HelperNodes helper, FunctionNode enclosingFunction, bool syncAsync)
: super(helper, enclosingFunction, syncAsync) {}
void setupAsyncContinuations(List<Statement> statements) {
expressionRewriter = new ExpressionLifter(this);
@ -754,8 +763,9 @@ abstract class AsyncRewriterBase extends ContinuationRewriterBase {
class AsyncStarFunctionRewriter extends AsyncRewriterBase {
VariableDeclaration controllerVariable;
AsyncStarFunctionRewriter(helper, enclosingFunction)
: super(helper, enclosingFunction);
AsyncStarFunctionRewriter(
HelperNodes helper, FunctionNode enclosingFunction, bool syncAsync)
: super(helper, enclosingFunction, syncAsync);
FunctionNode rewrite() {
var statements = <Statement>[];
@ -862,8 +872,9 @@ class AsyncFunctionRewriter extends AsyncRewriterBase {
VariableDeclaration completerVariable;
VariableDeclaration returnVariable;
AsyncFunctionRewriter(helper, enclosingFunction)
: super(helper, enclosingFunction);
AsyncFunctionRewriter(
HelperNodes helper, FunctionNode enclosingFunction, bool syncAsync)
: super(helper, enclosingFunction, syncAsync);
FunctionNode rewrite() {
var statements = <Statement>[];
@ -877,16 +888,29 @@ class AsyncFunctionRewriter extends AsyncRewriterBase {
final DartType returnType =
new InterfaceType(helper.futureOrClass, <DartType>[valueType]);
var completerTypeArguments = <DartType>[valueType];
var completerType =
new InterfaceType(helper.completerClass, completerTypeArguments);
// final Completer<T> :completer = new Completer<T>.sync();
completerVariable = new VariableDeclaration(":completer",
initializer: new StaticInvocation(helper.completerConstructor,
new Arguments([], types: completerTypeArguments))
..fileOffset = enclosingFunction.body?.fileOffset ?? -1,
isFinal: true,
type: completerType);
if (syncAsync) {
final completerType = new InterfaceType(
helper.asyncAwaitCompleterClass, completerTypeArguments);
// final Completer<T> :completer = new _AsyncAwaitCompleter<T>();
completerVariable = new VariableDeclaration(":completer",
initializer: new ConstructorInvocation(
helper.asyncAwaitCompleterConstructor,
new Arguments([], types: completerTypeArguments))
..fileOffset = enclosingFunction.body?.fileOffset ?? -1,
isFinal: true,
type: completerType);
} else {
final completerType =
new InterfaceType(helper.completerClass, completerTypeArguments);
// final Completer<T> :completer = new Completer<T>.sync();
completerVariable = new VariableDeclaration(":completer",
initializer: new StaticInvocation(helper.completerConstructor,
new Arguments([], types: completerTypeArguments))
..fileOffset = enclosingFunction.body?.fileOffset ?? -1,
isFinal: true,
type: completerType);
}
statements.add(completerVariable);
returnVariable = new VariableDeclaration(":return_value", type: returnType);
@ -894,14 +918,23 @@ class AsyncFunctionRewriter extends AsyncRewriterBase {
setupAsyncContinuations(statements);
// new Future.microtask(:async_op);
var newMicrotaskStatement = new ExpressionStatement(new StaticInvocation(
helper.futureMicrotaskConstructor,
new Arguments([new VariableGet(nestedClosureVariable)],
types: [const DynamicType()]))
..fileOffset = enclosingFunction.fileOffset);
statements.add(newMicrotaskStatement);
if (syncAsync) {
// :completer.start(:async_op);
var startStatement = new ExpressionStatement(new MethodInvocation(
new VariableGet(completerVariable),
new Name('start'),
new Arguments([new VariableGet(nestedClosureVariable)]))
..fileOffset = enclosingFunction.fileOffset);
statements.add(startStatement);
} else {
// new Future.microtask(:async_op);
var newMicrotaskStatement = new ExpressionStatement(new StaticInvocation(
helper.futureMicrotaskConstructor,
new Arguments([new VariableGet(nestedClosureVariable)],
types: [const DynamicType()]))
..fileOffset = enclosingFunction.fileOffset);
statements.add(newMicrotaskStatement);
}
// return :completer.future;
var completerGet = new VariableGet(completerVariable);
var returnStatement = new ReturnStatement(new PropertyGet(completerGet,
@ -964,9 +997,11 @@ class HelperNodes {
final Procedure asyncThenWrapper;
final Procedure awaitHelper;
final Class completerClass;
final Class asyncAwaitCompleterClass;
final Member completerComplete;
final Member completerCompleteError;
final Member completerConstructor;
final Member asyncAwaitCompleterConstructor;
final Member completerFuture;
final Library coreLibrary;
final CoreTypes coreTypes;
@ -1001,9 +1036,11 @@ class HelperNodes {
this.asyncThenWrapper,
this.awaitHelper,
this.completerClass,
this.asyncAwaitCompleterClass,
this.completerComplete,
this.completerCompleteError,
this.completerConstructor,
this.asyncAwaitCompleterConstructor,
this.completerFuture,
this.coreLibrary,
this.coreTypes,
@ -1039,9 +1076,11 @@ class HelperNodes {
coreTypes.asyncThenWrapperHelperProcedure,
coreTypes.awaitHelperProcedure,
coreTypes.completerClass,
coreTypes.asyncAwaitCompleterClass,
coreTypes.completerComplete,
coreTypes.completerCompleteError,
coreTypes.completerSyncConstructor,
coreTypes.asyncAwaitCompleterConstructor,
coreTypes.completerFuture,
coreTypes.coreLibrary,
coreTypes,

View file

@ -25,6 +25,7 @@ final ArgParser _argParser = new ArgParser(allowTrailingOptions: true)
'Produce kernel file for AOT compilation (enables global transformations).',
defaultsTo: false)
..addFlag('strong-mode', help: 'Enable strong mode', defaultsTo: true)
..addFlag('sync-async', help: 'Start `async` functions synchronously')
..addFlag('embed-sources',
help: 'Embed source files in the generated kernel program',
defaultsTo: true);
@ -62,12 +63,14 @@ Future<int> compile(List<String> arguments) async {
final String packages = options['packages'];
final bool strongMode = options['strong-mode'];
final bool aot = options['aot'];
final bool syncAsync = options['sync-async'];
ErrorDetector errorDetector = new ErrorDetector();
final CompilerOptions compilerOptions = new CompilerOptions()
..strongMode = strongMode
..target = new VmTarget(new TargetFlags(strongMode: strongMode))
..target = new VmTarget(
new TargetFlags(strongMode: strongMode, syncAsync: syncAsync))
..linkedDependencies = <Uri>[Uri.base.resolve(platformKernel)]
..packagesFileUri = packages != null ? Uri.base.resolve(packages) : null
..reportMessages = true

View file

@ -49,7 +49,9 @@ abstract class Compiler {
CompilerOptions options;
Compiler(this.fileSystem, Uri platformKernelPath,
{this.strongMode: false, bool suppressWarnings: false}) {
{this.strongMode: false,
bool suppressWarnings: false,
bool syncAsync: false}) {
Uri packagesUri = (Platform.packageConfig != null)
? Uri.parse(Platform.packageConfig)
: null;
@ -60,12 +62,14 @@ abstract class Compiler {
print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}");
print("DFE: platformKernelPath: ${platformKernelPath}");
print("DFE: strongMode: ${strongMode}");
print("DFE: syncAsync: ${syncAsync}");
}
options = new CompilerOptions()
..strongMode = strongMode
..fileSystem = fileSystem
..target = new VmTarget(new TargetFlags(strongMode: strongMode))
..target = new VmTarget(
new TargetFlags(strongMode: strongMode, syncAsync: syncAsync))
..packagesFileUri = packagesUri
..sdkSummary = platformKernelPath
..verbose = verbose
@ -100,9 +104,11 @@ class IncrementalCompiler extends Compiler {
IncrementalKernelGenerator generator;
IncrementalCompiler(FileSystem fileSystem, Uri platformKernelPath,
{bool strongMode: false, bool suppressWarnings: false})
{bool strongMode: false, bool suppressWarnings: false, syncAsync: false})
: super(fileSystem, platformKernelPath,
strongMode: strongMode, suppressWarnings: suppressWarnings);
strongMode: strongMode,
suppressWarnings: suppressWarnings,
syncAsync: syncAsync);
@override
Future<Program> compileInternal(Uri script) async {
@ -123,9 +129,12 @@ class SingleShotCompiler extends Compiler {
SingleShotCompiler(FileSystem fileSystem, Uri platformKernelPath,
{this.requireMain: false,
bool strongMode: false,
bool suppressWarnings: false})
bool suppressWarnings: false,
bool syncAsync: false})
: super(fileSystem, platformKernelPath,
strongMode: strongMode, suppressWarnings: suppressWarnings);
strongMode: strongMode,
suppressWarnings: suppressWarnings,
syncAsync: syncAsync);
@override
Future<Program> compileInternal(Uri script) async {
@ -139,7 +148,9 @@ final Map<int, Compiler> isolateCompilers = new Map<int, Compiler>();
Future<Compiler> lookupOrBuildNewIncrementalCompiler(int isolateId,
List sourceFiles, Uri platformKernelPath, List<int> platformKernel,
{bool strongMode: false, bool suppressWarnings: false}) async {
{bool strongMode: false,
bool suppressWarnings: false,
bool syncAsync: false}) async {
IncrementalCompiler compiler;
if (isolateCompilers.containsKey(isolateId)) {
compiler = isolateCompilers[isolateId];
@ -163,7 +174,9 @@ Future<Compiler> lookupOrBuildNewIncrementalCompiler(int isolateId,
// isolate needs to receive a message indicating that particular
// isolate was shut down. Message should be handled here in this script.
compiler = new IncrementalCompiler(fileSystem, platformKernelPath,
strongMode: strongMode, suppressWarnings: suppressWarnings);
strongMode: strongMode,
suppressWarnings: suppressWarnings,
syncAsync: syncAsync);
isolateCompilers[isolateId] = compiler;
}
return compiler;
@ -180,6 +193,7 @@ Future _processLoadRequest(request) async {
final int isolateId = request[6];
final List sourceFiles = request[7];
final bool suppressWarnings = request[8];
final bool syncAsync = request[9];
final Uri script = Uri.base.resolve(inputFileUri);
Uri platformKernelPath = null;
@ -202,7 +216,7 @@ Future _processLoadRequest(request) async {
if (incremental) {
compiler = await lookupOrBuildNewIncrementalCompiler(
isolateId, sourceFiles, platformKernelPath, platformKernel,
suppressWarnings: suppressWarnings);
suppressWarnings: suppressWarnings, syncAsync: syncAsync);
} else {
final FileSystem fileSystem = sourceFiles == null && platformKernel == null
? StandardFileSystem.instance
@ -210,7 +224,8 @@ Future _processLoadRequest(request) async {
compiler = new SingleShotCompiler(fileSystem, platformKernelPath,
requireMain: sourceFiles == null,
strongMode: strong,
suppressWarnings: suppressWarnings);
suppressWarnings: suppressWarnings,
syncAsync: syncAsync);
}
CompilationResult result;
@ -304,6 +319,7 @@ train(String scriptUri, String platformKernelPath) {
1 /* isolateId chosen randomly */,
null /* source files */,
false /* suppress warnings */,
false /* synchronous async */,
];
_processLoadRequest(request);
}

View file

@ -16,6 +16,7 @@ set -e
OPTIONS=()
PACKAGES=
SYNC_ASYNC=
ARGV=()
for arg in "$@"; do
@ -23,6 +24,9 @@ for arg in "$@"; do
--packages=*)
PACKAGES="$arg"
;;
--sync-async)
SYNC_ASYNC="--sync-async"
;;
--*)
OPTIONS+=("$arg")
;;
@ -72,6 +76,7 @@ BIN_DIR="$OUT_DIR/$DART_CONFIGURATION"
"${SDK_DIR}/pkg/vm/bin/gen_kernel.dart" \
--platform "${BIN_DIR}/vm_platform_strong.dill" \
--aot \
$SYNC_ASYNC \
$PACKAGES \
-o "$SNAPSHOT_FILE.dill" \
"$SOURCE_FILE"

View file

@ -17,6 +17,43 @@ import "dart:_internal" show VMLibraryHooks, patch;
// Equivalent of calling FATAL from C++ code.
_fatal(msg) native "DartAsync_fatal";
class _AsyncAwaitCompleter<T> implements Completer<T> {
final _completer = new Completer<T>.sync();
bool isSync;
_AsyncAwaitCompleter() : isSync = false;
void complete([FutureOr<T> value]) {
if (isSync) {
_completer.complete(value);
} else if (value is Future<T>) {
value.then(_completer.complete, onError: _completer.completeError);
} else {
scheduleMicrotask(() {
_completer.complete(value);
});
}
}
void completeError(e, [st]) {
if (isSync) {
_completer.completeError(e, st);
} else {
scheduleMicrotask(() {
_completer.completeError(e, st);
});
}
}
void start(f) {
f();
isSync = true;
}
Future<T> get future => _completer.future;
bool get isCompleted => _completer.isCompleted;
}
// We need to pass the value as first argument and leave the second and third
// arguments empty (used for error handling).
// See vm/ast_transformer.cc for usage.

View file

@ -157,6 +157,7 @@
C(stress_async_stacks, false, false, bool, false, \
"Stress test async stack traces") \
P(strong, bool, false, "Enable strong mode.") \
P(sync_async, bool, false, "Start `async` functions synchronously.") \
R(support_ast_printer, false, bool, true, "Support the AST printer.") \
R(support_compiler_stats, false, bool, true, "Support compiler stats.") \
R(support_disassembler, false, bool, true, "Support the disassembler.") \

View file

@ -386,6 +386,10 @@ class KernelCompilationRequest : public ValueObject {
suppress_warnings.type = Dart_CObject_kBool;
suppress_warnings.value.as_bool = FLAG_suppress_fe_warnings;
Dart_CObject dart_sync_async;
dart_sync_async.type = Dart_CObject_kBool;
dart_sync_async.value.as_bool = FLAG_sync_async;
Dart_CObject* message_arr[] = {&tag,
&send_port,
&uri,
@ -394,7 +398,8 @@ class KernelCompilationRequest : public ValueObject {
&dart_strong,
&isolate_id,
&files,
&suppress_warnings};
&suppress_warnings,
&dart_sync_async};
message.value.as_array.values = message_arr;
message.value.as_array.length = ARRAY_SIZE(message_arr);
// Send the message.

View file

@ -7574,30 +7574,42 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure,
Symbols::AsyncStackTraceVar(), false);
ASSERT((existing_var != NULL) && existing_var->is_captured());
// Create and return a new future that executes a closure with the current
// body.
// Create a completer that executes a closure with the current body and
// return the corresponding future.
// No need to capture parameters or other variables, since they have already
// been captured in the corresponding scope as the body has been parsed within
// a nested block (contained in the async function's block).
const Class& future = Class::ZoneHandle(Z, I->object_store()->future_class());
ASSERT(!future.IsNull());
const Function& constructor = Function::ZoneHandle(
Z, future.LookupFunction(Symbols::FutureMicrotask()));
ASSERT(!constructor.IsNull());
const Class& completer =
Class::ZoneHandle(Z, I->object_store()->completer_class());
ASSERT(!completer.IsNull());
const Function& completer_constructor = Function::ZoneHandle(
Z, completer.LookupFunction(Symbols::CompleterSyncConstructor()));
ASSERT(!completer_constructor.IsNull());
const Library& async_lib = Library::Handle(Z, Library::AsyncLibrary());
LocalVariable* async_completer =
current_block_->scope->LookupVariable(Symbols::AsyncCompleter(), false);
const TokenPosition token_pos = ST(closure_body->token_pos());
// Add to AST:
// :async_completer = new Completer.sync();
Function& completer_constructor = Function::ZoneHandle(Z);
if (FLAG_sync_async) {
const Class& completer_class = Class::Handle(
Z, async_lib.LookupClassAllowPrivate(Symbols::_AsyncAwaitCompleter()));
ASSERT(!completer_class.IsNull());
completer_constructor = completer_class.LookupConstructorAllowPrivate(
Symbols::_AsyncAwaitCompleterConstructor());
ASSERT(!completer_constructor.IsNull());
// Add to AST:
// :async_completer = new _AsyncAwaitCompleter();
} else {
const Class& completer =
Class::Handle(Z, I->object_store()->completer_class());
ASSERT(!completer.IsNull());
completer_constructor =
completer.LookupFunction(Symbols::CompleterSyncConstructor());
ASSERT(!completer_constructor.IsNull());
// Add to AST:
// :async_completer = new Completer.sync();
}
ArgumentListNode* empty_args = new (Z) ArgumentListNode(token_pos);
ConstructorCallNode* completer_constructor_node =
new (Z) ConstructorCallNode(token_pos, TypeArguments::ZoneHandle(Z),
@ -7624,8 +7636,6 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure,
new (Z) StoreLocalNode(token_pos, async_op_var, cn);
current_block_->statements->Add(store_async_op);
const Library& async_lib = Library::Handle(Library::AsyncLibrary());
if (FLAG_causal_async_stacks) {
// Add to AST:
// :async_stack_trace = _asyncStackTraceHelper();
@ -7689,13 +7699,29 @@ SequenceNode* Parser::CloseAsyncFunction(const Function& closure,
current_block_->statements->Add(store_async_catch_error_callback);
// Add to AST:
// new Future.microtask(:async_op);
ArgumentListNode* arguments = new (Z) ArgumentListNode(token_pos);
arguments->Add(new (Z) LoadLocalNode(token_pos, async_op_var));
ConstructorCallNode* future_node = new (Z) ConstructorCallNode(
token_pos, TypeArguments::ZoneHandle(Z), constructor, arguments);
current_block_->statements->Add(future_node);
if (FLAG_sync_async) {
// Add to AST:
// :async_completer.start(:async_op);
ArgumentListNode* arguments = new (Z) ArgumentListNode(token_pos);
arguments->Add(new (Z) LoadLocalNode(token_pos, async_op_var));
InstanceCallNode* start_call = new (Z) InstanceCallNode(
token_pos, new (Z) LoadLocalNode(token_pos, async_completer),
Symbols::_AsyncAwaitStart(), arguments);
current_block_->statements->Add(start_call);
} else {
// Add to AST:
// new Future.microtask(:async_op);
const Class& future = Class::Handle(Z, I->object_store()->future_class());
ASSERT(!future.IsNull());
const Function& constructor = Function::ZoneHandle(
Z, future.LookupFunction(Symbols::FutureMicrotask()));
ASSERT(!constructor.IsNull());
ArgumentListNode* arguments = new (Z) ArgumentListNode(token_pos);
arguments->Add(new (Z) LoadLocalNode(token_pos, async_op_var));
ConstructorCallNode* future_node = new (Z) ConstructorCallNode(
token_pos, TypeArguments::ZoneHandle(Z), constructor, arguments);
current_block_->statements->Add(future_node);
}
// Add to AST:
// return :async_completer.future;

View file

@ -145,6 +145,9 @@ class ObjectPointerVisitor;
V(CompleterComplete, "complete") \
V(CompleterCompleteError, "completeError") \
V(CompleterSyncConstructor, "Completer.sync") \
V(_AsyncAwaitCompleter, "_AsyncAwaitCompleter") \
V(_AsyncAwaitCompleterConstructor, "_AsyncAwaitCompleter.") \
V(_AsyncAwaitStart, "start") \
V(CompleterFuture, "future") \
V(StreamIterator, "StreamIterator") \
V(StreamIteratorConstructor, "StreamIterator.") \

View file

@ -132,6 +132,53 @@ class Timer {
}
}
class _AsyncAwaitCompleter<T> implements Completer<T> {
final _completer = new Completer<T>.sync();
bool isSync;
_AsyncAwaitCompleter() : isSync = false;
void complete([FutureOr<T> value]) {
if (isSync) {
_completer.complete(value);
} else if (value is Future<T>) {
value.then(_completer.complete, onError: _completer.completeError);
} else {
scheduleMicrotask(() {
_completer.complete(value);
});
}
}
void completeError(e, [st]) {
if (isSync) {
_completer.completeError(e, st);
} else {
scheduleMicrotask(() {
_completer.completeError(e, st);
});
}
}
Future<T> get future => _completer.future;
bool get isCompleted => _completer.isCompleted;
}
/// Initiates the computation of an `async` function and starts the body
/// synchronously.
///
/// Used as part of the runtime support for the async/await transformation.
///
/// This function sets up the first call into the transformed [bodyFunction].
/// Independently, it takes the [completer] and returns the future of the
/// completer for convenience of the transformed code.
dynamic _asyncStartSync(
_WrappedAsyncBody bodyFunction, _AsyncAwaitCompleter completer) {
bodyFunction(async_error_codes.SUCCESS, null);
completer.isSync = true;
return completer.future;
}
/// Initiates the computation of an `async` function.
///
/// Used as part of the runtime support for the async/await transformation.

View file

@ -19,8 +19,8 @@ main() {
return expect42(f());
});
test("async waits", () {
// Calling an "async" function won't do anything immediately.
test("async starts synchronously", () {
// Calling an "async" function starts immediately.
var result = [];
f() async {
result.add(1);
@ -31,7 +31,7 @@ main() {
var future = f();
result.add(0);
return future.whenComplete(() {
expect(result, equals([0, 1]));
expect(result, equals([1, 0]));
});
});
@ -184,7 +184,6 @@ main() {
return new Future.error("err"); // Not awaited.
}
;
return throwsErr(f());
});

View file

@ -14,6 +14,8 @@ foo() {
bar() async {
result += "bar";
await null;
result += "bar2";
}
main() {
@ -21,13 +23,13 @@ main() {
() async {
var f = new Future(foo);
var b = bar();
Expect.equals("", result);
Expect.equals("bar", result);
scheduleMicrotask(() => result += "micro");
await b;
await f;
// Validates that bar is scheduled as a microtask, before foo.
Expect.equals("barmicrofoo", result);
Expect.equals("barbar2microfoo", result);
asyncEnd();
}();
}

View file

@ -0,0 +1,264 @@
// Copyright (c) 2017, 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 'dart:async';
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
class AsyncTracker {
int runningAsyncs = 0;
List expectedEvents;
final List actualEvents = [];
AsyncTracker() {
asyncStart();
}
void start(String event) {
actualEvents.add("start $event");
runningAsyncs++;
}
void stop(String event) {
actualEvents.add("stop $event");
if (--runningAsyncs == 0) {
Expect.listEquals(expectedEvents, actualEvents);
asyncEnd();
}
}
void add(e) {
actualEvents.add(e);
}
}
void test1() {
var tracker = new AsyncTracker();
Future foo() async {
tracker.add("error-foo");
throw "foo";
}
tracker.start("micro1");
scheduleMicrotask(() {
tracker.start("micro2");
scheduleMicrotask(() {
tracker.stop("micro2");
});
tracker.stop("micro1");
});
tracker.start("foo");
foo().catchError((e) {
tracker.stop("foo");
});
tracker.start("micro3");
scheduleMicrotask(() {
tracker.stop("micro3");
});
tracker.expectedEvents = [
"start micro1",
"start foo",
"error-foo",
"start micro3",
"start micro2",
"stop micro1",
"stop foo",
"stop micro3",
"stop micro2",
];
}
void test2() {
var tracker = new AsyncTracker();
Future bar() async {
tracker.add("await null");
await null;
tracker.add("error-bar");
throw "bar";
}
tracker.start("micro1");
scheduleMicrotask(() {
tracker.start("micro2");
scheduleMicrotask(() {
tracker.start("micro3");
scheduleMicrotask(() {
tracker.stop("micro3");
});
tracker.stop("micro2");
});
tracker.stop("micro1");
});
tracker.start("bar");
bar().catchError((e) {
tracker.stop("bar");
});
tracker.start("micro4");
scheduleMicrotask(() {
tracker.start("micro5");
scheduleMicrotask(() {
tracker.stop("micro5");
});
tracker.stop("micro4");
});
tracker.expectedEvents = [
"start micro1",
"start bar",
"await null",
"start micro4",
"start micro2",
"stop micro1",
"error-bar",
"stop bar",
"start micro5",
"stop micro4",
"start micro3",
"stop micro2",
"stop micro5",
"stop micro3",
];
}
void test3() {
var tracker = new AsyncTracker();
Future gee() async {
tracker.add("error-gee");
return new Future.error("gee");
}
tracker.start("micro1");
scheduleMicrotask(() {
tracker.start("micro2");
scheduleMicrotask(() {
tracker.stop("micro2");
});
tracker.stop("micro1");
});
tracker.start("gee");
gee().catchError((e) {
tracker.stop("gee");
});
tracker.start("micro3");
scheduleMicrotask(() {
tracker.stop("micro3");
});
tracker.expectedEvents = [
"start micro1",
"start gee",
"error-gee",
"start micro3",
"start micro2",
"stop micro1",
"stop gee",
"stop micro3",
"stop micro2",
];
}
void test4() {
var tracker = new AsyncTracker();
Future toto() async {
tracker.add("await null");
await null;
tracker.add("error-toto");
return new Future.error("toto");
}
tracker.start("micro1");
scheduleMicrotask(() {
tracker.start("micro2");
scheduleMicrotask(() {
tracker.start("micro3");
scheduleMicrotask(() {
tracker.stop("micro3");
});
tracker.stop("micro2");
});
tracker.stop("micro1");
});
tracker.start("toto");
toto().catchError((e) {
tracker.stop("toto");
});
tracker.start("micro4");
scheduleMicrotask(() {
tracker.start("micro5");
scheduleMicrotask(() {
tracker.stop("micro5");
});
tracker.stop("micro4");
});
tracker.expectedEvents = [
"start micro1",
"start toto",
"await null",
"start micro4",
"start micro2",
"stop micro1",
"error-toto",
"start micro5",
"stop micro4",
"start micro3",
"stop micro2",
"stop toto",
"stop micro5",
"stop micro3",
];
}
void test5() {
var tracker = new AsyncTracker();
Future foo() async {
tracker.add("throw");
throw "foo";
}
bar() async {
tracker.start('micro');
scheduleMicrotask(() {
tracker.stop('micro');
});
try {
tracker.start('foo');
await foo();
} catch (e) {
tracker.stop('foo');
}
tracker.stop("bar");
}
tracker.start('bar');
tracker.expectedEvents = [
"start bar",
"start micro",
"start foo",
"throw",
"stop micro",
"stop foo",
"stop bar",
];
}
main() {
asyncStart();
test1();
test2();
test3();
test4();
asyncEnd();
}

View file

@ -40,9 +40,9 @@ foo1(Tracer tracer) async* {
yield 3;
tracer.trace("f");
} finally {
tracer.trace("f");
tracer.trace("g");
}
tracer.trace("g");
tracer.trace("h");
}
foo2(Tracer tracer) async* {
@ -116,10 +116,10 @@ runTest(test, expectedTrace, expectedError, shouldCancel) {
test() async {
// TODO(sigurdm): These tests are too dependent on scheduling, and buffering
// behavior.
await runTest(foo1, "abcdYefC", null, true);
await runTest(foo1, "abcYdgC", null, true);
await runTest(foo2, "abcX", "Error", false);
await runTest(foo3, "abcYX", "Error", false);
await runTest(foo4, "abcdYeYfX", "Error2", false);
await runTest(foo4, "abcYdYefX", "Error2", false);
}
void main() {

View file

@ -5,16 +5,23 @@
// VMOptions=--optimization-counter-threshold=5
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
var X = 0;
foo() async {
Expect.equals(X, 10); // foo runs after main returns.
return await 5;
Expect.equals(X, 0);
await 5;
Expect.equals(X, 10);
return await 499;
}
main() {
asyncStart();
var f = foo();
f.then((res) => print("f completed with $res"));
f.then((res) {
Expect.equals(499, res);
asyncEnd();
});
X = 10;
}

View file

@ -2,7 +2,7 @@
// 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.
// Test that an async function does not start immediately.
// Test that an async function does start immediately.
import "package:expect/expect.dart";
import "package:async_helper/async_helper.dart";
@ -18,5 +18,5 @@ foo() async {
void main() {
asyncStart();
foo().then((_) => Expect.equals(2, x)).whenComplete(asyncEnd);
Expect.equals(0, x);
Expect.equals(1, x);
}

View file

@ -49,6 +49,7 @@ field_type_check2_test/01: MissingRuntimeError
round_test: Pass, Fail, OK # Fixed in ff 35. Common JavaScript engine Math.round bug.
[ $compiler == dart2js && $runtime == jsshell ]
async_call_test: RuntimeError # Timer interface not supported: Issue 7728.
async_star_test/01: RuntimeError
async_star_test/02: RuntimeError
async_star_test/03: RuntimeError
@ -866,6 +867,7 @@ type_check_const_function_typedef2_test: MissingCompileTimeError
function_subtype_inline2_test: RuntimeError
[ $compiler == dart2js && $dart2js_with_kernel ]
async_error_timing_test: Crash
bug31436_test: RuntimeError
built_in_identifier_type_annotation_test/22: Crash # Issue 28815
built_in_identifier_type_annotation_test/52: MissingCompileTimeError # Issue 28815

View file

@ -13,6 +13,10 @@ var delayedValue = new Completer();
var delayedError = new Completer();
foo() async {
// Because of this `await null` the function returns and lets the caller
// install handlers. When the function finishes, it can then synchronously
// propagate the values.
await null;
new Future.microtask(() => 'in microtask')
.then(events.add)
.then(delayedValue.complete);
@ -20,6 +24,10 @@ foo() async {
}
bar() async {
// Because of this `await null` the function returns and lets the caller
// install handlers. When the function finishes, it can then synchronously
// propagate the values.
await null;
new Future.microtask(() => throw 'in microtask error')
.catchError(events.add)
.then(delayedError.complete);

View file

@ -278,6 +278,7 @@ html/interactive_geolocation_test: Skip # Requires allowing geo location.
html/custom/created_callback_test: RuntimeError
html/fontface_loaded_test: Fail # Support for promises.
html/js_typed_interop_lazy_test/01: RuntimeError
html/notification_permission_test: Timeout # Issue 32002
html/private_extension_member_test: RuntimeError
isolate/isolate_stress_test: Pass, Slow # Issue 10697
js/null_test: RuntimeError # Issue 30652

View file

@ -104,6 +104,7 @@ html/js_util_test: RuntimeError # Issue 29922
html/media_stream_test: RuntimeError # Issue 29922
html/mediasource_test: RuntimeError # Issue 29922
html/no_linked_scripts_htmltest: Skip # Issue 29919
html/notification_permission_test: Timeout # Issue 32002
html/scripts_htmltest: Skip # Issue 29919
html/transferables_test: CompileTimeError # Issue 30975
html/transition_event_test: Pass, RuntimeError, Timeout # Issue 29922, this test seems flaky

View file

@ -40,7 +40,8 @@ String dart2jsHtml(String title, String scriptPath) {
/// The [testName] is the short name of the test without any subdirectory path
/// or extension, like "math_test". The [testJSDir] is the relative path to the
/// build directory where the dartdevc-generated JS file is stored.
String dartdevcHtml(String testName, String testJSDir, String buildDir) {
String dartdevcHtml(String testName, String testJSDir, String buildDir,
{bool syncAsync}) {
var packagePaths = testPackages
.map((package) => ' "$package": "/root_dart/$buildDir/gen/utils/'
'dartdevc/pkg/$package",')
@ -86,7 +87,7 @@ window.ddcSettings = {
requirejs(["$testName", "dart_sdk", "async_helper"],
function($testName, sdk, async_helper) {
sdk.dart.ignoreWhitelistedErrors(false);
if ($syncAsync) sdk.dart.setStartAsyncSynchronously();
// TODO(rnystrom): This uses DDC's forked version of async_helper. Unfork
// these packages when possible.
async_helper.async_helper.asyncTestInitialize(function() {});

View file

@ -1065,6 +1065,7 @@ abstract class VMKernelCompilerMixin {
final args = [
_isAot ? '--aot' : '--no-aot',
_isStrong ? '--strong-mode' : '--no-strong-mode',
_isStrong ? '--sync-async' : '--no-sync-async',
'--platform=$vmPlatform',
'-o',
dillFile,

View file

@ -814,6 +814,12 @@ class StandardTestSuite extends TestSuite {
var commonArguments =
commonArgumentsFromFile(info.filePath, info.optionsFromFile);
// TODO(floitsch): Hack. When running the 2.0 tests always start
// async functions synchronously.
if (suiteName.endsWith("_2")) {
commonArguments.insert(0, "--sync-async");
}
var vmOptionsList = getVmOptions(info.optionsFromFile);
assert(!vmOptionsList.isEmpty);
@ -1026,13 +1032,37 @@ class StandardTestSuite extends TestSuite {
} else {
var jsDir =
new Path(compilationTempDir).relativeTo(Repository.dir).toString();
content = dartdevcHtml(nameNoExt, jsDir, buildDir);
// Always run with synchronous starts of `async` functions.
// If we want to make this dependent on other parameters or flags,
// this flag could be become conditional.
content = dartdevcHtml(nameNoExt, jsDir, buildDir, syncAsync: true);
}
}
var htmlPath = '$tempDir/test.html';
new File(htmlPath).writeAsStringSync(content);
// TODO(floitsch): Hack. When running the 2.0 tests always start
// async functions synchronously.
if (suiteName.endsWith("_2") &&
configuration.compiler == Compiler.dart2js) {
if (optionsFromFile == null) {
optionsFromFile = const <String, dynamic>{
'sharedOptions': const ['--sync-async']
};
} else {
optionsFromFile = new Map<String, dynamic>.from(optionsFromFile);
var sharedOptions = optionsFromFile['sharedOptions'];
if (sharedOptions == null) {
sharedOptions = const <String>['--sync-async'];
} else {
sharedOptions = sharedOptions.toList();
sharedOptions.insert(0, "--sync-async");
}
optionsFromFile['sharedOptions'] = sharedOptions;
}
}
// Construct the command(s) that compile all the inputs needed by the
// browser test. For running Dart in DRT, this will be noop commands.
var commands = <Command>[];