[vm/bytecode] Add async/async*/sync* attributes to closures

Change-Id: Ibb0f202ada840ee112f46d5db3d8f9459817c358
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106730
Reviewed-by: RĂ©gis Crelier <regis@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
diff --git a/pkg/vm/lib/bytecode/dbc.dart b/pkg/vm/lib/bytecode/dbc.dart
index 244e800..e89f666 100644
--- a/pkg/vm/lib/bytecode/dbc.dart
+++ b/pkg/vm/lib/bytecode/dbc.dart
@@ -10,7 +10,7 @@
 /// Before bumping current bytecode version format, make sure that
 /// all users have switched to a VM which is able to consume new
 /// version of bytecode.
-const int currentBytecodeFormatVersion = 10;
+const int currentBytecodeFormatVersion = 11;
 
 /// Version of experimental / bleeding edge bytecode format.
 /// Produced by bytecode generator when --use-future-bytecode-format
diff --git a/pkg/vm/lib/bytecode/declarations.dart b/pkg/vm/lib/bytecode/declarations.dart
index 58827b6..2519397 100644
--- a/pkg/vm/lib/bytecode/declarations.dart
+++ b/pkg/vm/lib/bytecode/declarations.dart
@@ -890,7 +890,11 @@
   static const int hasOptionalNamedParamsFlag = 1 << 1;
   static const int hasTypeParamsFlag = 1 << 2;
   static const int hasSourcePositionsFlag = 1 << 3;
+  static const int isAsyncFlag = 1 << 4;
+  static const int isAsyncStarFlag = 1 << 5;
+  static const int isSyncStarFlag = 1 << 6;
 
+  final int flags;
   final ObjectHandle parent;
   final ObjectHandle name;
   final int position;
@@ -903,6 +907,7 @@
   ClosureCode code;
 
   ClosureDeclaration(
+      this.flags,
       this.parent,
       this.name,
       this.position,
@@ -914,20 +919,6 @@
       this.returnType);
 
   void write(BufferedWriter writer) {
-    int flags = 0;
-    if (numRequiredParams != parameters.length) {
-      if (numNamedParams > 0) {
-        flags |= hasOptionalNamedParamsFlag;
-      } else {
-        flags |= hasOptionalPositionalParamsFlag;
-      }
-    }
-    if (typeParams.isNotEmpty) {
-      flags |= hasTypeParamsFlag;
-    }
-    if (position != TreeNode.noOffset) {
-      flags |= hasSourcePositionsFlag;
-    }
     writer.writePackedUInt30(flags);
     writer.writePackedObject(parent);
     writer.writePackedObject(name);
@@ -996,7 +987,7 @@
         (_) => new NameAndType(
             reader.readPackedObject(), reader.readPackedObject()));
     final returnType = reader.readPackedObject();
-    return new ClosureDeclaration(parent, name, position, endPosition,
+    return new ClosureDeclaration(flags, parent, name, position, endPosition,
         typeParams, numRequiredParams, numNamedParams, parameters, returnType);
   }
 
@@ -1004,6 +995,15 @@
   String toString() {
     final StringBuffer sb = new StringBuffer();
     sb.write('Closure $parent::$name');
+    if ((flags & isAsyncFlag) != 0) {
+      sb.write(' async');
+    }
+    if ((flags & isAsyncStarFlag) != 0) {
+      sb.write(' async*');
+    }
+    if ((flags & isSyncStarFlag) != 0) {
+      sb.write(' sync*');
+    }
     if (position != TreeNode.noOffset) {
       sb.write(' pos = $position, end-pos = $endPosition');
     }
diff --git a/pkg/vm/lib/bytecode/gen_bytecode.dart b/pkg/vm/lib/bytecode/gen_bytecode.dart
index 01426c5..d79d963 100644
--- a/pkg/vm/lib/bytecode/gen_bytecode.dart
+++ b/pkg/vm/lib/bytecode/gen_bytecode.dart
@@ -1852,7 +1852,8 @@
     }
   }
 
