mirror of
https://github.com/dart-lang/sdk
synced 2024-10-06 12:47:14 +00:00
Run async functions immediately.
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>
This commit is contained in:
parent
a42135073d
commit
67bac0bce6
|
@ -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
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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")');
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -48,7 +48,9 @@ abstract class Compiler {
|
|||
CompilerOptions options;
|
||||
|
||||
Compiler(this.fileSystem, Uri platformKernel,
|
||||
{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;
|
||||
|
@ -59,12 +61,14 @@ abstract class Compiler {
|
|||
print("DFE: Platform.resolvedExecutable: ${Platform.resolvedExecutable}");
|
||||
print("DFE: platformKernel: ${platformKernel}");
|
||||
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 = platformKernel
|
||||
..verbose = verbose
|
||||
|
@ -99,9 +103,11 @@ class IncrementalCompiler extends Compiler {
|
|||
IncrementalKernelGenerator generator;
|
||||
|
||||
IncrementalCompiler(FileSystem fileSystem, Uri platformKernel,
|
||||
{bool strongMode: false, bool suppressWarnings: false})
|
||||
{bool strongMode: false, bool suppressWarnings: false, syncAsync: false})
|
||||
: super(fileSystem, platformKernel,
|
||||
strongMode: strongMode, suppressWarnings: suppressWarnings);
|
||||
strongMode: strongMode,
|
||||
suppressWarnings: suppressWarnings,
|
||||
syncAsync: syncAsync);
|
||||
|
||||
@override
|
||||
Future<Program> compileInternal(Uri script) async {
|
||||
|
@ -122,9 +128,12 @@ class SingleShotCompiler extends Compiler {
|
|||
SingleShotCompiler(FileSystem fileSystem, Uri platformKernel,
|
||||
{this.requireMain: false,
|
||||
bool strongMode: false,
|
||||
bool suppressWarnings: false})
|
||||
bool suppressWarnings: false,
|
||||
bool syncAsync: false})
|
||||
: super(fileSystem, platformKernel,
|
||||
strongMode: strongMode, suppressWarnings: suppressWarnings);
|
||||
strongMode: strongMode,
|
||||
suppressWarnings: suppressWarnings,
|
||||
syncAsync: syncAsync);
|
||||
|
||||
@override
|
||||
Future<Program> compileInternal(Uri script) async {
|
||||
|
@ -138,7 +147,9 @@ final Map<int, Compiler> isolateCompilers = new Map<int, Compiler>();
|
|||
|
||||
Future<Compiler> lookupOrBuildNewIncrementalCompiler(
|
||||
int isolateId, List sourceFiles, Uri 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];
|
||||
|
@ -162,7 +173,9 @@ Future<Compiler> lookupOrBuildNewIncrementalCompiler(
|
|||
// 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, platformKernel,
|
||||
strongMode: strongMode, suppressWarnings: suppressWarnings);
|
||||
strongMode: strongMode,
|
||||
suppressWarnings: suppressWarnings,
|
||||
syncAsync: syncAsync);
|
||||
isolateCompilers[isolateId] = compiler;
|
||||
}
|
||||
return compiler;
|
||||
|
@ -189,6 +202,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];
|
||||
|
||||
Compiler compiler;
|
||||
// TODO(aam): There should be no need to have an option to choose
|
||||
|
@ -198,7 +212,7 @@ Future _processLoadRequest(request) async {
|
|||
if (incremental) {
|
||||
compiler = await lookupOrBuildNewIncrementalCompiler(
|
||||
isolateId, sourceFiles, platformKernel,
|
||||
suppressWarnings: suppressWarnings);
|
||||
suppressWarnings: suppressWarnings, syncAsync: syncAsync);
|
||||
} else {
|
||||
final FileSystem fileSystem = sourceFiles == null
|
||||
? StandardFileSystem.instance
|
||||
|
@ -206,7 +220,8 @@ Future _processLoadRequest(request) async {
|
|||
compiler = new SingleShotCompiler(fileSystem, platformKernel,
|
||||
requireMain: sourceFiles == null,
|
||||
strongMode: strong,
|
||||
suppressWarnings: suppressWarnings);
|
||||
suppressWarnings: suppressWarnings,
|
||||
syncAsync: syncAsync);
|
||||
}
|
||||
|
||||
CompilationResult result;
|
||||
|
@ -293,6 +308,7 @@ train(String scriptUri, String platformKernel) {
|
|||
1 /* isolateId chosen randomly */,
|
||||
null /* source files */,
|
||||
false /* suppress warnings */,
|
||||
false /* synchronous async */,
|
||||
];
|
||||
_processLoadRequest(request);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.") \
|
||||
|
|
|
@ -370,6 +370,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,
|
||||
|
@ -378,7 +382,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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.") \
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
});
|
||||
|
||||
|
|
|
@ -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();
|
||||
}();
|
||||
}
|
||||
|
|
264
tests/language_2/async_error_timing_test.dart
Normal file
264
tests/language_2/async_error_timing_test.dart
Normal 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();
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -89,6 +89,7 @@ html/fileapi_directory_test: Fail # TODO(dart2js-team): Please triage this failu
|
|||
html/fileapi_entry_test: Pass, Fail # TODO(dart2js-team): Please triage this failure.
|
||||
html/fileapi_file_test: Fail # TODO(dart2js-team): Please triage this failure.
|
||||
html/media_stream_test: RuntimeError # Please triage.
|
||||
html/notification_permission_test: Timeout # Issue 32002
|
||||
html/speechrecognition_test: RuntimeError # Please triage.
|
||||
isolate/function_send_test: Skip # Crashes Chrome 62: https://bugs.chromium.org/p/chromium/issues/detail?id=775506
|
||||
isolate/kill_self_synchronously_test: RuntimeError
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {});
|
||||
|
|
|
@ -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>[];
|
||||
|
|
Loading…
Reference in a new issue