[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.