-  int _genClosureBytecode(TreeNode node, String name, FunctionNode function) {
+  int _genClosureBytecode(
+      LocalFunction node, String name, FunctionNode function) {
     _pushAssemblerState();
 
     locals.enterScope(node);
@@ -1865,15 +1866,6 @@
     final savedLoopDepth = currentLoopDepth;
     currentLoopDepth = 0;
 
-    int position = TreeNode.noOffset;
-    int endPosition = TreeNode.noOffset;
-    if (emitSourcePositions) {
-      position = (node is ast.FunctionDeclaration)
-          ? node.fileOffset
-          : function.fileOffset;
-      endPosition = function.fileEndOffset;
-    }
-
     if (function.typeParameters.isNotEmpty) {
       functionTypeParameters ??= new List<TypeParameter>();
       functionTypeParameters.addAll(function.typeParameters);
@@ -1890,27 +1882,8 @@
     locals.sortedNamedParameters.forEach(_evaluateDefaultParameterValue);
 
     final int closureIndex = closures.length;
-    objectTable.declareClosure(function, enclosingMember, closureIndex);
-    final List<NameAndType> parameters = function.positionalParameters
-        .followedBy(function.namedParameters)
-        .map((v) => new NameAndType(objectTable.getNameHandle(null, v.name),
-            objectTable.getHandle(v.type)))
-        .toList();
-    final ClosureDeclaration closure = new ClosureDeclaration(
-        objectTable
-            .getHandle(savedIsClosure ? parentFunction : enclosingMember),
-        objectTable.getNameHandle(null, name),
-        position,
-        endPosition,
-        function.typeParameters
-            .map((tp) => new NameAndType(
-                objectTable.getNameHandle(null, tp.name),
-                objectTable.getHandle(tp.bound)))
-            .toList(),
-        function.requiredParameterCount,
-        function.namedParameters.length,
-        parameters,
-        objectTable.getHandle(function.returnType));
+    final closure = getClosureDeclaration(node, function, name, closureIndex,
+        savedIsClosure ? parentFunction : enclosingMember);
     closures.add(closure);
 
     final int closureFunctionIndex = cp.addClosureFunction(closureIndex);
@@ -1972,6 +1945,71 @@
     return closureFunctionIndex;
   }
 
+  ClosureDeclaration getClosureDeclaration(LocalFunction node,
+      FunctionNode function, String name, int closureIndex, TreeNode parent) {
+    objectTable.declareClosure(function, enclosingMember, closureIndex);
+
+    int flags = 0;
+    int position = TreeNode.noOffset;
+    int endPosition = TreeNode.noOffset;
+    if (emitSourcePositions) {
+      position = (node is ast.FunctionDeclaration)
+          ? node.fileOffset
+          : function.fileOffset;
+      endPosition = function.fileEndOffset;
+      if (position != TreeNode.noOffset) {
+        flags |= ClosureDeclaration.hasSourcePositionsFlag;
+      }
+    }
+
+    switch (function.dartAsyncMarker) {
+      case AsyncMarker.Async:
+        flags |= ClosureDeclaration.isAsyncFlag;
+        break;
+      case AsyncMarker.AsyncStar:
+        flags |= ClosureDeclaration.isAsyncStarFlag;
+        break;
+      case AsyncMarker.SyncStar:
+        flags |= ClosureDeclaration.isSyncStarFlag;
+        break;
+      default:
+        break;
+    }
+
+    final List<NameAndType> parameters = function.positionalParameters
+        .followedBy(function.namedParameters)
+        .map((v) => new NameAndType(objectTable.getNameHandle(null, v.name),
+            objectTable.getHandle(v.type)))
+        .toList();
+    if (function.requiredParameterCount != parameters.length) {
+      if (function.namedParameters.isNotEmpty) {
+        flags |= ClosureDeclaration.hasOptionalNamedParamsFlag;
+      } else {
+        flags |= ClosureDeclaration.hasOptionalPositionalParamsFlag;
+      }
+    }
+
+    final typeParams = function.typeParameters
+        .map((tp) => new NameAndType(objectTable.getNameHandle(null, tp.name),
+            objectTable.getHandle(tp.bound)))
+        .toList();
+    if (typeParams.isNotEmpty) {
+      flags |= ClosureDeclaration.hasTypeParamsFlag;
+    }
+
+    return new ClosureDeclaration(
+        flags,
+        objectTable.getHandle(parent),
+        objectTable.getNameHandle(null, name),
+        position,
+        endPosition,
+        typeParams,
+        function.requiredParameterCount,
+        function.namedParameters.length,
+        parameters,
+        objectTable.getHandle(function.returnType));
+  }
+
   void _genSyncYieldingPrologue(FunctionNode function, Label continuationLabel,
       int switchVarIndexInFrame) {
     // switch_var = :await_jump_var
@@ -2045,7 +2083,7 @@
     asm.emitStoreFieldTOS(cp.addInstanceField(closureContext));
   }
 
-  void _genClosure(TreeNode node, String name, FunctionNode function) {
+  void _genClosure(LocalFunction node, String name, FunctionNode function) {
     final int closureFunctionIndex = _genClosureBytecode(node, name, function);
     _genAllocateClosureInstance(node, closureFunctionIndex, function);
   }
diff --git a/pkg/vm/testcases/bytecode/async.dart.expect b/pkg/vm/testcases/bytecode/async.dart.expect
index 32e7ee9..e142924 100644
--- a/pkg/vm/testcases/bytecode/async.dart.expect
+++ b/pkg/vm/testcases/bytecode/async.dart.expect
@@ -75,7 +75,7 @@
   [38] = Reserved
   [39] = EndClosureFunctionScope
 }
-Closure #lib::asyncInFieldInitializer (field)::'<anonymous closure>' (dart:async::Future < dart:core::int > x) -> dart:async::Future < dart:core::Null >
+Closure #lib::asyncInFieldInitializer (field)::'<anonymous closure>' async (dart:async::Future < dart:core::int > x) -> dart:async::Future < dart:core::Null >
 ClosureCode {
   EntryFixed           2, 4
   CheckStack           0
@@ -1708,7 +1708,7 @@
   [38] = Reserved
   [39] = EndClosureFunctionScope
 }
-Closure #lib::closure::'nested' () -> dart:async::Future < dart:core::int >
+Closure #lib::closure::'nested' async () -> dart:async::Future < dart:core::int >
 ClosureCode {
   EntryFixed           1, 4
   CheckStack           0
diff --git a/runtime/vm/compiler/frontend/bytecode_reader.cc b/runtime/vm/compiler/frontend/bytecode_reader.cc
index 5d3ba7d..c7f1d7a 100644
--- a/runtime/vm/compiler/frontend/bytecode_reader.cc
+++ b/runtime/vm/compiler/frontend/bytecode_reader.cc
@@ -472,6 +472,9 @@
   const int kHasOptionalNamedParamsFlag = 1 << 1;
   const int kHasTypeParamsFlag = 1 << 2;
   const int kHasSourcePositionsFlag = 1 << 3;
+  const int kIsAsyncFlag = 1 << 4;
+  const int kIsAsyncStarFlag = 1 << 5;
+  const int kIsSyncStarFlag = 1 << 6;
 
   const intptr_t flags = reader_.ReadUInt();
 
@@ -499,6 +502,19 @@
   closure.set_is_declared_in_bytecode(true);
   closure.set_end_token_pos(end_position);
 
+  if ((flags & kIsSyncStarFlag) != 0) {
+    closure.set_modifier(RawFunction::kSyncGen);
+  } else if ((flags & kIsAsyncFlag) != 0) {
+    closure.set_modifier(RawFunction::kAsync);
+    closure.set_is_inlinable(!FLAG_causal_async_stacks);
+  } else if ((flags & kIsAsyncStarFlag) != 0) {
+    closure.set_modifier(RawFunction::kAsyncGen);
+    closure.set_is_inlinable(!FLAG_causal_async_stacks);
+  }
+  if (Function::Cast(parent).IsAsyncOrGenerator()) {
+    closure.set_is_generated_body(true);
+  }
+
   closures_->SetAt(closureIndex, closure);
 
   Type& signature_type = Type::Handle(
diff --git a/runtime/vm/constants_kbc.h b/runtime/vm/constants_kbc.h
index 5c422d8..caac3a1 100644
--- a/runtime/vm/constants_kbc.h
+++ b/runtime/vm/constants_kbc.h
@@ -749,7 +749,7 @@
   // Maximum bytecode format version supported by VM.
   // The range of supported versions should include version produced by bytecode
   // generator (currentBytecodeFormatVersion in pkg/vm/lib/bytecode/dbc.dart).
-  static const intptr_t kMaxSupportedBytecodeFormatVersion = 10;
+  static const intptr_t kMaxSupportedBytecodeFormatVersion = 11;
 
   enum Opcode {
 #define DECLARE_BYTECODE(name, encoding, kind, op1, op2, op3) k##name,
diff --git a/tools/VERSION b/tools/VERSION
index aa48b1b..831acfe 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -35,5 +35,5 @@
 PATCH 3
 PRERELEASE 0
 PRERELEASE_PATCH 0
-ABI_VERSION 5
+ABI_VERSION 6
 OLDEST_SUPPORTED_ABI_VERSION 3