[vm] Support closurization of methods through @pragma entry-points.

Fixes dartbug.com/35720.

Also enable native extensions in AOT.
Increases Flutter Gallery snapshot size by 0.19%.

Change-Id: I1b24c3b9a49a13fd48452e9a89595516a7f01780
Cq-Include-Trybots: luci.dart.try:vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-mac-release-simarm64-try, vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/92283
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/pkg/vm/lib/transformations/pragma.dart b/pkg/vm/lib/transformations/pragma.dart
index 0ab0b88..ff3431b 100644
--- a/pkg/vm/lib/transformations/pragma.dart
+++ b/pkg/vm/lib/transformations/pragma.dart
@@ -13,7 +13,7 @@
 
 abstract class ParsedPragma {}
 
-enum PragmaEntryPointType { Always, GetterOnly, SetterOnly }
+enum PragmaEntryPointType { Default, GetterOnly, SetterOnly, CallOnly }
 
 class ParsedEntryPointPragma extends ParsedPragma {
   final PragmaEntryPointType type;
@@ -73,17 +73,20 @@
       case kEntryPointPragmaName:
         PragmaEntryPointType type;
         if (options is NullConstant) {
-          type = PragmaEntryPointType.Always;
+          type = PragmaEntryPointType.Default;
         } else if (options is BoolConstant && options.value == true) {
-          type = PragmaEntryPointType.Always;
+          type = PragmaEntryPointType.Default;
         } else if (options is StringConstant) {
           if (options.value == "get") {
             type = PragmaEntryPointType.GetterOnly;
           } else if (options.value == "set") {
             type = PragmaEntryPointType.SetterOnly;
+          } else if (options.value == "call") {
+            type = PragmaEntryPointType.CallOnly;
           } else {
             throw "Error: string directive to @pragma('$kEntryPointPragmaName', ...) "
-                "must be either 'get' or 'set'.";
+                "must be either 'get' or 'set' for fields "
+                "or 'get' or 'call' for procedures.";
           }
         }
         return type != null ? new ParsedEntryPointPragma(type) : null;
diff --git a/pkg/vm/lib/transformations/type_flow/native_code.dart b/pkg/vm/lib/transformations/type_flow/native_code.dart
index 291237b..26f3c47 100644
--- a/pkg/vm/lib/transformations/type_flow/native_code.dart
+++ b/pkg/vm/lib/transformations/type_flow/native_code.dart
@@ -58,7 +58,7 @@
     if (!klass.isAbstract) {
       var type = _annotationsDefineRoot(klass.annotations);
       if (type != null) {
-        if (type != PragmaEntryPointType.Always) {
+        if (type != PragmaEntryPointType.Default) {
           throw "Error: pragma entry-point definition on a class must evaluate "
               "to null, true or false. See entry_points_pragma.md.";
         }
@@ -73,17 +73,39 @@
   visitProcedure(Procedure proc) {
     var type = _annotationsDefineRoot(proc.annotations);
     if (type != null) {
-      if (type != PragmaEntryPointType.Always) {
-        throw "Error: pragma entry-point definition on a procedure (including"
-            "getters and setters) must evaluate to null, true or false. "
-            "See entry_points_pragma.md.";
+      void addSelector(CallKind ck) {
+        entryPoints.addRawCall(proc.isInstanceMember
+            ? new InterfaceSelector(proc, callKind: ck)
+            : new DirectSelector(proc, callKind: ck));
       }
-      var callKind = proc.isGetter
+
+      final defaultCallKind = proc.isGetter
           ? CallKind.PropertyGet
           : (proc.isSetter ? CallKind.PropertySet : CallKind.Method);
-      entryPoints.addRawCall(proc.isInstanceMember
-          ? new InterfaceSelector(proc, callKind: callKind)
-          : new DirectSelector(proc, callKind: callKind));
+
+      switch (type) {
+        case PragmaEntryPointType.CallOnly:
+          addSelector(defaultCallKind);
+          break;
+        case PragmaEntryPointType.SetterOnly:
+          if (!proc.isSetter) {
+            throw "Error: cannot generate a setter for a method or getter ($proc).";
+          }
+          addSelector(CallKind.PropertySet);
+          break;
+        case PragmaEntryPointType.GetterOnly:
+          if (proc.isSetter) {
+            throw "Error: cannot closurize a setter ($proc).";
+          }
+          addSelector(CallKind.PropertyGet);
+          break;
+        case PragmaEntryPointType.Default:
+          addSelector(defaultCallKind);
+          if (!proc.isSetter && !proc.isGetter) {
+            addSelector(CallKind.PropertyGet);
+          }
+      }
+
       nativeCodeOracle.setMemberReferencedFromNativeCode(proc);
     }
   }
@@ -92,9 +114,10 @@
   visitConstructor(Constructor ctor) {
     var type = _annotationsDefineRoot(ctor.annotations);
     if (type != null) {
-      if (type != PragmaEntryPointType.Always) {
-        throw "Error: pragma entry-point definition on a constructor must "
-            "evaluate to null, true or false. See entry_points_pragma.md.";
+      if (type != PragmaEntryPointType.Default &&
+          type != PragmaEntryPointType.CallOnly) {
+        throw "Error: pragma entry-point definition on a constructor ($ctor) must"
+            "evaluate to null, true, false or 'call'. See entry_points_pragma.md.";
       }
       entryPoints
           .addRawCall(new DirectSelector(ctor, callKind: CallKind.Method));
@@ -125,12 +148,15 @@
         }
         addSelector(CallKind.PropertySet);
         break;
-      case PragmaEntryPointType.Always:
+      case PragmaEntryPointType.Default:
         addSelector(CallKind.PropertyGet);
         if (!field.isFinal) {
           addSelector(CallKind.PropertySet);
         }
         break;
+      case PragmaEntryPointType.CallOnly:
+        throw "Error: can't generate invocation dispatcher for field $field"
+            "through @pragma('vm:entry-point')";
     }
 
     nativeCodeOracle.setMemberReferencedFromNativeCode(field);
diff --git a/pkg/vm/test/transformations/type_flow/annotation_matcher.dart b/pkg/vm/test/transformations/type_flow/annotation_matcher.dart
index 1b959c5..2b7f278 100644
--- a/pkg/vm/test/transformations/type_flow/annotation_matcher.dart
+++ b/pkg/vm/test/transformations/type_flow/annotation_matcher.dart
@@ -49,7 +49,7 @@
       case kEntryPointPragmaName:
         // We ignore the option because we can't properly evaluate it, assume
         // it's true.
-        return new ParsedEntryPointPragma(PragmaEntryPointType.Always);
+        return new ParsedEntryPointPragma(PragmaEntryPointType.Default);
       case kExactResultTypePragmaName:
         if (options is TypeLiteral) {
           return new ParsedResultTypeByTypePragma(options.type);
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
index 5c65020..5fc9b87 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/no_such_method.dart.expect
@@ -45,7 +45,7 @@
   synthetic constructor •() → self::B
     : super self::A::•()
     ;
-[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasTearOffUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
+[@vm.procedure-attributes.metadata=hasDynamicUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T1::•();
   }
   no-such-method-forwarder get bar() → dynamic
@@ -59,7 +59,7 @@
   synthetic constructor •() → self::C
     : super core::Object::•()
     ;
-[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasTearOffUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
+[@vm.procedure-attributes.metadata=hasDynamicUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T2::•();
   }
 }
@@ -78,7 +78,7 @@
   synthetic constructor •() → self::E
     : super core::Object::•()
     ;
-[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasTearOffUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
+[@vm.procedure-attributes.metadata=hasDynamicUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T4::•();
   }
   no-such-method-forwarder get bar() → dynamic
@@ -88,7 +88,7 @@
   synthetic constructor •() → self::F
     : super core::Object::•()
     ;
-[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
+[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T2::•();
   }
 }
@@ -96,7 +96,7 @@
   synthetic constructor •() → self::G
     : super core::Object::•()
     ;
-[@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
+[@vm.procedure-attributes.metadata=hasThisUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T5::•();
   }
 }
@@ -106,7 +106,7 @@
     ;
 [@vm.procedure-attributes.metadata=hasThisUses:false,hasTearOffUses:false]  method foo({[@vm.inferred-type.metadata=dart.core::_Smi] dynamic left = null, [@vm.inferred-type.metadata=dart.core::_Smi] dynamic right = null}) → dynamic
     return new self::T6::•();
-[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false,hasTearOffUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
+[@vm.procedure-attributes.metadata=hasDynamicUses:false,hasThisUses:false]  method noSuchMethod(core::Invocation invocation) → dynamic {
     return new self::T7::•();
   }
 }
diff --git a/runtime/bin/entrypoints_verification_test_extension.cc b/runtime/bin/entrypoints_verification_test_extension.cc
index e070996..f6b347d 100644
--- a/runtime/bin/entrypoints_verification_test_extension.cc
+++ b/runtime/bin/entrypoints_verification_test_extension.cc
@@ -16,6 +16,8 @@
     abort();                                                                   \
   }
 
+bool isDartPrecompiledRuntime = true;
+
 Dart_Handle GetCurrentLibrary() {
   Dart_Handle libraries = Dart_GetLoadedLibraries();
   CHECK(libraries);
@@ -36,6 +38,13 @@
   abort();
 }
 
+// Some invalid accesses are allowed in AOT since we don't retain @pragma
+// annotations. Therefore we skip the negative tests in AOT.
+#define FAIL(name, result)                                                     \
+  if (!isDartPrecompiledRuntime) {                                             \
+    Fail(name, result);                                                        \
+  }
+
 void Fail(const char* name, Dart_Handle result) {
   ASSERT(Dart_IsApiError(result));
   const char* error = Dart_GetError(result);
@@ -43,37 +52,52 @@
   ASSERT(strstr(error, "It is illegal to access"));
 }
 
-void FailClosurize(const char* name, Dart_Handle result) {
+#define FAIL_INVOKE_FIELD(name, result)                                        \
+  if (!isDartPrecompiledRuntime) {                                             \
+    FailInvokeField(name, result);                                             \
+  }
+
+void FailInvokeField(const char* name, Dart_Handle result) {
   ASSERT(Dart_IsApiError(result));
   const char* error = Dart_GetError(result);
   ASSERT(strstr(error, name));
-  ASSERT(strstr(error, "Entry-points do not allow closurizing methods"));
+  ASSERT(strstr(error, "Entry-points do not allow invoking fields"));
+}
+
+void FailClosurizeConstructor(const char* name, Dart_Handle result) {
+  ASSERT(Dart_IsUnhandledExceptionError(result));
+  const char* error = Dart_GetError(result);
+  ASSERT(strstr(error, name));
+  ASSERT(strstr(error, "No static getter"));
 }
 
 void TestFields(Dart_Handle target) {
-  Fail("fld0", Dart_GetField(target, Dart_NewStringFromCString("fld0")));
-  Fail("fld0",
+  FAIL("fld0", Dart_GetField(target, Dart_NewStringFromCString("fld0")));
+  FAIL("fld0",
        Dart_SetField(target, Dart_NewStringFromCString("fld0"), Dart_Null()));
 
-  Dart_Handle result =
-      Dart_Invoke(target, Dart_NewStringFromCString("fld0"), 0, nullptr);
-  FailClosurize("fld0", result);
+  FAIL_INVOKE_FIELD(
+      "fld0",
+      Dart_Invoke(target, Dart_NewStringFromCString("fld0"), 0, nullptr));
 
   CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld1")));
   CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld1"), Dart_Null()));
-  FailClosurize("fld1", Dart_Invoke(target, Dart_NewStringFromCString("fld1"),
-                                    0, nullptr));
+  FAIL_INVOKE_FIELD(
+      "fld1",
+      Dart_Invoke(target, Dart_NewStringFromCString("fld1"), 0, nullptr));
 
   CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld2")));
-  Fail("fld2",
+  FAIL("fld2",
        Dart_SetField(target, Dart_NewStringFromCString("fld2"), Dart_Null()));
-  FailClosurize("fld2", Dart_Invoke(target, Dart_NewStringFromCString("fld2"),
-                                    0, nullptr));
+  FAIL_INVOKE_FIELD(
+      "fld2",
+      Dart_Invoke(target, Dart_NewStringFromCString("fld2"), 0, nullptr));
 
-  Fail("fld3", Dart_GetField(target, Dart_NewStringFromCString("fld3")));
+  FAIL("fld3", Dart_GetField(target, Dart_NewStringFromCString("fld3")));
   CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld3"), Dart_Null()));
-  FailClosurize("fld3", Dart_Invoke(target, Dart_NewStringFromCString("fld3"),
-                                    0, nullptr));
+  FAIL_INVOKE_FIELD(
+      "fld3",
+      Dart_Invoke(target, Dart_NewStringFromCString("fld3"), 0, nullptr));
 }
 
 void RunTests(Dart_NativeArguments arguments) {
@@ -81,14 +105,14 @@
 
   //////// Test allocation and constructor invocation.
 
-  Fail("C", Dart_GetClass(lib, Dart_NewStringFromCString("C")));
+  FAIL("C", Dart_GetClass(lib, Dart_NewStringFromCString("C")));
 
   Dart_Handle D_class = Dart_GetClass(lib, Dart_NewStringFromCString("D"));
   CHECK(D_class);
 
   CHECK(Dart_Allocate(D_class));
 
-  Fail("D.", Dart_New(D_class, Dart_Null(), 0, nullptr));
+  FAIL("D.", Dart_New(D_class, Dart_Null(), 0, nullptr));
 
   CHECK(Dart_New(D_class, Dart_NewStringFromCString("defined"), 0, nullptr));
   Dart_Handle D =
@@ -97,32 +121,51 @@
 
   //////// Test actions against methods
 
-  Fail("fn0", Dart_Invoke(D, Dart_NewStringFromCString("fn0"), 0, nullptr));
+  FailClosurizeConstructor(
+      "defined", Dart_GetField(D_class, Dart_NewStringFromCString("defined")));
+  FailClosurizeConstructor(
+      "fact", Dart_GetField(D_class, Dart_NewStringFromCString("fact")));
+
+  FAIL("fn0", Dart_Invoke(D, Dart_NewStringFromCString("fn0"), 0, nullptr));
 
   CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1"), 0, nullptr));
+  FAIL("fn1", Dart_Invoke(D, Dart_NewStringFromCString("fn1_get"), 0, nullptr));
+  CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1_call"), 0, nullptr));
 
-  Fail("get_fn0", Dart_GetField(D, Dart_NewStringFromCString("fn0")));
+  FAIL("fn0", Dart_GetField(D, Dart_NewStringFromCString("fn0")));
 
-  Fail("get_fn1", Dart_GetField(D, Dart_NewStringFromCString("fn1")));
+  CHECK(Dart_GetField(D, Dart_NewStringFromCString("fn1")));
+  CHECK(Dart_GetField(D, Dart_NewStringFromCString("fn1_get")));
+  FAIL("fn1", Dart_GetField(D, Dart_NewStringFromCString("fn1_call")));
 
-  Fail("fn2",
+  FAIL("fn2",
        Dart_Invoke(D_class, Dart_NewStringFromCString("fn2"), 0, nullptr));
 
   CHECK(Dart_Invoke(D_class, Dart_NewStringFromCString("fn3"), 0, nullptr));
+  CHECK(
+      Dart_Invoke(D_class, Dart_NewStringFromCString("fn3_call"), 0, nullptr));
+  FAIL("fn3",
+       Dart_Invoke(D_class, Dart_NewStringFromCString("fn3_get"), 0, nullptr));
 
-  FailClosurize("fn2",
-                Dart_GetField(D_class, Dart_NewStringFromCString("fn2")));
+  FAIL("fn2", Dart_GetField(D_class, Dart_NewStringFromCString("fn2")));
 
-  FailClosurize("fn3",
-                Dart_GetField(D_class, Dart_NewStringFromCString("fn3")));
+  CHECK(Dart_GetField(D_class, Dart_NewStringFromCString("fn3")));
+  FAIL("fn3_call",
+       Dart_GetField(D_class, Dart_NewStringFromCString("fn3_call")));
+  CHECK(Dart_GetField(D_class, Dart_NewStringFromCString("fn3_get")));
 
-  Fail("fn0", Dart_Invoke(lib, Dart_NewStringFromCString("fn0"), 0, nullptr));
+  FAIL("fn0", Dart_Invoke(lib, Dart_NewStringFromCString("fn0"), 0, nullptr));
 
   CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1"), 0, nullptr));
+  FAIL("fn1",
+       Dart_Invoke(lib, Dart_NewStringFromCString("fn1_get"), 0, nullptr));
+  CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1_call"), 0, nullptr));
 
-  FailClosurize("fn0", Dart_GetField(lib, Dart_NewStringFromCString("fn0")));
+  FAIL("fn0", Dart_GetField(lib, Dart_NewStringFromCString("fn0")));
 
-  FailClosurize("fn1", Dart_GetField(lib, Dart_NewStringFromCString("fn1")));
+  CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1")));
+  CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1_get")));
+  FAIL("fn1", Dart_GetField(lib, Dart_NewStringFromCString("fn1_call")));
 
   //////// Test actions against fields
 
@@ -146,6 +189,8 @@
 
 DART_EXPORT Dart_Handle
 entrypoints_verification_test_extension_Init(Dart_Handle parent_library) {
+  isDartPrecompiledRuntime = Dart_IsPrecompiledRuntime();
+
   if (Dart_IsError(parent_library)) {
     return parent_library;
   }
diff --git a/runtime/docs/compiler/aot/entry_point_pragma.md b/runtime/docs/compiler/aot/entry_point_pragma.md
index c560070..3694a98 100644
--- a/runtime/docs/compiler/aot/entry_point_pragma.md
+++ b/runtime/docs/compiler/aot/entry_point_pragma.md
@@ -55,16 +55,24 @@
 @pragma("vm:entry-point")
 @pragma("vm:entry-point", true/false)
 @pragma("vm:entry-point", !const bool.formEnvironment("dart.vm.product"))
+@pragma("vm:entry-point", "get")
+@pragma("vm:entry-point", "call")
 void foo() { ... }
 ```
 
-If the second parameter is missing, `null` or `true`, the procedure will
-available for lookup and invocation directly from native or VM code. If the
-procedure is a *generative* constructor, the enclosing class must also be
+If the second parameter is missing, `null` or `true`, the procedure (and its
+closurized form, excluding constructors and setters) will available for lookup
+and invocation directly from native or VM code.
+
+If the procedure is a *generative* constructor, the enclosing class must also be
 annotated for allocation from native or VM code.
 
-Note that annotating a procedure does not allow closurizing it, e.g. access a
-non-getter via `Dart_GetField`.
+If the annotation is "get" or "call", the procedure will only be available for
+closurization (access via `Dart_GetField`) or invocation (access via
+`Dart_Invoke`).
+
+"@pragma("vm:entry-point", "get") against constructors or setters is disallowed
+since they cannot be closurized.
 
 ### Fields
 
@@ -86,3 +94,5 @@
 'get'/'set' parameter is used, only the getter/setter is marked. For static
 fields, the implicit getter is always marked. The third form does not make sense
 for static fields because they do not belong to an interface.
+
+Note that no form of entry-point annotation allows invoking a field.
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 2187130..7daf021 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -987,6 +987,7 @@
   auto& cls = Class::Handle(Z);
   auto& members = Array::Handle(Z);
   auto& function = Function::Handle(Z);
+  auto& function2 = Function::Handle(Z);
   auto& field = Field::Handle(Z);
   auto& metadata = Array::Handle(Z);
   auto& reusable_object_handle = Object::Handle(Z);
@@ -1051,13 +1052,23 @@
         if (function.has_pragma()) {
           metadata ^= lib.GetMetadata(function);
           if (metadata.IsNull()) continue;
-          if (FindEntryPointPragma(isolate(), metadata, &reusable_field_handle,
-                                   &reusable_object_handle) !=
-              EntryPointPragma::kAlways) {
-            continue;
+          auto type =
+              FindEntryPointPragma(isolate(), metadata, &reusable_field_handle,
+                                   &reusable_object_handle);
+
+          if (type == EntryPointPragma::kAlways ||
+              type == EntryPointPragma::kCallOnly) {
+            AddFunction(function);
           }
 
-          AddFunction(function);
+          if ((type == EntryPointPragma::kAlways ||
+               type == EntryPointPragma::kGetterOnly) &&
+              function.kind() != RawFunction::kConstructor &&
+              !function.IsSetterFunction()) {
+            function2 = function.ImplicitClosureFunction();
+            AddFunction(function2);
+          }
+
           if (function.IsGenerativeConstructor()) {
             AddInstantiatedClass(cls);
           }
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 8e45f57..c0e9a54 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -3749,7 +3749,7 @@
         current_func, constr_name.ToCString(), error_message.ToCString()));
     return ApiError::New(message);
   }
-  RawError* error = constructor.VerifyEntryPoint();
+  RawError* error = constructor.VerifyCallEntryPoint();
   if (error != Error::null()) return error;
   return constructor.raw();
 }
@@ -4043,7 +4043,7 @@
   if (!constructor.IsNull() && constructor.IsGenerativeConstructor() &&
       constructor.AreValidArgumentCounts(
           kTypeArgsLen, number_of_arguments + extra_args, 0, NULL)) {
-    CHECK_ERROR_HANDLE(constructor.VerifyEntryPoint());
+    CHECK_ERROR_HANDLE(constructor.VerifyCallEntryPoint());
     // Create the argument list.
     // Constructors get the uninitialized object.
     if (!type_arguments.IsNull()) {
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index 34d59ab..f214e56 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -572,7 +572,9 @@
   Instance& constant = Instance::Handle(Z);
   String& uri_path = String::Handle(Z);
   Library& library = Library::Handle(Z);
+#if !defined(DART_PRECOMPILER)
   Object& result = Object::Handle(Z);
+#endif
 
   for (intptr_t i = 0; i < length; ++i) {
     library ^= potential_extension_libraries_.At(i);
@@ -603,6 +605,7 @@
 
       if (uri_path.IsNull()) continue;
 
+#if !defined(DART_PRECOMPILER)
       if (!I->HasTagHandler()) {
         H.ReportError("no library handler registered.");
       }
@@ -614,6 +617,7 @@
       if (result.IsError()) {
         H.ReportError(Error::Cast(result), "library handler failed");
       }
+#endif
 
       // Create a dummy library and add it as an import to the current library.
       // This allows later to discover and reload this native extension, e.g.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 318379e..1105288 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -3294,7 +3294,7 @@
         Function::Handle(zone, LookupStaticFunction(internal_getter_name));
 
     if (field.IsNull() && !getter.IsNull() && check_is_entrypoint) {
-      CHECK_ERROR(getter.VerifyEntryPoint());
+      CHECK_ERROR(getter.VerifyCallEntryPoint());
     }
 
     if (getter.IsNull() || (respect_reflectable && !getter.is_reflectable())) {
@@ -3302,7 +3302,7 @@
         getter = LookupStaticFunction(getter_name);
         if (!getter.IsNull()) {
           if (check_is_entrypoint) {
-            CHECK_ERROR(EntryPointClosurizationError(getter_name));
+            CHECK_ERROR(getter.VerifyClosurizedEntryPoint());
           }
           if (getter.SafeToClosurize()) {
             // Looking for a getter but found a regular method: closurize it.
@@ -3360,7 +3360,7 @@
     const Function& setter =
         Function::Handle(zone, LookupStaticFunction(internal_setter_name));
     if (!setter.IsNull() && check_is_entrypoint) {
-      CHECK_ERROR(setter.VerifyEntryPoint());
+      CHECK_ERROR(setter.VerifyCallEntryPoint());
     }
     const int kNumArgs = 1;
     const Array& args = Array::Handle(zone, Array::New(kNumArgs));
@@ -3422,7 +3422,7 @@
       Function::Handle(zone, LookupStaticFunction(function_name));
 
   if (!function.IsNull() && check_is_entrypoint) {
-    CHECK_ERROR(function.VerifyEntryPoint());
+    CHECK_ERROR(function.VerifyCallEntryPoint());
   }
 
   if (function.IsNull()) {
@@ -3432,7 +3432,7 @@
                            check_is_entrypoint));
     if (getter_result.raw() != Object::sentinel().raw()) {
       if (check_is_entrypoint) {
-        CHECK_ERROR(EntryPointClosurizationError(function_name));
+        CHECK_ERROR(EntryPointFieldInvocationError(function_name));
       }
       // Make room for the closure (receiver) in the argument list.
       const intptr_t num_args = args.Length();
@@ -10612,7 +10612,35 @@
 }
 
 void Library::DropDependenciesAndCaches() const {
-  StorePointer(&raw_ptr()->imports_, Object::empty_array().raw());
+  // We need to preserve the "dart-ext:" imports because they are used by
+  // Loader::ReloadNativeExtensions().
+  intptr_t native_import_count = 0;
+  Array& imports = Array::Handle(raw_ptr()->imports_);
+  Namespace& ns = Namespace::Handle();
+  Library& lib = Library::Handle();
+  String& url = String::Handle();
+  for (int i = 0; i < imports.Length(); ++i) {
+    ns = Namespace::RawCast(imports.At(i));
+    if (ns.IsNull()) continue;
+    lib = ns.library();
+    url = lib.url();
+    if (url.StartsWith(Symbols::DartExtensionScheme())) {
+      native_import_count++;
+    }
+  }
+  Array& new_imports =
+      Array::Handle(Array::New(native_import_count, Heap::kOld));
+  for (int i = 0, j = 0; i < imports.Length(); ++i) {
+    ns = Namespace::RawCast(imports.At(i));
+    if (ns.IsNull()) continue;
+    lib = ns.library();
+    url = lib.url();
+    if (url.StartsWith(Symbols::DartExtensionScheme())) {
+      new_imports.SetAt(j++, ns);
+    }
+  }
+
+  StorePointer(&raw_ptr()->imports_, new_imports.raw());
   StorePointer(&raw_ptr()->exports_, Object::empty_array().raw());
   StoreNonPointer(&raw_ptr()->num_imports_, 0);
   StorePointer(&raw_ptr()->resolved_names_, Array::null());
@@ -10831,7 +10859,7 @@
     if (obj.IsFunction()) {
       getter = Function::Cast(obj).raw();
       if (check_is_entrypoint) {
-        CHECK_ERROR(getter.VerifyEntryPoint());
+        CHECK_ERROR(getter.VerifyCallEntryPoint());
       }
     } else {
       obj = LookupLocalOrReExportObject(getter_name);
@@ -10841,7 +10869,7 @@
       if (obj.IsFunction() && check_is_entrypoint) {
         if (!getter_name.Equals(String::Handle(String::New("main"))) ||
             raw() != Isolate::Current()->object_store()->root_library()) {
-          CHECK_ERROR(EntryPointClosurizationError(getter_name));
+          CHECK_ERROR(Function::Cast(obj).VerifyClosurizedEntryPoint());
         }
       }
       if (obj.IsFunction() && Function::Cast(obj).SafeToClosurize()) {
@@ -10912,7 +10940,7 @@
   }
 
   if (!setter.IsNull() && check_is_entrypoint) {
-    CHECK_ERROR(setter.VerifyEntryPoint());
+    CHECK_ERROR(setter.VerifyCallEntryPoint());
   }
 
   const int kNumArgs = 1;
@@ -10950,7 +10978,7 @@
   }
 
   if (!function.IsNull() && check_is_entrypoint) {
-    CHECK_ERROR(function.VerifyEntryPoint());
+    CHECK_ERROR(function.VerifyCallEntryPoint());
   }
 
   if (function.IsNull()) {
@@ -10959,7 +10987,7 @@
         function_name, false, respect_reflectable, check_is_entrypoint));
     if (getter_result.raw() != Object::sentinel().raw()) {
       if (check_is_entrypoint) {
-        CHECK_ERROR(EntryPointClosurizationError(function_name));
+        CHECK_ERROR(EntryPointFieldInvocationError(function_name));
       }
       // Make room for the closure (receiver) in arguments.
       intptr_t numArgs = args.Length();
@@ -15682,7 +15710,7 @@
   Function& function = Function::Handle(
       zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name));
 
-  if (check_is_entrypoint) {
+  if (!function.IsNull() && check_is_entrypoint) {
     // The getter must correspond to either an entry-point field or a getter
     // method explicitly marked.
     Field& field = Field::Handle(zone);
@@ -15691,8 +15719,8 @@
     }
     if (!field.IsNull()) {
       CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly));
-    } else if (!function.IsNull()) {
-      CHECK_ERROR(function.VerifyEntryPoint());
+    } else {
+      CHECK_ERROR(function.VerifyCallEntryPoint());
     }
   }
 
@@ -15701,7 +15729,7 @@
     function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
 
     if (!function.IsNull() && check_is_entrypoint) {
-      CHECK_ERROR(EntryPointClosurizationError(getter_name));
+      CHECK_ERROR(function.VerifyClosurizedEntryPoint());
     }
 
     if (!function.IsNull() && function.SafeToClosurize()) {
@@ -15750,7 +15778,7 @@
     if (!field.IsNull()) {
       CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly));
     } else if (!setter.IsNull()) {
-      CHECK_ERROR(setter.VerifyEntryPoint());
+      CHECK_ERROR(setter.VerifyCallEntryPoint());
     }
   }
 
@@ -15778,7 +15806,7 @@
       zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name));
 
   if (!function.IsNull() && check_is_entrypoint) {
-    CHECK_ERROR(function.VerifyEntryPoint());
+    CHECK_ERROR(function.VerifyCallEntryPoint());
   }
 
   // TODO(regis): Support invocation of generic functions with type arguments.
@@ -15798,7 +15826,7 @@
     function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name);
     if (!function.IsNull()) {
       if (check_is_entrypoint) {
-        CHECK_ERROR(EntryPointClosurizationError(function_name));
+        CHECK_ERROR(EntryPointFieldInvocationError(function_name));
       }
       ASSERT(function.kind() != RawFunction::kMethodExtractor);
       // Invoke the getter.
@@ -21681,15 +21709,19 @@
     if (pragma->raw() == Symbols::Set().raw()) {
       return EntryPointPragma::kSetterOnly;
     }
+    if (pragma->raw() == Symbols::Call().raw()) {
+      return EntryPointPragma::kCallOnly;
+    }
   }
   return EntryPointPragma::kNever;
 }
 
 DART_WARN_UNUSED_RESULT
-RawError* VerifyEntryPoint(const Library& lib,
-                           const Object& member,
-                           const Object& annotated,
-                           EntryPointPragma kind) {
+RawError* VerifyEntryPoint(
+    const Library& lib,
+    const Object& member,
+    const Object& annotated,
+    std::initializer_list<EntryPointPragma> allowed_kinds) {
 #if defined(DART_PRECOMPILED_RUNTIME)
   // Annotations are discarded in the AOT snapshot, so we can't determine
   // precisely if this member was marked as an entry-point. Instead, we use
@@ -21713,13 +21745,23 @@
   EntryPointPragma pragma =
       FindEntryPointPragma(Isolate::Current(), Array::Cast(metadata),
                            &Field::Handle(), &Object::Handle());
-  const bool is_marked_entrypoint =
-      pragma == kind || pragma == EntryPointPragma::kAlways;
+  bool is_marked_entrypoint = pragma == EntryPointPragma::kAlways;
+  if (!is_marked_entrypoint) {
+    for (const auto allowed_kind : allowed_kinds) {
+      if (pragma == allowed_kind) {
+        is_marked_entrypoint = true;
+        break;
+      }
+    }
+  }
 #endif
   if (!is_marked_entrypoint) {
     const char* member_cstring =
         member.IsFunction()
-            ? Function::Cast(member).ToLibNamePrefixedQualifiedCString()
+            ? OS::SCreate(
+                  Thread::Current()->zone(), "%s (kind %s)",
+                  Function::Cast(member).ToLibNamePrefixedQualifiedCString(),
+                  Function::KindToCString(Function::Cast(member).kind()))
             : member.ToCString();
     char const* error = OS::SCreate(
         Thread::Current()->zone(),
@@ -21735,12 +21777,12 @@
 }
 
 DART_WARN_UNUSED_RESULT
-RawError* EntryPointClosurizationError(const String& getter_name) {
+RawError* EntryPointFieldInvocationError(const String& getter_name) {
   if (!FLAG_verify_entry_points) return Error::null();
 
   char const* error = OS::SCreate(
       Thread::Current()->zone(),
-      "ERROR: Entry-points do not allow closurizing methods "
+      "ERROR: Entry-points do not allow invoking fields "
       "(failure to resolve '%s')\n"
       "ERROR: See "
       "https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/"
@@ -21750,50 +21792,67 @@
   return ApiError::New(String::Handle(String::New(error)));
 }
 
-RawError* Function::VerifyEntryPoint() const {
+RawError* Function::VerifyCallEntryPoint() const {
   if (!FLAG_verify_entry_points) return Error::null();
 
   const Class& cls = Class::Handle(Owner());
   const Library& lib = Library::Handle(cls.library());
   switch (kind()) {
     case RawFunction::kRegularFunction:
-    case RawFunction::kGetterFunction:
     case RawFunction::kSetterFunction:
     case RawFunction::kConstructor:
       return dart::VerifyEntryPoint(lib, *this, *this,
-                                    EntryPointPragma::kAlways);
+                                    {EntryPointPragma::kCallOnly});
       break;
-    case RawFunction::kImplicitGetter: {
-      const Field& accessed = Field::Handle(accessor_field());
-      return dart::VerifyEntryPoint(lib, *this, accessed,
-                                    EntryPointPragma::kGetterOnly);
+    case RawFunction::kGetterFunction:
+      return dart::VerifyEntryPoint(
+          lib, *this, *this,
+          {EntryPointPragma::kCallOnly, EntryPointPragma::kGetterOnly});
       break;
-    }
-    case RawFunction::kImplicitSetter: {
-      const Field& accessed = Field::Handle(accessor_field());
-      return dart::VerifyEntryPoint(lib, *this, accessed,
-                                    EntryPointPragma::kSetterOnly);
+    case RawFunction::kImplicitGetter:
+      return dart::VerifyEntryPoint(lib, *this, Field::Handle(accessor_field()),
+                                    {EntryPointPragma::kGetterOnly});
       break;
-    }
+    case RawFunction::kImplicitSetter:
+      return dart::VerifyEntryPoint(lib, *this, Field::Handle(accessor_field()),
+                                    {EntryPointPragma::kSetterOnly});
+    case RawFunction::kMethodExtractor:
+      return Function::Handle(extracted_method_closure())
+          .VerifyClosurizedEntryPoint();
+      break;
     default:
-      return dart::VerifyEntryPoint(lib, *this, Object::Handle(),
-                                    EntryPointPragma::kAlways);
+      return dart::VerifyEntryPoint(lib, *this, Object::Handle(), {});
       break;
   }
 }
 
+RawError* Function::VerifyClosurizedEntryPoint() const {
+  if (!FLAG_verify_entry_points) return Error::null();
+
+  const Class& cls = Class::Handle(Owner());
+  const Library& lib = Library::Handle(cls.library());
+  switch (kind()) {
+    case RawFunction::kRegularFunction:
+    case RawFunction::kImplicitClosureFunction:
+      return dart::VerifyEntryPoint(lib, *this, *this,
+                                    {EntryPointPragma::kGetterOnly});
+    default:
+      UNREACHABLE();
+  }
+}
+
 RawError* Field::VerifyEntryPoint(EntryPointPragma pragma) const {
   if (!FLAG_verify_entry_points) return Error::null();
   const Class& cls = Class::Handle(Owner());
   const Library& lib = Library::Handle(cls.library());
-  return dart::VerifyEntryPoint(lib, *this, *this, pragma);
+  return dart::VerifyEntryPoint(lib, *this, *this, {pragma});
 }
 
 RawError* Class::VerifyEntryPoint() const {
   if (!FLAG_verify_entry_points) return Error::null();
   const Library& lib = Library::Handle(library());
   if (!lib.IsNull()) {
-    return dart::VerifyEntryPoint(lib, *this, *this, EntryPointPragma::kAlways);
+    return dart::VerifyEntryPoint(lib, *this, *this, {});
   } else {
     return Error::null();
   }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index e426c9d0..b269010 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -2606,7 +2606,10 @@
   }
 
   DART_WARN_UNUSED_RESULT
-  RawError* VerifyEntryPoint() const;
+  RawError* VerifyCallEntryPoint() const;
+
+  DART_WARN_UNUSED_RESULT
+  RawError* VerifyClosurizedEntryPoint() const;
 
   static intptr_t InstanceSize() {
     return RoundedAllocationSize(sizeof(RawFunction));
@@ -2970,7 +2973,13 @@
   friend class HeapProfiler;
 };
 
-enum class EntryPointPragma { kAlways, kNever, kGetterOnly, kSetterOnly };
+enum class EntryPointPragma {
+  kAlways,
+  kNever,
+  kGetterOnly,
+  kSetterOnly,
+  kCallOnly
+};
 
 class FfiTrampolineData : public Object {
  public:
@@ -9414,7 +9423,7 @@
                                       Object* reusable_object_handle);
 
 DART_WARN_UNUSED_RESULT
-RawError* EntryPointClosurizationError(const String& getter_name);
+RawError* EntryPointFieldInvocationError(const String& getter_name);
 
 }  // namespace dart
 
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 3fecf1c..f1c49d8 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -907,7 +907,8 @@
   RawArray* parameter_types_;
   RawArray* parameter_names_;
   RawTypeArguments* type_parameters_;  // Array of TypeParameter.
-  RawObject* data_;  // Additional data specific to the function kind.
+  RawObject* data_;  // Additional data specific to the function kind. See
+                     // Function::set_data() for details.
   RawObject** to_snapshot(Snapshot::Kind kind) {
     switch (kind) {
       case Snapshot::kFullAOT:
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 68c0371..662a052 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -368,6 +368,7 @@
   V(Index, "index")                                                            \
   V(DartScheme, "dart:")                                                       \
   V(DartSchemePrivate, "dart:_")                                               \
+  V(DartExtensionScheme, "dart-ext:")                                          \
   V(DartInternalPackage, "package:dart_internal/")                             \
   V(DartNativeWrappers, "dart:nativewrappers")                                 \
   V(DartNativeWrappersLibName, "nativewrappers")                               \
diff --git a/tests/standalone_2/io/entrypoints_verification_test.dart b/tests/standalone_2/io/entrypoints_verification_test.dart
index cf12ac1..048f1f3 100644
--- a/tests/standalone_2/io/entrypoints_verification_test.dart
+++ b/tests/standalone_2/io/entrypoints_verification_test.dart
@@ -37,11 +37,23 @@
   @pragma("vm:entry-point")
   void fn1() {}
 
+  @pragma("vm:entry-point", "get")
+  void fn1_get() {}
+
+  @pragma("vm:entry-point", "call")
+  void fn1_call() {}
+
   static void fn2() {}
 
   @pragma("vm:entry-point")
   static void fn3() {}
 
+  @pragma("vm:entry-point", "call")
+  static void fn3_call() {}
+
+  @pragma("vm:entry-point", "get")
+  static void fn3_get() {}
+
   void Function() fld0;
 
   @pragma("vm:entry-point")
@@ -59,6 +71,12 @@
 @pragma("vm:entry-point")
 void fn1() {}
 
+@pragma("vm:entry-point", "get")
+void fn1_get() {}
+
+@pragma("vm:entry-point", "call")
+void fn1_call() {}
+
 class E extends D {
   E.ctor();
 }
diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status
index 71c00fc..4423aa7 100644
--- a/tests/standalone_2/standalone_2_kernel.status
+++ b/tests/standalone_2/standalone_2_kernel.status
@@ -225,5 +225,5 @@
 io/web_socket_compression_test: Skip # Timeout
 io/web_socket_test: Skip # Timeout
 
-[ $hot_reload || $hot_reload_rollback || $compiler != dartk && $compiler != dartkb ]
-io/entrypoints_verification_test: Skip # Test runs in JIT mode only
+[ $hot_reload || $hot_reload_rollback || $compiler != dartk && $compiler != dartkb && $compiler != dartkp || $compiler == dartkp && $system == windows ]
+io/entrypoints_verification_test: Skip # Cannot run in precompiled Windows because the DLL is linked against dart.exe instead of dart_precompiled_runtime.exe.