diff --git a/runtime/bin/entrypoints_verification_test.cc b/runtime/bin/entrypoints_verification_test.cc index 869dfd6616b..dc58dc222d6 100644 --- a/runtime/bin/entrypoints_verification_test.cc +++ b/runtime/bin/entrypoints_verification_test.cc @@ -2,9 +2,9 @@ // 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. -#include -#include -#include +#include +#include +#include // TODO(dartbug.com/40579): This requires static linking to either link // dart.exe or dart_precompiled_runtime.exe on Windows. @@ -12,99 +12,179 @@ #include "include/dart_api.h" #include "include/dart_native_api.h" +static bool is_dart_precompiled_runtime = true; + +bool IsTreeShaken(const char* name, Dart_Handle handle, const char* error) { + // No tree shaking in the JIT runtime. + if (!is_dart_precompiled_runtime) return false; + if (Dart_IsApiError(handle)) { + // All tree-shaking related API errors should include the expected name. + if (strstr(error, name) == nullptr) return false; + // Node was tree shaken (e.g., 'Class C not found in library...'). + if (strstr(error, " not found in ") != nullptr) return true; + // Constructor was tree shaken. + if (strstr(error, "Dart_New: could not find ") != nullptr) return true; + } else if (Dart_IsUnhandledExceptionError(handle)) { + // TFA replaces operations with a throw in some cases. If obfuscation + // is turned on and the member has no entry point annotation, then its + // name may be obfuscated in the result and so cannot be depended on. + if (strstr(error, "Attempt to execute code removed by ") != nullptr) { + return true; + } + // All other tree-shaking related unhandled exceptions are NSM errors + // that should include the expected name. + if (strstr(error, name) == nullptr) return false; + if (strstr(error, "NoSuchMethodError: ") == nullptr) { + return false; + } + if (strstr(error, "No top-level method '") != nullptr) return true; + if (strstr(error, "No top-level getter '") != nullptr) return true; + if (strstr(error, "No top-level setter '") != nullptr) return true; + if (strstr(error, "No static method '") != nullptr) return true; + if (strstr(error, "No static getter '") != nullptr) return true; + if (strstr(error, "No static setter '") != nullptr) return true; + if (strstr(error, "' has no instance method '") != nullptr) return true; + if (strstr(error, "' has no instance getter '") != nullptr) return true; + if (strstr(error, "' has no instance setter '") != nullptr) return true; + } + // Not an tree shaking-related error. + return false; +} + +#if defined(_MSC_VER) +#define FATAL(fmt, ...) \ + do { \ + fprintf(stderr, "Failed at %s:%d: " fmt "!\n", __FILE__, __LINE__, \ + __VA_ARGS__); \ + abort(); \ + } while (false) +#else +#define FATAL(fmt, ...) \ + do { \ + fprintf(stderr, "Failed at %s:%d: " fmt "!\n", __FILE__, __LINE__, \ + ##__VA_ARGS__); \ + abort(); \ + } while (false) +#endif + #define CHECK(H) \ do { \ + fprintf(stderr, "Checking %s...\n", #H); \ Dart_Handle __handle__ = H; \ if (Dart_IsError(__handle__)) { \ const char* message = Dart_GetError(__handle__); \ - fprintf(stderr, "Check \"" #H "\" failed: %s", message); \ - abort(); \ + FATAL("\n%s", message); \ + } else { \ + fprintf(stderr, " Check passed.\n\n"); \ } \ } while (false) -#define ASSERT(E) \ - if (!(E)) { \ - fprintf(stderr, "Assertion \"" #E "\" failed at %s:%d!\n", __FILE__, \ - __LINE__); \ - abort(); \ - } +#define ASSERT_SUBSTRING(needle, haystack) \ + do { \ + if (strstr(haystack, needle) == nullptr) { \ + FATAL("expected '%s' within:\n%s\n", needle, haystack); \ + } \ + } while (false) -static bool is_dart_precompiled_runtime = true; +#define FAIL(name, result) \ + do { \ + fprintf(stderr, "Expect failure to access '%s'\n", name); \ + if (!Dart_IsError(result)) { \ + FATAL("No error for accessing %s", name); \ + } \ + const char* error = Dart_GetError(result); \ + if (IsTreeShaken(name, result, error)) { \ + fprintf(stderr, " Received error due to tree shaking: %s\n\n", error); \ + } else if (!Dart_IsApiError(result)) { \ + FATAL("Not an API error for accessing %s: %s", name, error); \ + } else { \ + ASSERT_SUBSTRING(name, error); \ + ASSERT_SUBSTRING("ERROR: ", error); \ + ASSERT_SUBSTRING("' from native code, it must be annotated.", error); \ + fprintf(stderr, " Received expected error: %s\n\n", error); \ + } \ + } while (false) // 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 (!is_dart_precompiled_runtime) { \ - Fail(name, result); \ - } +// annotations. Only use this if there's no other way to detect missing +// annotations, e.g., a function with no code signals that the precompiler +// did not preserve the code object because the function is not annotated +// as a "call" entry point. +#define FAIL_UNLESS_PRECOMPILED(name, result) \ + do { \ + if (!is_dart_precompiled_runtime) { \ + FAIL(name, result); \ + } else { \ + CHECK(result); \ + } \ + } while (false); -void Fail(const char* name, Dart_Handle result) { - ASSERT(Dart_IsApiError(result)); - const char* error = Dart_GetError(result); - ASSERT(strstr(error, name)); - ASSERT(strstr(error, "It is illegal to access")); -} +#define FAIL_CLOSURIZE_CONSTRUCTOR(name, result) \ + do { \ + fprintf(stderr, "Expect failure to closurize constructor '%s'\n", name); \ + if (!Dart_IsError(result)) { \ + FATAL("No error for closurizing %s", name); \ + } \ + const char* error = Dart_GetError(result); \ + if (!Dart_IsUnhandledExceptionError(result)) { \ + FATAL("Not an unhandled exception error for closurizing %s: %s", name, \ + error); \ + } else { \ + ASSERT_SUBSTRING(name, error); \ + ASSERT_SUBSTRING("No static getter", error); \ + fprintf(stderr, " Received expected error: %s\n\n", error); \ + } \ + } while (false) -#define FAIL_INVOKE_FIELD(name, result) \ - if (!is_dart_precompiled_runtime) { \ - FailInvokeField(name, result); \ - } - -static 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 invoking fields")); -} - -static 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")); -} - -static void TestFields(Dart_Handle target) { - FAIL("fld0", Dart_GetField(target, Dart_NewStringFromCString("fld0"))); - FAIL("fld0", - Dart_SetField(target, Dart_NewStringFromCString("fld0"), Dart_Null())); - - 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())); - FAIL_INVOKE_FIELD( - "fld1", - Dart_Invoke(target, Dart_NewStringFromCString("fld1"), 0, nullptr)); - - CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld2"))); - FAIL("fld2", - Dart_SetField(target, Dart_NewStringFromCString("fld2"), Dart_Null())); - FAIL_INVOKE_FIELD( - "fld2", - Dart_Invoke(target, Dart_NewStringFromCString("fld2"), 0, nullptr)); - - FAIL("fld3", Dart_GetField(target, Dart_NewStringFromCString("fld3"))); - CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld3"), Dart_Null())); - FAIL_INVOKE_FIELD( - "fld3", - Dart_Invoke(target, Dart_NewStringFromCString("fld3"), 0, nullptr)); -} +#define TEST_FIELDS(target) \ + do { \ + /* Since the fields start off initialized to a non-null value and then */ \ + /* are updated with a null value, check invocation prior to set. */ \ + FAIL("fld0", Dart_GetField(target, Dart_NewStringFromCString("fld0"))); \ + FAIL("fld0", \ + Dart_Invoke(target, Dart_NewStringFromCString("fld0"), 0, nullptr)); \ + FAIL("fld0", Dart_SetField(target, Dart_NewStringFromCString("fld0"), \ + Dart_Null())); \ + CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld1"))); \ + CHECK(Dart_Invoke(target, Dart_NewStringFromCString("fld1"), 0, nullptr)); \ + CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld1"), \ + Dart_Null())); \ + CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld2"))); \ + CHECK(Dart_Invoke(target, Dart_NewStringFromCString("fld2"), 0, nullptr)); \ + if (Dart_IsLibrary(target) || Dart_IsType(target)) { \ + /* There are no implicit setters for static fields, so the pragma */ \ + /* must be checked; in precompiled mode, that means a false positive. */ \ + FAIL_UNLESS_PRECOMPILED( \ + "fld2", Dart_SetField(target, Dart_NewStringFromCString("fld2"), \ + Dart_Null())); \ + } else { \ + FAIL("fld2", Dart_SetField(target, Dart_NewStringFromCString("fld2"), \ + Dart_Null())); \ + } \ + FAIL("fld3", Dart_GetField(target, Dart_NewStringFromCString("fld3"))); \ + FAIL("fld3", \ + Dart_Invoke(target, Dart_NewStringFromCString("fld3"), 0, nullptr)); \ + CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld3"), \ + Dart_Null())); \ + } while (false) DART_EXPORT void RunTests() { is_dart_precompiled_runtime = Dart_IsPrecompiledRuntime(); Dart_Handle lib = Dart_RootLibrary(); - //////// Test allocation and constructor invocation. + //////// Test class access. FAIL("C", Dart_GetClass(lib, Dart_NewStringFromCString("C"))); Dart_Handle D_class = Dart_GetClass(lib, Dart_NewStringFromCString("D")); CHECK(D_class); + Dart_Handle F_class = Dart_GetClass(lib, Dart_NewStringFromCString("F")); + CHECK(F_class); + + //////// Test allocation and constructor invocation. + CHECK(Dart_Allocate(D_class)); FAIL("D.", Dart_New(D_class, Dart_Null(), 0, nullptr)); @@ -116,30 +196,39 @@ DART_EXPORT void RunTests() { //////// Test actions against methods - FailClosurizeConstructor( + fprintf(stderr, "\n\nTesting methods with library target\n\n\n"); + + FAIL("noop", Dart_Invoke(lib, Dart_NewStringFromCString("noop"), 0, nullptr)); + + FAIL("fn0", Dart_Invoke(lib, Dart_NewStringFromCString("fn0"), 0, nullptr)); + + CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1"), 0, nullptr)); + FAIL("fn1_get", + Dart_Invoke(lib, Dart_NewStringFromCString("fn1_get"), 0, nullptr)); + CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1_call"), 0, nullptr)); + + FAIL("fn0", Dart_GetField(lib, Dart_NewStringFromCString("fn0"))); + + CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1"))); + CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1_get"))); + FAIL("fn1_call", Dart_GetField(lib, Dart_NewStringFromCString("fn1_call"))); + + fprintf(stderr, "\n\nTesting methods with class target\n\n\n"); + + FAIL_CLOSURIZE_CONSTRUCTOR( "defined", Dart_GetField(D_class, Dart_NewStringFromCString("defined"))); - FailClosurizeConstructor( + FAIL_CLOSURIZE_CONSTRUCTOR( "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("fn0", Dart_GetField(D, Dart_NewStringFromCString("fn0"))); - - 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", 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", + FAIL("fn3_get", Dart_Invoke(D_class, Dart_NewStringFromCString("fn3_get"), 0, nullptr)); FAIL("fn2", Dart_GetField(D_class, Dart_NewStringFromCString("fn2"))); @@ -149,25 +238,27 @@ DART_EXPORT void RunTests() { 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)); + fprintf(stderr, "\n\nTesting methods with instance target\n\n\n"); - 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)); + CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1"), 0, nullptr)); + FAIL("fn1_get", + Dart_Invoke(D, Dart_NewStringFromCString("fn1_get"), 0, nullptr)); + CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1_call"), 0, nullptr)); - FAIL("fn0", Dart_GetField(lib, Dart_NewStringFromCString("fn0"))); + FAIL("fn0", Dart_GetField(D, Dart_NewStringFromCString("fn0"))); - CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1"))); - CHECK(Dart_GetField(lib, Dart_NewStringFromCString("fn1_get"))); - FAIL("fn1", Dart_GetField(lib, Dart_NewStringFromCString("fn1_call"))); + CHECK(Dart_GetField(D, Dart_NewStringFromCString("fn1"))); + CHECK(Dart_GetField(D, Dart_NewStringFromCString("fn1_get"))); + FAIL("fn1_call", Dart_GetField(D, Dart_NewStringFromCString("fn1_call"))); //////// Test actions against fields - TestFields(D); + fprintf(stderr, "\n\nTesting fields with library target\n\n\n"); + TEST_FIELDS(lib); - Dart_Handle F_class = Dart_GetClass(lib, Dart_NewStringFromCString("F")); - TestFields(F_class); + fprintf(stderr, "\n\nTesting fields with class target\n\n\n"); + TEST_FIELDS(F_class); - TestFields(lib); + fprintf(stderr, "\n\nTesting fields with instance target\n\n\n"); + TEST_FIELDS(D); } diff --git a/runtime/docs/compiler/aot/entry_point_pragma.md b/runtime/docs/compiler/aot/entry_point_pragma.md index fc68848a9d8..e0317579726 100644 --- a/runtime/docs/compiler/aot/entry_point_pragma.md +++ b/runtime/docs/compiler/aot/entry_point_pragma.md @@ -4,6 +4,10 @@ The annotation `@pragma("vm:entry-point", ...)` **must** be placed on a class or member to indicate that it may be resolved, allocated or invoked directly from native or VM code _in AOT mode_. +To reduce the differences between JIT and AOT mode, entry point annotations are +also checked in JIT mode except for uses of the `dart:mirrors` library and +debugging uses via the VM service. + ## Background Dart VM precompiler (AOT compiler) performs whole-program optimizations such as @@ -88,11 +92,14 @@ three forms may be attached to static fields. int foo; ``` -If the second parameter is missing, `null` or `true, the field is marked for +If the second parameter is missing, `null` or `true`, the field is marked for native access and for non-static fields the corresponding getter and setter in the interface of the enclosing class are marked for native invocation. If the -'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. +"get" or "set" parameter is used, only the getter or setter is marked. For +static fields, the implicit getter is always marked if the field is marked +for native access. -Note that no form of entry-point annotation allows invoking a field. +A field containing a closure may only be invoked using Dart_Invoke if the +getter is marked, in which case it is the same as retrieving the closure from +the field using Dart_GetField and then invoking the closure using +Dart_InvokeClosure. diff --git a/runtime/lib/isolate.cc b/runtime/lib/isolate.cc index 9ced70beded..7231276cbe6 100644 --- a/runtime/lib/isolate.cc +++ b/runtime/lib/isolate.cc @@ -714,7 +714,7 @@ ObjectPtr IsolateSpawnState::ResolveFunction() { // Check whether the root library defines a main function. const Library& lib = Library::Handle(zone, IG->object_store()->root_library()); - const String& main = String::Handle(zone, String::New("main")); + const String& main = Symbols::main(); Function& func = Function::Handle(zone, lib.LookupFunctionAllowPrivate(main)); if (func.IsNull()) { // Check whether main is reexported from the root library. diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc index 8525f68d5cd..17a485afe7e 100644 --- a/runtime/lib/mirrors.cc +++ b/runtime/lib/mirrors.cc @@ -1221,6 +1221,8 @@ DEFINE_NATIVE_ENTRY(TypeVariableMirror_upper_bound, 0, 1) { return param.bound(); } +static constexpr bool kNoStrictEntryPointChecks = false; + DEFINE_NATIVE_ENTRY(InstanceMirror_invoke, 0, 5) { // Argument 0 is the mirror, which is unused by the native. It exists // because this native is an instance method in order to be polymorphic @@ -1230,7 +1232,8 @@ DEFINE_NATIVE_ENTRY(InstanceMirror_invoke, 0, 5) { arguments->NativeArgAt(2)); GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3)); GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4)); - RETURN_OR_PROPAGATE(reflectee.Invoke(function_name, args, arg_names)); + RETURN_OR_PROPAGATE(reflectee.Invoke(function_name, args, arg_names, + kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(InstanceMirror_invokeGetter, 0, 3) { @@ -1239,7 +1242,8 @@ DEFINE_NATIVE_ENTRY(InstanceMirror_invokeGetter, 0, 3) { // with its cousins. GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1)); GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2)); - RETURN_OR_PROPAGATE(reflectee.InvokeGetter(getter_name)); + RETURN_OR_PROPAGATE( + reflectee.InvokeGetter(getter_name, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(InstanceMirror_invokeSetter, 0, 4) { @@ -1249,7 +1253,8 @@ DEFINE_NATIVE_ENTRY(InstanceMirror_invokeSetter, 0, 4) { GET_NATIVE_ARGUMENT(Instance, reflectee, arguments->NativeArgAt(1)); GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2)); GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3)); - RETURN_OR_PROPAGATE(reflectee.InvokeSetter(setter_name, value)); + RETURN_OR_PROPAGATE( + reflectee.InvokeSetter(setter_name, value, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(InstanceMirror_computeType, 0, 1) { @@ -1309,7 +1314,8 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invoke, 0, 5) { arguments->NativeArgAt(2)); GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3)); GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4)); - RETURN_OR_PROPAGATE(klass.Invoke(function_name, args, arg_names)); + RETURN_OR_PROPAGATE( + klass.Invoke(function_name, args, arg_names, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(ClassMirror_invokeGetter, 0, 3) { @@ -1324,7 +1330,8 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invokeGetter, 0, 3) { UNREACHABLE(); } GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2)); - RETURN_OR_PROPAGATE(klass.InvokeGetter(getter_name, true)); + RETURN_OR_PROPAGATE( + klass.InvokeGetter(getter_name, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(ClassMirror_invokeSetter, 0, 4) { @@ -1335,7 +1342,8 @@ DEFINE_NATIVE_ENTRY(ClassMirror_invokeSetter, 0, 4) { const Class& klass = Class::Handle(ref.GetClassReferent()); GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2)); GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3)); - RETURN_OR_PROPAGATE(klass.InvokeSetter(setter_name, value)); + RETURN_OR_PROPAGATE( + klass.InvokeSetter(setter_name, value, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(ClassMirror_invokeConstructor, 0, 5) { @@ -1488,7 +1496,8 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_invoke, 0, 5) { arguments->NativeArgAt(2)); GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(3)); GET_NON_NULL_NATIVE_ARGUMENT(Array, arg_names, arguments->NativeArgAt(4)); - RETURN_OR_PROPAGATE(library.Invoke(function_name, args, arg_names)); + RETURN_OR_PROPAGATE(library.Invoke(function_name, args, arg_names, + kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(LibraryMirror_invokeGetter, 0, 3) { @@ -1498,7 +1507,8 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_invokeGetter, 0, 3) { GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(1)); const Library& library = Library::Handle(ref.GetLibraryReferent()); GET_NON_NULL_NATIVE_ARGUMENT(String, getter_name, arguments->NativeArgAt(2)); - RETURN_OR_PROPAGATE(library.InvokeGetter(getter_name, true)); + RETURN_OR_PROPAGATE( + library.InvokeGetter(getter_name, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(LibraryMirror_invokeSetter, 0, 4) { @@ -1509,7 +1519,8 @@ DEFINE_NATIVE_ENTRY(LibraryMirror_invokeSetter, 0, 4) { const Library& library = Library::Handle(ref.GetLibraryReferent()); GET_NON_NULL_NATIVE_ARGUMENT(String, setter_name, arguments->NativeArgAt(2)); GET_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(3)); - RETURN_OR_PROPAGATE(library.InvokeSetter(setter_name, value)); + RETURN_OR_PROPAGATE( + library.InvokeSetter(setter_name, value, kNoStrictEntryPointChecks)); } DEFINE_NATIVE_ENTRY(MethodMirror_owner, 0, 2) { diff --git a/runtime/tests/vm/dart/entrypoints_verification_test.dart b/runtime/tests/vm/dart/entrypoints_verification_test.dart index a64e3472c68..e74bbd6f4da 100644 --- a/runtime/tests/vm/dart/entrypoints_verification_test.dart +++ b/runtime/tests/vm/dart/entrypoints_verification_test.dart @@ -2,22 +2,20 @@ // 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. // -// VMOptions=--verify-entry-points=true // SharedObjects=entrypoints_verification_test import 'dart:ffi'; import './dylib_utils.dart'; -main() { +main(List args) { final helper = dlopenPlatformSpecific('entrypoints_verification_test'); final runTest = helper.lookupFunction('RunTests'); runTest(); - - new C(); - new D(); } +final void Function() noop = () {}; + class C {} @pragma("vm:entry-point") @@ -52,16 +50,16 @@ class D { @pragma("vm:entry-point", "get") static void fn3_get() {} - void Function()? fld0; + void Function()? fld0 = noop; @pragma("vm:entry-point") - void Function()? fld1; + void Function()? fld1 = noop; @pragma("vm:entry-point", "get") - void Function()? fld2; + void Function()? fld2 = noop; @pragma("vm:entry-point", "set") - void Function()? fld3; + void Function()? fld3 = noop; } void fn0() {} @@ -81,25 +79,25 @@ class E extends D { @pragma("vm:entry-point") class F { - static void Function()? fld0; + static void Function()? fld0 = noop; @pragma("vm:entry-point") - static void Function()? fld1; + static void Function()? fld1 = noop; @pragma("vm:entry-point", "get") - static void Function()? fld2; + static void Function()? fld2 = noop; @pragma("vm:entry-point", "set") - static void Function()? fld3; + static void Function()? fld3 = noop; } -void Function()? fld0; +void Function()? fld0 = noop; @pragma("vm:entry-point") -void Function()? fld1; +void Function()? fld1 = noop; @pragma("vm:entry-point", "get") -void Function()? fld2; +void Function()? fld2 = noop; @pragma("vm:entry-point", "set") -void Function()? fld3; +void Function()? fld3 = noop; diff --git a/runtime/vm/benchmark_test.cc b/runtime/vm/benchmark_test.cc index 2a6952b7770..66f007c234a 100644 --- a/runtime/vm/benchmark_test.cc +++ b/runtime/vm/benchmark_test.cc @@ -176,6 +176,7 @@ base class Class extends NativeFieldWrapperClass1 { external int method(int param1, int param2); } +@pragma('vm:entry-point', 'call') void benchmark(int count) { Class c = Class(); c.init(); @@ -336,7 +337,9 @@ BENCHMARK(FrameLookup) { return StackFrame.accessFrame(); } } + @pragma('vm:entry-point') class StackFrameTest { + @pragma('vm:entry-point', 'call') static int testMain() { First obj = new First(); return obj.method1(1); @@ -430,6 +433,7 @@ BENCHMARK(CreateMirrorSystem) { const char* kScriptChars = "import 'dart:mirrors';\n" "\n" + "@pragma('vm:entry-point', 'call')\n" "void benchmark() {\n" " currentMirrorSystem();\n" "}\n"; @@ -535,6 +539,7 @@ BENCHMARK(SimpleMessage) { BENCHMARK(LargeMap) { const char* kScript = + "@pragma('vm:entry-point', 'call')\n" "makeMap() {\n" " Map m = {};\n" " for (int i = 0; i < 100000; ++i) m[i*13+i*(i>>7)] = i;\n" diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index 61811efad53..94e90ac87f8 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -738,7 +738,7 @@ void Precompiler::AddRoots() { UNREACHABLE(); } - const String& name = String::Handle(String::New("main")); + const String& name = Symbols::main(); Function& main = Function::Handle(lib.LookupFunctionAllowPrivate(name)); if (main.IsNull()) { const Object& obj = Object::Handle(lib.LookupReExport(name)); @@ -1381,20 +1381,10 @@ const char* Precompiler::MustRetainFunction(const Function& function) { // * Native functions (for LinkNativeCall) // * Selector matches a symbol used in Resolver::ResolveDynamic calls // in dart_entry.cc or dart_api_impl.cc. - // * _Closure.call (used in async stack handling) if (function.is_old_native()) { return "native function"; } - // Use the same check for _Closure.call as in stack_trace.{h|cc}. - const auto& selector = String::Handle(Z, function.name()); - if (selector.ptr() == Symbols::call().ptr()) { - const auto& name = String::Handle(Z, function.QualifiedScrubbedName()); - if (name.Equals(Symbols::_ClosureCall())) { - return "_Closure.call"; - } - } - // We have to retain functions which can be a target of a SwitchableCall // at AOT runtime, since the AOT runtime needs to be able to find the // function object in the class. diff --git a/runtime/vm/compiler/backend/il_test_helper.cc b/runtime/vm/compiler/backend/il_test_helper.cc index 43a6867250b..212cfbfd905 100644 --- a/runtime/vm/compiler/backend/il_test_helper.cc +++ b/runtime/vm/compiler/backend/il_test_helper.cc @@ -81,12 +81,15 @@ TypeParameterPtr GetFunctionTypeParameter(const Function& fun, intptr_t index) { return param.ptr(); } -ObjectPtr Invoke(const Library& lib, const char* name) { +ObjectPtr Invoke(const Library& lib, + const char* name, + bool check_is_entrypoint) { Thread* thread = Thread::Current(); Dart_Handle api_lib = Api::NewHandle(thread, lib.ptr()); Dart_Handle result; { TransitionVMToNative transition(thread); + SetFlagScope sfs(&FLAG_verify_entry_points, check_is_entrypoint); result = Dart_Invoke(api_lib, NewString(name), /*argc=*/0, /*argv=*/nullptr); EXPECT_VALID(result); diff --git a/runtime/vm/compiler/backend/il_test_helper.h b/runtime/vm/compiler/backend/il_test_helper.h index 25f140ba70d..5bd608728d8 100644 --- a/runtime/vm/compiler/backend/il_test_helper.h +++ b/runtime/vm/compiler/backend/il_test_helper.h @@ -65,7 +65,9 @@ ClassPtr GetClass(const Library& lib, const char* name); TypeParameterPtr GetClassTypeParameter(const Class& klass, intptr_t index); TypeParameterPtr GetFunctionTypeParameter(const Function& fun, intptr_t index); -ObjectPtr Invoke(const Library& lib, const char* name); +ObjectPtr Invoke(const Library& lib, + const char* name, + bool check_is_entrypoint = false); InstructionsPtr BuildInstructions( std::function fun); diff --git a/runtime/vm/compiler/backend/inliner_test.cc b/runtime/vm/compiler/backend/inliner_test.cc index d758d064e28..93a07b674b0 100644 --- a/runtime/vm/compiler/backend/inliner_test.cc +++ b/runtime/vm/compiler/backend/inliner_test.cc @@ -580,7 +580,9 @@ external InspectStack(); @pragma('vm:never-inline') void nop() {} +@pragma('vm:entry-point', 'get') int prologueCount = 0; +@pragma('vm:entry-point', 'get') int epilogueCount = 0; @pragma('vm:never-inline') diff --git a/runtime/vm/compiler/backend/redundancy_elimination_test.cc b/runtime/vm/compiler/backend/redundancy_elimination_test.cc index 06a3efc2543..e04a92557fe 100644 --- a/runtime/vm/compiler/backend/redundancy_elimination_test.cc +++ b/runtime/vm/compiler/backend/redundancy_elimination_test.cc @@ -1940,6 +1940,7 @@ ISOLATE_UNIT_TEST_CASE(Ffi_StructSinking) { external int a; } + @pragma('vm:entry-point') int test(int addr) => Pointer.fromAddress(addr)[0].a; )"; diff --git a/runtime/vm/compiler_test.cc b/runtime/vm/compiler_test.cc index 26f62a4425d..b41522c92c1 100644 --- a/runtime/vm/compiler_test.cc +++ b/runtime/vm/compiler_test.cc @@ -176,13 +176,16 @@ ISOLATE_UNIT_TEST_CASE(RegenerateAllocStubs) { TEST_CASE(EvalExpression) { const char* kScriptChars = - "int ten = 2 * 5; \n" - "get dot => '.'; \n" - "class A { \n" - " var apa = 'Herr Nilsson'; \n" - " calc(x) => '${x*ten}'; \n" - "} \n" - "makeObj() => new A(); \n"; + R"( + int ten = 2 * 5; + get dot => '.'; + class A { + var apa = 'Herr Nilsson'; + calc(x) => '${x*ten}'; + } + @pragma('vm:entry-point', 'call') + makeObj() => new A(); + )"; Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle obj_handle = diff --git a/runtime/vm/custom_isolate_test.cc b/runtime/vm/custom_isolate_test.cc index ce51db403b4..cdec10dd936 100644 --- a/runtime/vm/custom_isolate_test.cc +++ b/runtime/vm/custom_isolate_test.cc @@ -29,6 +29,7 @@ static const char* kCustomIsolateScriptChars = import 'dart:isolate'; final RawReceivePort mainPort = new RawReceivePort(); + @pragma('vm:entry-point', 'get') final SendPort mainSendPort = mainPort.sendPort; @pragma('vm:external-name', 'native_echo') @@ -55,6 +56,7 @@ static const char* kCustomIsolateScriptChars = SendPort spawn(); } + @pragma('vm:entry-point', 'call') isolateMain() { echo('Running isolateMain'); mainPort.handler = (message) { diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index bd3b4a4f203..d109c2454fe 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -4200,8 +4200,10 @@ static ObjectPtr ResolveConstructor(const char* current_func, current_func, constr_name.ToCString(), error_message.ToCString())); return ApiError::New(message); } - ErrorPtr error = constructor.VerifyCallEntryPoint(); - if (error != Error::null()) return error; + if (FLAG_verify_entry_points) { + ErrorPtr error = constructor.VerifyEntryPoint(EntryPointPragma::kCallOnly); + if (error != Error::null()) return error; + } return constructor.ptr(); } @@ -4264,7 +4266,9 @@ DART_EXPORT Dart_Handle Dart_New(Dart_Handle type, Instance& new_object = Instance::Handle(Z); if (constructor.IsGenerativeConstructor()) { - CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + if (FLAG_verify_entry_points) { + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + } #if defined(DEBUG) if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) { @@ -4383,7 +4387,9 @@ DART_EXPORT Dart_Handle Dart_Allocate(Dart_Handle type) { const TypeArguments& type_arguments = TypeArguments::Handle(Z, type_obj.GetInstanceTypeArguments(T)); - CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + if (FLAG_verify_entry_points) { + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + } #if defined(DEBUG) if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) { return Api::NewError("Precompilation dropped '%s'", cls.ToCString()); @@ -4413,7 +4419,9 @@ Dart_AllocateWithNativeFields(Dart_Handle type, RETURN_NULL_ERROR(native_fields); } const Class& cls = Class::Handle(Z, type_obj.type_class()); - CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + if (FLAG_verify_entry_points) { + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + } #if defined(DEBUG) if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) { return Api::NewError("Precompilation dropped '%s'", cls.ToCString()); @@ -4505,7 +4513,10 @@ DART_EXPORT Dart_Handle Dart_InvokeConstructor(Dart_Handle object, if (!constructor.IsNull() && constructor.IsGenerativeConstructor() && constructor.AreValidArgumentCounts( kTypeArgsLen, number_of_arguments + extra_args, 0, nullptr)) { - CHECK_ERROR_HANDLE(constructor.VerifyCallEntryPoint()); + if (FLAG_verify_entry_points) { + CHECK_ERROR_HANDLE( + constructor.VerifyEntryPoint(EntryPointPragma::kCallOnly)); + } // Create the argument list. Dart_Handle result; Array& args = Array::Handle(Z); @@ -4585,8 +4596,8 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, return result; } return Api::NewHandle( - T, cls.Invoke(function_name, args, arg_names, respect_reflectable, - check_is_entrypoint)); + T, cls.Invoke(function_name, args, arg_names, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsNull() || obj.IsInstance()) { // Since we have allocated an object it would mean that the type of the // receiver is already resolved and finalized, hence it is not necessary @@ -4601,8 +4612,8 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, } args.SetAt(0, instance); return Api::NewHandle( - T, instance.Invoke(function_name, args, arg_names, respect_reflectable, - check_is_entrypoint)); + T, instance.Invoke(function_name, args, arg_names, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsLibrary()) { // Check whether class finalization is needed. const Library& lib = Library::Cast(obj); @@ -4624,8 +4635,8 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, } return Api::NewHandle( - T, lib.Invoke(function_name, args, arg_names, respect_reflectable, - check_is_entrypoint)); + T, lib.Invoke(function_name, args, arg_names, check_is_entrypoint, + respect_reflectable)); } else { return Api::NewError( "%s expects argument 'target' to be an object, type, or library.", @@ -4675,7 +4686,6 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) { RETURN_TYPE_ERROR(Z, name, String); } const Object& obj = Object::Handle(Z, Api::UnwrapHandle(container)); - const bool throw_nsm_if_absent = true; const bool respect_reflectable = false; const bool check_is_entrypoint = FLAG_verify_entry_points; @@ -4690,9 +4700,8 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) { const Library& lib = Library::Handle(Z, cls.library()); field_name = lib.PrivateName(field_name); } - return Api::NewHandle( - T, cls.InvokeGetter(field_name, throw_nsm_if_absent, - respect_reflectable, check_is_entrypoint)); + return Api::NewHandle(T, cls.InvokeGetter(field_name, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsNull() || obj.IsInstance()) { Instance& instance = Instance::Handle(Z); instance ^= obj.ptr(); @@ -4702,8 +4711,8 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) { field_name = lib.PrivateName(field_name); } return Api::NewHandle(T, - instance.InvokeGetter(field_name, respect_reflectable, - check_is_entrypoint)); + instance.InvokeGetter(field_name, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsLibrary()) { const Library& lib = Library::Cast(obj); // Check that the library is loaded. @@ -4715,9 +4724,8 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) { if (Library::IsPrivate(field_name)) { field_name = lib.PrivateName(field_name); } - return Api::NewHandle( - T, lib.InvokeGetter(field_name, throw_nsm_if_absent, - respect_reflectable, check_is_entrypoint)); + return Api::NewHandle(T, lib.InvokeGetter(field_name, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsError()) { return container; } else { @@ -4767,8 +4775,8 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container, field_name = lib.PrivateName(field_name); } return Api::NewHandle( - T, cls.InvokeSetter(field_name, value_instance, respect_reflectable, - check_is_entrypoint)); + T, cls.InvokeSetter(field_name, value_instance, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsNull() || obj.IsInstance()) { Instance& instance = Instance::Handle(Z); instance ^= obj.ptr(); @@ -4779,7 +4787,7 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container, } return Api::NewHandle( T, instance.InvokeSetter(field_name, value_instance, - respect_reflectable, check_is_entrypoint)); + check_is_entrypoint, respect_reflectable)); } else if (obj.IsLibrary()) { // To access a top-level we may need to use the Field or the // setter Function. The setter function may either be in the @@ -4796,8 +4804,8 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container, field_name = lib.PrivateName(field_name); } return Api::NewHandle( - T, lib.InvokeSetter(field_name, value_instance, respect_reflectable, - check_is_entrypoint)); + T, lib.InvokeSetter(field_name, value_instance, check_is_entrypoint, + respect_reflectable)); } else if (obj.IsError()) { return container; } @@ -5481,7 +5489,9 @@ DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library, cls_name.ToCString(), lib_name.ToCString()); } cls.EnsureDeclarationLoaded(); - CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + if (FLAG_verify_entry_points) { + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + } return Api::NewHandle(T, cls.RareType()); } @@ -5511,7 +5521,9 @@ static Dart_Handle GetTypeCommon(Dart_Handle library, name_str.ToCString(), lib_name.ToCString()); } cls.EnsureDeclarationLoaded(); - CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + if (FLAG_verify_entry_points) { + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); + } Type& type = Type::Handle(); if (cls.NumTypeArguments() == 0) { diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc index b164ae00c97..258efa8a574 100644 --- a/runtime/vm/dart_api_impl_test.cc +++ b/runtime/vm/dart_api_impl_test.cc @@ -16,6 +16,7 @@ #include "vm/dart.h" #include "vm/dart_api_state.h" #include "vm/debugger_api_impl_test.h" +#include "vm/flags.h" #include "vm/heap/verifier.h" #include "vm/lockers.h" #include "vm/timeline.h" @@ -44,6 +45,7 @@ UNIT_TEST_CASE(DartAPI_DartInitializeAfterCleanup) { { TestIsolateScope scope; const char* kScriptChars = + "@pragma('vm:entry-point', 'call')\n" "int testMain() {\n" " return 42;\n" "}\n"; @@ -137,6 +139,7 @@ UNIT_TEST_CASE(DartAPI_DartInitializeHeapSizes) { TEST_CASE(Dart_KillIsolate) { const char* kScriptChars = + "@pragma('vm:entry-point', 'call')\n" "int testMain() {\n" " return 42;\n" "}\n"; @@ -161,6 +164,7 @@ class InfiniteLoopTask : public ThreadPool::Task { virtual void Run() { TestIsolateScope scope; const char* kScriptChars = + "@pragma('vm:entry-point', 'call')\n" "testMain() {\n" " while(true) {};" "}\n"; @@ -211,6 +215,7 @@ TEST_CASE(Dart_KillIsolatePriority) { TEST_CASE(DartAPI_ErrorHandleBasics) { const char* kScriptChars = + "@pragma('vm:entry-point', 'call')\n" "void testMain() {\n" " throw new Exception(\"bad news\");\n" "}\n"; @@ -233,7 +238,7 @@ TEST_CASE(DartAPI_ErrorHandleBasics) { EXPECT_STREQ("myerror", Dart_GetError(error)); EXPECT_STREQ(ZONE_STR("Unhandled exception:\n" "Exception: bad news\n" - "#0 testMain (%s:2:3)", + "#0 testMain (%s:3:3)", TestCase::url()), Dart_GetError(exception)); @@ -249,6 +254,7 @@ TEST_CASE(DartAPI_StackTraceInfo) { const char* kScriptChars = "bar() => throw new Error();\n" "foo() => bar();\n" + "@pragma('vm:entry-point', 'call')\n" "testMain() => foo();\n"; Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); @@ -305,7 +311,7 @@ TEST_CASE(DartAPI_StackTraceInfo) { EXPECT_STREQ("testMain", cstr); Dart_StringToCString(script_url, &cstr); EXPECT_SUBSTRING("test-lib", cstr); - EXPECT_EQ(3, line_number); + EXPECT_EQ(4, line_number); EXPECT_EQ(15, column_number); // Out-of-bounds frames. @@ -318,6 +324,7 @@ TEST_CASE(DartAPI_StackTraceInfo) { TEST_CASE(DartAPI_DeepStackTraceInfo) { const char* kScriptChars = "foo(n) => n == 1 ? throw new Error() : foo(n-1);\n" + "@pragma('vm:entry-point', 'call')\n" "testMain() => foo(100);\n"; Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); @@ -383,7 +390,7 @@ TEST_CASE(DartAPI_DeepStackTraceInfo) { EXPECT_STREQ("testMain", cstr); Dart_StringToCString(script_url, &cstr); EXPECT_SUBSTRING("test-lib", cstr); - EXPECT_EQ(2, line_number); + EXPECT_EQ(3, line_number); EXPECT_EQ(15, column_number); // Out-of-bounds frames. @@ -399,8 +406,11 @@ void VerifyStackOverflowStackTraceInfo(const char* script, int expected_line_number, int expected_column_number) { Dart_Handle lib = TestCase::LoadTestScript(script, nullptr); - Dart_Handle error = Dart_Invoke(lib, NewString(entry_func_name), 0, nullptr); - + Dart_Handle error; + { + SetFlagScope sfs(&FLAG_verify_entry_points, false); + error = Dart_Invoke(lib, NewString(entry_func_name), 0, nullptr); + } EXPECT(Dart_IsError(error)); Dart_StackTrace stacktrace; @@ -477,6 +487,7 @@ TEST_CASE(DartAPI_StackOverflowStackTraceInfoArrowFunction) { TEST_CASE(DartAPI_OutOfMemoryStackTraceInfo) { const char* kScriptChars = "var number_of_ints = 134000000;\n" + "@pragma('vm:entry-point', 'call')" "testMain() {\n" " new List(number_of_ints)\n" "}\n"; @@ -566,7 +577,7 @@ void CurrentStackTraceNative(Dart_NativeArguments args) { EXPECT_STREQ("testMain", cstr); Dart_StringToCString(script_url, &cstr); EXPECT_STREQ(test_lib, cstr); - EXPECT_EQ(5, line_number); + EXPECT_EQ(6, line_number); EXPECT_EQ(15, column_number); // Out-of-bounds frames. @@ -593,6 +604,7 @@ TEST_CASE(DartAPI_CurrentStackTraceInfo) { @pragma("vm:external-name", "CurrentStackTraceNative") external inspectStack(); foo(n) => n == 1 ? inspectStack() : foo(n-1); +@pragma("vm:entry-point", "call") testMain() => foo(100); )"; @@ -735,6 +747,7 @@ exitRightNow() { @pragma("vm:external-name", "Test_nativeFunc") external void nativeFunc(closure); +@pragma("vm:entry-point", "call") void Func1() { nativeFunc(() => exitRightNow()); } @@ -765,6 +778,7 @@ sendAndExitNow() { @pragma("vm:external-name", "Test_nativeFunc") external void nativeFunc(closure); +@pragma("vm:entry-point", "call") void Func1() { nativeFunc(() => sendAndExitNow()); } @@ -824,6 +838,7 @@ raiseCompileError() { @pragma("vm:external-name", "Test_nativeFunc") external void nativeFunc(closure); +@pragma("vm:entry-point", "call") void Func1() { nativeFunc(() => raiseCompileError()); } @@ -867,6 +882,7 @@ void throwException() { @pragma("vm:external-name", "Test_nativeFunc") external void nativeFunc(closure); +@pragma("vm:entry-point", "call") void Func2() { nativeFunc(() => throwException()); } @@ -1089,6 +1105,7 @@ TEST_CASE(DartAPI_InstanceGetType) { TEST_CASE(DartAPI_FunctionName) { const char* kScriptChars = "int getInt() { return 1; }\n"; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); @@ -1109,6 +1126,7 @@ TEST_CASE(DartAPI_FunctionName) { TEST_CASE(DartAPI_FunctionOwner) { const char* kScriptChars = "int getInt() { return 1; }\n"; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); @@ -1145,6 +1163,7 @@ TEST_CASE(DartAPI_IsTearOff) { " int bar() => 24;\n" "}\n" "Baz getBaz() => Baz();\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); @@ -1193,6 +1212,7 @@ TEST_CASE(DartAPI_FunctionIsStatic) { "int getInt() { return 1; }\n" "class Foo { String getString() => 'foobar'; }\n"; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); @@ -1229,6 +1249,7 @@ TEST_CASE(DartAPI_FunctionIsStatic) { TEST_CASE(DartAPI_ClosureFunction) { const char* kScriptChars = "int getInt() { return 1; }\n"; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); @@ -1259,6 +1280,7 @@ TEST_CASE(DartAPI_GetStaticMethodClosure) { " }\n" "}\n"; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); Dart_Handle foo_cls = Dart_GetClass(lib, NewString("Foo")); @@ -1379,6 +1401,7 @@ TEST_CASE(DartAPI_NumberValues) { "double getDouble() { return 1.0; }\n" "bool getBool() { return false; }\n" "getNull() { return null; }\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle result; // Create a test library and Load up a test script in it. Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); @@ -1641,12 +1664,14 @@ TEST_CASE(DartAPI_MalformedStringToUTF8) { // Strings are allowed to have individual or out of order surrogates, even // if that doesn't make sense as renderable characters. const char* kScriptChars = + "@pragma('vm:entry-point', 'call')" "String lowSurrogate() {" " return '\\u{1D11E}'[1];" "}" "String highSurrogate() {" " return '\\u{1D11E}'[0];" "}" + "@pragma('vm:entry-point', 'call')" "String reversed() => lowSurrogate() + highSurrogate();"; Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); @@ -1683,6 +1708,7 @@ TEST_CASE(DartAPI_MalformedStringToUTF8) { TEST_CASE(DartAPI_CopyUTF8EncodingOfString) { const char* kScriptChars = + "@pragma('vm:entry-point', 'call')" "String lowSurrogate() {" " return '\\u{1D11E}'[1];" "}"; @@ -1748,6 +1774,7 @@ TEST_CASE(DartAPI_ListAccess) { "List immutable() {" " return const [0, 1, 2];" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle result; // Create a test library and Load up a test script in it. @@ -1888,6 +1915,7 @@ TEST_CASE(DartAPI_ListAccess) { TEST_CASE(DartAPI_MapAccess) { EXPECT(!Dart_IsMap(Dart_Null())); const char* kScriptChars = + "@pragma('vm:entry-point', 'call')" "Map testMain() {" " return {" " 'a' : 1," @@ -1980,6 +2008,7 @@ TEST_CASE(DartAPI_MapAccess) { TEST_CASE(DartAPI_IsFuture) { const char* kScriptChars = "import 'dart:async';" + "@pragma('vm:entry-point', 'call')" "Future testMain() {" " return new Completer().future;" "}"; @@ -2007,6 +2036,7 @@ TEST_CASE(DartAPI_TypedDataViewListGetAsBytes) { const char* kScriptChars = "import 'dart:typed_data';\n" + "@pragma('vm:entry-point', 'call')\n" "List testMain(int size) {\n" " var a = new Int8List(size);\n" " var view = new Int8List.view(a.buffer, 0, size);\n" @@ -2039,6 +2069,7 @@ TEST_CASE(DartAPI_TypedDataViewListIsTypedData) { const char* kScriptChars = "import 'dart:typed_data';\n" + "@pragma('vm:entry-point', 'call')\n" "List testMain(int size) {\n" " var a = new Int8List(size);\n" " var view = new Int8List.view(a.buffer, 0, size);\n" @@ -2062,6 +2093,7 @@ TEST_CASE(DartAPI_UnmodifiableTypedDataViewListIsTypedData) { const char* kScriptChars = "import 'dart:typed_data';\n" + "@pragma('vm:entry-point', 'call')\n" "List testMain(int size) {\n" " var a = new Int8List(size);\n" " var view = a.asUnmodifiableView();\n" @@ -2338,9 +2370,11 @@ TEST_CASE(DartAPI_ExternalByteDataFinalizer) { // wrapper. const char* kScriptChars = "var array;\n" + "@pragma('vm:entry-point', 'call')\n" "extractAndSaveArray(byteData) {\n" " array = byteData.buffer.asUint8List();\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "releaseArray() {\n" " array = null;\n" "}\n"; @@ -2520,7 +2554,10 @@ static void TestDirectAccess(Dart_Handle lib, // Invoke the dart function that sets initial values. Dart_Handle dart_args[1]; dart_args[0] = array; - result = Dart_Invoke(lib, NewString("setMain"), 1, dart_args); + { + SetFlagScope sfs(&FLAG_verify_entry_points, false); + result = Dart_Invoke(lib, NewString("setMain"), 1, dart_args); + } EXPECT_VALID(result); // Now Get a direct access to this typed data object and check it's contents. @@ -2555,7 +2592,10 @@ static void TestDirectAccess(Dart_Handle lib, EXPECT_VALID(result); // Invoke the dart function in order to check the modified values. - result = Dart_Invoke(lib, NewString("testMain"), 1, dart_args); + { + SetFlagScope sfs(&FLAG_verify_entry_points, false); + result = Dart_Invoke(lib, NewString("testMain"), 1, dart_args); + } EXPECT_VALID(result); } @@ -2816,12 +2856,14 @@ class Expect { if (!threw) throw 'did not throw'; } } +@pragma('vm:entry-point', 'call') testList(data) { for (var i = 0; i < data.length; i++) { Expect.equals(i, data[i]); Expect.throws(() => data[i] = 0); } } +@pragma('vm:entry-point', 'call') testBytes(data) { for (var i = 0; i < data.length; i++) { Expect.equals(i, data.getUint8(i)); @@ -2982,6 +3024,7 @@ testBytes(data) { TEST_CASE(DartAPI_UnmodifiableTypedData_PassByReference) { const char* kScriptChars = R"( import 'dart:isolate'; +@pragma('vm:entry-point', 'call') test(original) { var port = new RawReceivePort(); port.handler = (msg) { @@ -3111,6 +3154,7 @@ TEST_CASE(DartAPI_ExternalClampedTypedDataAccess) { TEST_CASE(DartAPI_ExternalUint8ClampedArrayAccess) { const char* kScriptChars = + "@pragma('vm:entry-point', 'call')\n" "testClamped(List a) {\n" " if (a[1] != 11) return false;\n" " a[1] = 3;\n" @@ -3246,6 +3290,7 @@ static void CheckFloat32x4Data(Dart_Handle obj) { TEST_CASE(DartAPI_Float32x4List) { const char* kScriptChars = "import 'dart:typed_data';\n" + "@pragma('vm:entry-point', 'call')\n" "Float32x4List float32x4() {\n" " return new Float32x4List(10);\n" "}\n"; @@ -3706,6 +3751,7 @@ TEST_CASE(DartAPI_FinalizableHandle) { } TEST_CASE(DartAPI_WeakPersistentHandleErrors) { + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_EnterScope(); // nullptr callback. @@ -3766,6 +3812,7 @@ TEST_CASE(DartAPI_WeakPersistentHandleErrors) { } TEST_CASE(DartAPI_FinalizableHandleErrors) { + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_EnterScope(); // nullptr callback. @@ -4900,6 +4947,7 @@ TEST_CASE(DartAPI_TypeGetNonParametricTypes) { "Type getMyClass0Type() { return new MyClass0().runtimeType; }\n" "Type getMyClass1Type() { return new MyClass1().runtimeType; }\n" "Type getMyClass2Type() { return new MyClass2().runtimeType; }\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); bool instanceOf = false; @@ -5014,6 +5062,7 @@ TEST_CASE(DartAPI_TypeGetParameterizedTypes) { "Type getListIntType() { return type>(); }\n" "Type getListType() { return List; }\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle corelib = Dart_LookupLibrary(NewString("dart:core")); EXPECT_VALID(corelib); @@ -5228,6 +5277,7 @@ TEST_CASE(DartAPI_FieldAccess) { "}\n"; // Shared setup. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("Fields"), 0, nullptr); @@ -5391,7 +5441,9 @@ TEST_CASE(DartAPI_FieldAccess) { } TEST_CASE(DartAPI_SetField_FunnyValue) { - const char* kScriptChars = "var top;\n"; + const char* kScriptChars = + "@pragma('vm:entry-point')\n" + "var top;\n"; Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle name = NewString("top"); @@ -5423,7 +5475,9 @@ TEST_CASE(DartAPI_SetField_FunnyValue) { } TEST_CASE(DartAPI_SetField_BadType) { - const char* kScriptChars = "late int foo;\n"; + const char* kScriptChars = + "@pragma('vm:entry-point', 'set')\n" + "late int foo;\n"; Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle name = NewString("foo"); Dart_Handle result = Dart_SetField(lib, name, Dart_True()); @@ -5454,6 +5508,7 @@ TEST_CASE(DartAPI_InjectNativeFields2) { " static int? fld3;\n" " static const int fld4 = 10;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "NativeFields testMain() {\n" " NativeFields obj = new NativeFields(10, 20);\n" " return obj;\n" @@ -5486,6 +5541,7 @@ TEST_CASE(DartAPI_InjectNativeFields3) { " static int? fld3;\n" " static const int fld4 = 10;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "NativeFields testMain() {\n" " NativeFields obj = new NativeFields(10, 20);\n" " return obj;\n" @@ -5529,6 +5585,7 @@ TEST_CASE(DartAPI_InjectNativeFields4) { " static int? fld3;\n" " static const int fld4 = 10;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "NativeFields testMain() {\n" " NativeFields obj = new NativeFields(10, 20);\n" " return obj;\n" @@ -5632,6 +5689,7 @@ TEST_CASE(DartAPI_TestNativeFieldsAccess) { @pragma('vm:external-name', 'TestNativeFieldsAccess_invalidAccess') external invalidAccess(); } + @pragma('vm:entry-point', 'call') NativeFields testMain() { NativeFields obj = new NativeFields(10, 20); obj.initNativeFlds(); @@ -5661,6 +5719,7 @@ TEST_CASE(DartAPI_InjectNativeFieldsSuperClass) { "base class NativeFields extends NativeFieldsSuper {\n" " fld() => fld1;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "int testMain() {\n" " NativeFields obj = new NativeFields();\n" " return obj.fld();\n" @@ -5681,6 +5740,7 @@ TEST_CASE(DartAPI_InjectNativeFieldsSuperClass) { } static void TestNativeFields(Dart_Handle retobj) { + SetFlagScope sfs(&FLAG_verify_entry_points, false); // Access and set various instance fields of the object. Dart_Handle result = Dart_GetField(retobj, NewString("fld3")); EXPECT(Dart_IsError(result)); @@ -5768,6 +5828,7 @@ TEST_CASE(DartAPI_ImplicitNativeFieldAccess) { " static int? fld3;\n" " static const int fld4 = 10;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "NativeFields testMain() {\n" " NativeFields obj = new NativeFields(10, 20);\n" " return obj;\n" @@ -5795,10 +5856,12 @@ TEST_CASE(DartAPI_NegativeNativeFieldAccess) { " static int? fld3;\n" " static const int fld4 = 10;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "NativeFields testMain1() {\n" " NativeFields obj = new NativeFields(10, 20);\n" " return obj;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "Function testMain2() {\n" " return () {};\n" "}\n"; @@ -5868,6 +5931,7 @@ TEST_CASE(DartAPI_GetStaticField_RunsInitializer) { "}\n"; Dart_Handle result; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("TestClass"), 0, nullptr); @@ -5911,6 +5975,7 @@ TEST_CASE(DartAPI_GetField_CheckIsolate) { int64_t value = 0; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("TestClass"), 0, nullptr); @@ -5933,6 +5998,7 @@ TEST_CASE(DartAPI_SetField_CheckIsolate) { int64_t value = 0; // Create a test library and Load up a test script in it. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("TestClass"), 0, nullptr); @@ -5978,6 +6044,7 @@ TEST_CASE(DartAPI_New) { "}\n" "\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("MyClass"), 0, nullptr); @@ -6198,6 +6265,7 @@ TEST_CASE(DartAPI_New_Issue42939) { "}\n" "\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("MyClass"), 0, nullptr); @@ -6251,6 +6319,7 @@ TEST_CASE(DartAPI_New_Issue44205) { "Type getIntType() { return int; }\n" "\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); Dart_Handle int_wrapper_type = @@ -6297,6 +6366,7 @@ TEST_CASE(DartAPI_InvokeConstructor_Issue44205) { "Type getIntType() { return int; }\n" "\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); Dart_Handle int_wrapper_type = @@ -6338,6 +6408,7 @@ TEST_CASE(DartAPI_InvokeClosure_Issue44205) { " final int fld2;\n" " static const int fld4 = 10;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "Function testMain1() {\n" " InvokeClosure obj = new InvokeClosure(10, 20);\n" " return obj.method1(10);\n" @@ -6371,6 +6442,7 @@ TEST_CASE(DartAPI_NewListOfType) { "void expectListOfDynamic(List _) {}\n" "void expectListOfVoid(List _) {}\n" "void expectListOfNever(List _) {}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle zxhandle_type = @@ -6444,6 +6516,7 @@ TEST_CASE(DartAPI_NewListOfTypeFilled) { " final List handles;\n" " ChannelReadResult(this.handles);\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle zxhandle_type = @@ -6538,6 +6611,7 @@ TEST_CASE(DartAPI_Invoke) { "}\n"; // Shared setup. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("Methods"), 0, nullptr); @@ -6645,6 +6719,7 @@ TEST_CASE(DartAPI_Invoke_PrivateStatic) { "\n"; // Shared setup. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("Methods"), 0, nullptr); @@ -6663,9 +6738,189 @@ TEST_CASE(DartAPI_Invoke_PrivateStatic) { EXPECT_STREQ("hidden static !!!", str); } +TEST_CASE(DartAPI_MissingEntryPoints) { + const char* kScriptChars = R"( +class C { + final cx = 1; + int cy = 2; + @pragma('vm:entry-point', 'get') + int cz = 3; + @pragma('vm:entry-point', 'set') + int cw = 4; + + void c1() {} + + @pragma('vm:entry-point', 'call') + void c2() {} + + @pragma('vm:entry-point', 'get') + void c3() {} +} + +@pragma('vm:entry-point') +class D { + static final dx = 1; + static int dy = 2; + @pragma('vm:entry-point', 'get') + static int dz = 3; + @pragma('vm:entry-point', 'set') + static int dw = 4; + + static void d1() {} + + @pragma('vm:entry-point', 'call') + static void d2() {} + + @pragma('vm:entry-point', 'get') + static void d3() { + print('Okay to closurize.'); + } +} + +final x = 1; +int y = 2; +@pragma('vm:entry-point', 'get') +int z = 3; +@pragma('vm:entry-point', 'set') +int w = 4; + +void test1() {} + +@pragma('vm:entry-point', 'call') +void test2() {} + +@pragma('vm:entry-point', 'get') +void test3() {} + +@pragma('vm:entry-point', 'call') +C newC() => C(); + )"; + + // Shared setup. + SetFlagScope sfs(&FLAG_verify_entry_points, true); + Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); + EXPECT_VALID(lib); + Dart_Handle instance = Dart_Invoke(lib, NewString("newC"), 0, nullptr); + EXPECT_VALID(instance); + Dart_Handle d_class = Dart_GetClass(lib, NewString("D")); + EXPECT_VALID(d_class); + Dart_Handle value = Dart_NewInteger(0); + Dart_Handle name; + + // Top level method, not annotated for calling or closurization. + name = NewString("test1"); + EXPECT_ERROR(Dart_Invoke(lib, name, 0, nullptr), "entry_point_pragma.md"); + EXPECT_ERROR(Dart_GetField(lib, name), "entry_point_pragma.md"); + + // Top level method annotated for calling, not closurization. + name = NewString("test2"); + EXPECT_VALID(Dart_Invoke(lib, name, 0, nullptr)); + EXPECT_ERROR(Dart_GetField(lib, name), "entry_point_pragma.md"); + + // Top level method annotated for closurization, not calling. + name = NewString("test3"); + EXPECT_ERROR(Dart_Invoke(lib, name, 0, nullptr), "entry_point_pragma.md"); + EXPECT_VALID(Dart_GetField(lib, name)); + + // Final top level field, not annotated for getting. + name = NewString("x"); + EXPECT_ERROR(Dart_GetField(lib, name), "entry_point_pragma.md"); + + // Top level field, not annotated for getting or setting. + name = NewString("y"); + EXPECT_ERROR(Dart_GetField(lib, name), "entry_point_pragma.md"); + EXPECT_ERROR(Dart_SetField(lib, name, value), "entry_point_pragma.md"); + + // Top level field annotated for getting, not setting. + name = NewString("z"); + EXPECT_VALID(Dart_GetField(lib, name)); + EXPECT_ERROR(Dart_SetField(lib, name, value), "entry_point_pragma.md"); + + // Top level field annotated for setting, not getting. + name = NewString("w"); + EXPECT_ERROR(Dart_GetField(lib, name), "entry_point_pragma.md"); + EXPECT_VALID(Dart_SetField(lib, name, value)); + + // Instance method, not annotated for calling or closurization. + name = NewString("c1"); + EXPECT_ERROR(Dart_Invoke(instance, name, 0, nullptr), + "entry_point_pragma.md"); + EXPECT_ERROR(Dart_GetField(instance, name), "entry_point_pragma.md"); + + // Instance method annotated for calling, not closurization. + name = NewString("c2"); + EXPECT_VALID(Dart_Invoke(instance, name, 0, nullptr)); + EXPECT_ERROR(Dart_GetField(instance, name), "entry_point_pragma.md"); + + // Instance method annotated for closurization, not calling. + name = NewString("c3"); + EXPECT_ERROR(Dart_Invoke(instance, name, 0, nullptr), + "entry_point_pragma.md"); + EXPECT_VALID(Dart_GetField(instance, name)); + + // Final instance field, not annotated for getting. + name = NewString("cx"); + EXPECT_ERROR(Dart_GetField(instance, name), "entry_point_pragma.md"); + + // Instance field, not annotated for getting or setting. + name = NewString("cy"); + EXPECT_ERROR(Dart_GetField(instance, name), "entry_point_pragma.md"); + EXPECT_ERROR(Dart_SetField(instance, name, value), "entry_point_pragma.md"); + + // Instance field annotated for getting, not setting. + name = NewString("cz"); + EXPECT_VALID(Dart_GetField(instance, name)); + EXPECT_ERROR(Dart_SetField(instance, name, value), "entry_point_pragma.md"); + + // Instance field annotated for setting, not getting. + name = NewString("cw"); + EXPECT_ERROR(Dart_GetField(instance, name), "entry_point_pragma.md"); + EXPECT_VALID(Dart_SetField(instance, name, value)); + + // Class, not annotated for access. + name = NewString("C"); + EXPECT_ERROR(Dart_GetClass(lib, name), "entry_point_pragma.md"); + + // Static method, not annotated for calling or closurization. + name = NewString("d1"); + EXPECT_ERROR(Dart_Invoke(d_class, name, 0, nullptr), "entry_point_pragma.md"); + EXPECT_ERROR(Dart_GetField(d_class, name), "entry_point_pragma.md"); + + // Instance method annotated for calling, not closurization. + name = NewString("d2"); + EXPECT_VALID(Dart_Invoke(d_class, name, 0, nullptr)); + EXPECT_ERROR(Dart_GetField(d_class, name), "entry_point_pragma.md"); + + // Instance method annotated for closurization, not calling. + name = NewString("d3"); + EXPECT_ERROR(Dart_Invoke(d_class, name, 0, nullptr), "entry_point_pragma.md"); + EXPECT_VALID(Dart_GetField(d_class, name)); + + // Final static field, getter. + name = NewString("dx"); + EXPECT_ERROR(Dart_GetField(d_class, name), "entry_point_pragma.md"); + + // Static field, not annotated for getting or setting. + name = NewString("dy"); + value = Dart_NewInteger(0); + EXPECT_ERROR(Dart_GetField(d_class, name), "entry_point_pragma.md"); + EXPECT_ERROR(Dart_SetField(d_class, name, value), "entry_point_pragma.md"); + + // Static field annotated for getting, not setting. + name = NewString("dz"); + EXPECT_VALID(Dart_GetField(d_class, name)); + EXPECT_ERROR(Dart_SetField(d_class, name, value), "entry_point_pragma.md"); + + // Static field annotated for setting, not getting. + name = NewString("dw"); + EXPECT_ERROR(Dart_GetField(d_class, name), "entry_point_pragma.md"); + EXPECT_VALID(Dart_SetField(d_class, name, value)); +} + TEST_CASE(DartAPI_Invoke_FunnyArgs) { const char* kScriptChars = "test(arg) => 'hello $arg';\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle func_name = NewString("test"); Dart_Handle args[1]; @@ -6739,6 +6994,7 @@ TEST_CASE(DartAPI_Invoke_BadArgs) { #endif // defined(PRODUCT) // Shared setup. + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); Dart_Handle type = Dart_GetNonNullableType(lib, NewString("Methods"), 0, nullptr); @@ -6793,6 +7049,7 @@ TEST_CASE(DartAPI_Invoke_BadArgs) { } TEST_CASE(DartAPI_Invoke_Null) { + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle result = Dart_Invoke(Dart_Null(), NewString("toString"), 0, nullptr); EXPECT_VALID(result); @@ -6855,6 +7112,7 @@ TEST_CASE(DartAPI_InvokeNoSuchMethod) { " return new TestClass();\n" " }\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle result; Dart_Handle instance; // Create a test library and Load up a test script in it. @@ -6913,6 +7171,7 @@ TEST_CASE(DartAPI_InvokeClosure) { Dart_Handle result; CHECK_API_SCOPE(thread); + SetFlagScope sfs(&FLAG_verify_entry_points, false); // Create a test library and Load up a test script in it. Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); @@ -6970,9 +7229,11 @@ TEST_CASE(DartAPI_ThrowException) { const char* kScriptChars = R"( @pragma('vm:external-name', 'ThrowException_native') + @pragma('vm:entry-point', 'call') external int test(); )"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle result; intptr_t size = thread->ZoneSizeInBytes(); Dart_EnterScope(); // Start a Dart API scope for invoking API functions. @@ -7167,6 +7428,7 @@ int testMain(String extstr) { obj2); })"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, native_args_lookup); const char* ascii_str = "string"; @@ -7202,6 +7464,7 @@ class MyObject { @pragma("vm:external-name", "Name_Does_Not_Matter") external int method1(int i, int j); } +@pragma('vm:entry-point', 'call') testMain() { MyObject obj = new MyObject(); return obj.method1(77, 125); @@ -7226,6 +7489,7 @@ TEST_CASE(DartAPI_TypeToNullability) { " static var name = 'Class';\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); const Dart_Handle name = NewString("Class"); @@ -7262,6 +7526,7 @@ TEST_CASE(DartAPI_GetNullableType) { " static var name = '_Class';\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); // Lookup a class. @@ -7322,6 +7587,7 @@ TEST_CASE(DartAPI_GetNonNullableType) { " static var name = '_Class';\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); // Lookup a class. @@ -7384,6 +7650,7 @@ TEST_CASE(DartAPI_InstanceOf) { " return new InstanceOfTest();\n" " }\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle result; // Create a test library and Load up a test script in it. Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); @@ -7612,6 +7879,7 @@ TEST_CASE(DartAPI_SetNativeResolver) { external static baz(); } )"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle error = Dart_NewApiError("incoming error"); Dart_Handle result; @@ -7961,6 +8229,7 @@ VM_UNIT_TEST_CASE(DartAPI_NewNativePort) { TestIsolateScope __test_isolate__; const char* kScriptChars = "import 'dart:isolate';\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port) {\n" " var receivePort = new RawReceivePort();\n" " var replyPort = receivePort.sendPort;\n" @@ -8035,6 +8304,7 @@ static void NewNativePort_sendInteger321(Dart_Port dest_port_id, TEST_CASE(DartAPI_NativePortPostInteger) { const char* kScriptChars = "import 'dart:isolate';\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port) {\n" " var receivePort = new RawReceivePort();\n" " var replyPort = receivePort.sendPort;\n" @@ -8112,6 +8382,7 @@ TEST_CASE(DartAPI_NativePortPostTransferrableTypedData) { const char* kScriptChars = "import 'dart:typed_data';\n" "import 'dart:isolate';\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port1, SendPort port2) {\n" " final td1 =" " TransferableTypedData.fromList([Uint8List(10)..[0] = 42]);\n" @@ -8175,6 +8446,7 @@ TEST_CASE(DartAPI_NativePortPostExternalTypedData) { const char* kScriptChars = "import 'dart:typed_data';\n" "import 'dart:isolate';\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port, Uint8List data) {\n" " port.send(data);\n" "}\n"; @@ -8216,6 +8488,7 @@ TEST_CASE(DartAPI_NativePortPostUserClass) { const char* kScriptChars = "import 'dart:isolate';\n" "class ABC {}\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port) {\n" " port.send(new ABC());\n" "}\n"; @@ -8266,6 +8539,7 @@ static void NewNativePort_nativeReceiveNull(Dart_Port dest_port_id, TEST_CASE(DartAPI_NativePortReceiveNull) { const char* kScriptChars = "import 'dart:isolate';\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port) {\n" " var receivePort = new RawReceivePort();\n" " var replyPort = receivePort.sendPort;\n" @@ -8318,6 +8592,7 @@ static void NewNativePort_nativeReceiveInteger(Dart_Port dest_port_id, TEST_CASE(DartAPI_NativePortReceiveInteger) { const char* kScriptChars = "import 'dart:isolate';\n" + "@pragma('vm:entry-point', 'call')" "void callPort(SendPort port) {\n" " var receivePort = new RawReceivePort();\n" " var replyPort = receivePort.sendPort;\n" @@ -8524,6 +8799,7 @@ static void IsolateShutdownRunDartCodeTestCallback(void* isolate_group_data, ASSERT(add_result == 0); } Dart_EnterScope(); + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = Dart_RootLibrary(); EXPECT_VALID(lib); Dart_Handle arg1 = Dart_NewInteger(90); @@ -8703,6 +8979,7 @@ TEST_CASE(DartAPI_NativeFunctionClosure) { } } } + @pragma('vm:entry-point', 'call') int testMain() { Test obj = new Test(); Expect.equals(1, obj.foo1()); @@ -8852,6 +9129,7 @@ TEST_CASE(DartAPI_NativeStaticFunctionClosure) { } } } + @pragma('vm:entry-point', 'call') int testMain() { Test obj = new Test(); Expect.equals(0, Test.foo1()); @@ -9363,6 +9641,7 @@ TEST_CASE(DartAPI_StringFromExternalTypedData) { "testView16(external) {\n" " return test(external.buffer.asUint16List());\n" "}\n"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); { @@ -9941,6 +10220,7 @@ TEST_CASE(DartAPI_InvokeVMServiceMethod) { } return false; } + @pragma('vm:entry-point', 'call') bool validateResult(Uint8List bytes) { final map = json.decode(utf8.decode(bytes)); validate(map['jsonrpc'] == '2.0'); @@ -10362,6 +10642,7 @@ TEST_CASE(DartAPI_HeapSampling_UserDefinedClass) { const char* kScriptChars = R"( class Bar {} final list = []; + @pragma('vm:entry-point', 'call') foo() { for (int i = 0; i < 100000; ++i) { list.add(Bar()); @@ -10496,6 +10777,7 @@ TEST_CASE(DartAPI_HeapSampling_NonTrivialSamplingPeriod) { const char* kScriptChars = R"( final list = []; + @pragma('vm:entry-point', 'call') foo() { for (int i = 0; i < 1000; ++i) { list.add(List.filled(100, 0)); diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc index ba2bb6b0e88..73d0db677df 100644 --- a/runtime/vm/debugger.cc +++ b/runtime/vm/debugger.cc @@ -714,8 +714,7 @@ const Context& ActivationFrame::GetSavedCurrentContext() { const auto variable_index = VariableIndex(var_info.index()); obj = GetStackVar(variable_index); if (obj.IsClosure()) { - ASSERT(function().name() == Symbols::call().ptr()); - ASSERT(function().IsInvokeFieldDispatcher()); + ASSERT(function().IsClosureCallDispatcher()); // Closure.call frames. ctx_ = Closure::Cast(obj).GetContext(); } else if (obj.IsContext()) { diff --git a/runtime/vm/exceptions_test.cc b/runtime/vm/exceptions_test.cc index 3528493472a..8cbd25de742 100644 --- a/runtime/vm/exceptions_test.cc +++ b/runtime/vm/exceptions_test.cc @@ -104,6 +104,7 @@ TEST_CASE(UnhandledExceptions) { UnhandledExceptions.invoke(); return 2; } + @pragma('vm:entry-point', 'call') static int method2() { throw new Second(); } @@ -121,6 +122,7 @@ TEST_CASE(UnhandledExceptions) { UnhandledExceptions.equals(3, Second.method3(1)); } )"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, native_lookup); EXPECT_VALID(Dart_Invoke(lib, NewString("testMain"), 0, nullptr)); } diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h index aa06ebc9868..02d46c56a36 100644 --- a/runtime/vm/flag_list.h +++ b/runtime/vm/flag_list.h @@ -246,7 +246,7 @@ constexpr bool FLAG_support_il_printer = false; R(eliminate_type_checks, true, bool, true, \ "Eliminate type checks when allowed by static type analysis.") \ D(support_rr, bool, false, "Support running within RR.") \ - P(verify_entry_points, bool, false, \ + P(verify_entry_points, bool, true, \ "Throw API error on invalid member access through native API. See " \ "entry_point_pragma.md") \ C(branch_coverage, false, false, bool, false, "Enable branch coverage") \ diff --git a/runtime/vm/isolate_reload_test.cc b/runtime/vm/isolate_reload_test.cc index d30886b35d0..bc611fa61c9 100644 --- a/runtime/vm/isolate_reload_test.cc +++ b/runtime/vm/isolate_reload_test.cc @@ -1163,6 +1163,7 @@ TEST_CASE(IsolateReload_LibraryShow) { "main() {\n" " return importedFunc();\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "mainInt() {\n" " return importedIntFunc();\n" "}\n"; @@ -1182,6 +1183,7 @@ TEST_CASE(IsolateReload_LibraryShow) { "main() {\n" " return importedFunc();\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "mainInt() {\n" " return importedIntFunc();\n" "}\n"; @@ -6405,6 +6407,7 @@ TEST_CASE(IsolateReload_ImplicitGetterWithLoadGuard) { A a = A(3); + @pragma('vm:entry-point', 'call') main() { int sum = 0; // Trigger OSR and optimize this function. @@ -6481,6 +6484,7 @@ TEST_CASE(IsolateReload_KeepPragma1) { // Old version of closure function bar() has a pragma. const char* kScript = "import 'file:///test:isolate_reload_helper';\n" + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " @pragma('vm:prefer-inline')\n" " void bar() {}\n" @@ -6496,6 +6500,7 @@ TEST_CASE(IsolateReload_KeepPragma1) { // New version of closure function bar() doesn't have a pragma. const char* kReloadScript = "import 'file:///test:isolate_reload_helper';\n" + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " void bar() {}\n" " return bar;\n" @@ -6606,6 +6611,7 @@ TEST_CASE(IsolateReload_KeepPragma2) { // Old version of closure function bar() has a pragma. const char* kScript = "import 'file:///test:isolate_reload_helper';\n" + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " @pragma('vm:prefer-inline')\n" " void bar() {}\n" @@ -6621,6 +6627,7 @@ TEST_CASE(IsolateReload_KeepPragma2) { // New version of closure function bar() has a different pragma. const char* kReloadScript = "import 'file:///test:isolate_reload_helper';\n" + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " @pragma('vm:never-inline')\n" " void bar() {}\n" @@ -6661,6 +6668,7 @@ TEST_CASE(IsolateReload_KeepPragma3) { // Old version of closure function bar() doesn't have a pragma. const char* kScript = "import 'file:///test:isolate_reload_helper';\n" + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " void bar() {}\n" " return bar;\n" @@ -6675,6 +6683,7 @@ TEST_CASE(IsolateReload_KeepPragma3) { // New version of closure function bar() has a pragma. const char* kReloadScript = "import 'file:///test:isolate_reload_helper';\n" + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " @pragma('vm:never-inline')\n" " void bar() {}\n" diff --git a/runtime/vm/isolate_test.cc b/runtime/vm/isolate_test.cc index 9189b861075..c4a8f3e7432 100644 --- a/runtime/vm/isolate_test.cc +++ b/runtime/vm/isolate_test.cc @@ -43,6 +43,7 @@ void IsolateSpawn(const char* platform_script_value) { "}\n", platform_script_value); + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle test_lib = TestCase::LoadTestScript(scriptChars, nullptr); free(scriptChars); @@ -242,6 +243,7 @@ ISOLATE_UNIT_TEST_CASE(Isolate_MayExit_True) { EXPECT_EQ(false, thread->is_unwind_in_progress()); + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_EnterScope(); Dart_Handle lib = @@ -266,6 +268,7 @@ ISOLATE_UNIT_TEST_CASE(Isolate_MayExit_False) { EXPECT_EQ(false, thread->is_unwind_in_progress()); + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_EnterScope(); Dart_Handle lib = diff --git a/runtime/vm/json_test.cc b/runtime/vm/json_test.cc index 704c6b2ac8c..ec5d4009cbc 100644 --- a/runtime/vm/json_test.cc +++ b/runtime/vm/json_test.cc @@ -236,6 +236,7 @@ TEST_CASE(JSON_JSONStream_DartString) { "var wrongEncoding = '\\u{1D11E}' + surrogates[0] + '\\u{1D11E}';" "var nullInMiddle = 'This has\\u0000 four words.';"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, nullptr); EXPECT_VALID(lib); diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc index d57a16b5a79..2bf17b04aba 100644 --- a/runtime/vm/kernel_isolate.cc +++ b/runtime/vm/kernel_isolate.cc @@ -165,8 +165,7 @@ class RunKernelTask : public ThreadPool::Task { return false; } ASSERT(!root_library.IsNull()); - const String& entry_name = String::Handle(Z, String::New("main")); - ASSERT(!entry_name.IsNull()); + const String& entry_name = Symbols::main(); const Function& entry = Function::Handle( Z, root_library.LookupFunctionAllowPrivate(entry_name)); if (entry.IsNull()) { diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index d35413a98c1..0de7818de15 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -4674,10 +4674,205 @@ static ObjectPtr ThrowTypeError(const TokenPosition token_pos, return DartEntry::InvokeFunction(throwNew, args); } +static bool WriteQualifiedMemberName(Zone* zone, + BaseTextBuffer* buffer, + const Object& member) { + if (member.IsFunction()) { + const auto& fun = Function::Cast(member); + const auto& cls = Class::Handle(zone, fun.Owner()); + if (WriteQualifiedMemberName(zone, buffer, cls)) { + buffer->AddString("."); + } + buffer->AddString(fun.UserVisibleNameCString()); + if (!fun.IsRegularFunction()) { + buffer->Printf(" (kind %s)", Function::KindToCString(fun.kind())); + } + } else if (member.IsField()) { + const auto& field = Field::Cast(member); + const auto& cls = Class::Handle(field.Owner()); + if (WriteQualifiedMemberName(zone, buffer, cls)) { + buffer->AddString("."); + } + const auto& name = String::Handle(field.name()); + buffer->AddString(name.ToCString()); + } else if (member.IsClass()) { + const Class& cls = Class::Cast(member); + const Library& lib = Library::Handle(cls.library()); + if (!lib.IsNull()) { + const String& name = String::Handle(lib.url()); + buffer->Printf("%s::", name.ToCString()); + } + if (!cls.IsTopLevel()) { + buffer->AddString(cls.UserVisibleNameCString()); + } + return !cls.IsTopLevel(); + } + return false; +} + +DART_WARN_UNUSED_RESULT +static ErrorPtr VerifyEntryPoint(const Library& lib, + const Object& member, + const Object& annotated, + EntryPointPragma expected) { + ASSERT(expected != EntryPointPragma::kNever); + auto* const thread = Thread::Current(); + auto* const zone = thread->zone(); + // Special cases for certain types of functions that should delegate to + // different members within the same class. + if (member.IsFunction()) { + const auto& fun = Function::Cast(member); + if (fun.IsMethodExtractor()) { + ASSERT(expected == EntryPointPragma::kGetterOnly); + // To be able to call a method extractor, the original method needs to + // be annotated for closure retrieval. + const auto& closure = + Function::Handle(zone, fun.extracted_method_closure()); + return VerifyEntryPoint( + lib, closure, annotated.IsNull() ? annotated : closure, expected); + } else if (fun.IsImplicitClosureFunction()) { + // Check the annotations on the parent function instead. + const auto& parent = Function::Handle(zone, fun.parent_function()); + return VerifyEntryPoint( + lib, parent, annotated.IsNull() ? annotated : parent, expected); + } else if (fun.IsImplicitGetterOrSetter()) { + ASSERT(fun.IsImplicitSetterFunction() || + expected == EntryPointPragma::kGetterOnly); + ASSERT(!fun.IsImplicitSetterFunction() || + expected == EntryPointPragma::kSetterOnly); + // For implicit getters or setters, the field must be properly annotated. + const auto& field = Field::Handle(zone, fun.accessor_field()); +#if defined(DART_PRECOMPILED_RUNTIME) + if (!fun.HasCode()) { + return VerifyEntryPoint(lib, field, Object::null_object(), expected); + } +#endif + return VerifyEntryPoint(lib, field, + annotated.IsNull() ? annotated : field, expected); + } + } + + // For method and fields, the expected pragma should always be more specific. + ASSERT_EQUAL(member.IsClass(), expected == EntryPointPragma::kAlways); + + // A null annotated object is used to signal an error should always be thrown. + if (!annotated.IsNull()) { + bool is_marked_entrypoint = false; +#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 + // "has_pragma()" as a proxy, since that bit is usually retained. + if (annotated.IsClass()) { + is_marked_entrypoint = Class::Cast(annotated).has_pragma(); + } else if (annotated.IsField()) { + is_marked_entrypoint = Field::Cast(annotated).has_pragma(); + } else if (annotated.IsFunction()) { + const auto& f = Function::Cast(annotated); + is_marked_entrypoint = f.has_pragma(); + if (expected == EntryPointPragma::kCallOnly && !f.HasCode()) { + // If the function does not have code attached, that means it was not + // properly annotated to allow native invocation. + is_marked_entrypoint = false; + } else if (expected == EntryPointPragma::kGetterOnly && + !f.HasImplicitClosureFunction()) { + // If the function does not have an implicit closure function, that + // means it was not properly annotated to allow native closurization. + is_marked_entrypoint = false; + } + } else { + FATAL("Unexpected annotated node %s", annotated.ToCString()); + } +#else + const auto& metadata = Object::Handle(zone, lib.GetMetadata(annotated)); + if (metadata.IsError()) { + return Error::RawCast(metadata.ptr()); + } + ASSERT(!metadata.IsNull() && metadata.IsArray()); + const EntryPointPragma pragma = + FindEntryPointPragma(thread->isolate_group(), Array::Cast(metadata), + &Field::Handle(zone), &Object::Handle(zone)); + ASSERT(pragma != EntryPointPragma::kCallOnly || annotated.IsFunction()); + is_marked_entrypoint = + pragma == EntryPointPragma::kAlways || pragma == expected; +#endif + if (is_marked_entrypoint) { + return Error::null(); + } + } + + ZoneTextBuffer buffer(zone); + switch (expected) { + case EntryPointPragma::kAlways: + buffer.AddString("ERROR: To access '"); + WriteQualifiedMemberName(zone, &buffer, member); + buffer.AddString("' from native code, it must be annotated.\n"); + break; + case EntryPointPragma::kCallOnly: + ASSERT(member.IsFunction()); + buffer.AddString("ERROR: To invoke '"); + WriteQualifiedMemberName(zone, &buffer, member); + buffer.AddString("' from native code, it must be annotated.\n"); + break; + case EntryPointPragma::kGetterOnly: + if (member.IsField() || + (member.IsFunction() && Function::Cast(member).IsGetterFunction())) { + buffer.AddString("ERROR: To retrieve the value of '"); + WriteQualifiedMemberName(zone, &buffer, member); + buffer.AddString("' from native code, it must be annotated.\n"); + } else { + const auto& function = Function::Cast(member); + // Other types of functions cannot be closurized. + if (!function.IsRegularFunction()) { + // Other types of functions reaching here are internal errors. + buffer.AddString("Cannot get closure value for method '"); + WriteQualifiedMemberName(zone, &buffer, function); + buffer.Printf("' of kind %s", + Function::KindToCString(function.kind())); + FATAL("%s", buffer.buffer()); + } + buffer.AddString("ERROR: To closurize '"); + WriteQualifiedMemberName(zone, &buffer, function); + buffer.AddString("' from native code, it must be annotated.\n"); + } + break; + case EntryPointPragma::kSetterOnly: + if (member.IsField() || + (member.IsFunction() && Function::Cast(member).IsSetterFunction())) { + buffer.AddString("ERROR: To set the value of '"); + WriteQualifiedMemberName(zone, &buffer, member); + buffer.AddString("' from native code, it must be annotated.\n"); + } else { + // Other types of functions reaching here are internal errors. + buffer.AddString("Cannot set value of '"); + WriteQualifiedMemberName(zone, &buffer, member); + buffer.AddString("'"); + FATAL("%s", buffer.buffer()); + } + break; + default: + FATAL("Unexpected EntryPointPragma value %" Pd, + static_cast(expected)); + } + buffer.AddString( + "ERROR: See https://github.com/dart-lang/sdk/blob/master/runtime/" + "docs/compiler/aot/entry_point_pragma.md\n"); + OS::PrintErr("%s", buffer.buffer()); + return ApiError::New(String::Handle(String::New(buffer.buffer()))); +} + +#if defined(DART_PRECOMPILED_RUNTIME) +DART_WARN_UNUSED_RESULT +static ErrorPtr EntryPointFunctionInvocationError(const Function& function) { + // Ensures a failure by passing null for the annotated object. + return VerifyEntryPoint(Library::Handle(Library::null()), function, + Object::null_object(), EntryPointPragma::kCallOnly); +} +#endif + ObjectPtr Class::InvokeGetter(const String& getter_name, - bool throw_nsm_if_absent, + bool check_is_entrypoint, bool respect_reflectable, - bool check_is_entrypoint) const { + bool for_invocation) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -4686,28 +4881,27 @@ ObjectPtr Class::InvokeGetter(const String& getter_name, // Note static fields do not have implicit getters. const Field& field = Field::Handle(zone, LookupStaticField(getter_name)); - if (!field.IsNull() && check_is_entrypoint) { - CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); - } - if (field.IsNull() || field.IsUninitialized()) { const String& internal_getter_name = String::Handle(zone, Field::GetterName(getter_name)); Function& getter = Function::Handle(zone, LookupStaticFunction(internal_getter_name)); - if (field.IsNull() && !getter.IsNull() && check_is_entrypoint) { - CHECK_ERROR(getter.VerifyCallEntryPoint()); - } - if (getter.IsNull() || (respect_reflectable && !getter.is_reflectable())) { - if (getter.IsNull()) { + if (for_invocation) { + // LookupStaticFunction(getter_name) has already failed in Invoke(). + // Instead of throwing an NSM, indicate that no getter was found + // by returning a value that cannot be returned by a getter (here, + // the sentinel value). + return Object::sentinel().ptr(); + } else if (getter.IsNull()) { getter = LookupStaticFunction(getter_name); if (!getter.IsNull()) { - if (check_is_entrypoint) { - CHECK_ERROR(getter.VerifyClosurizedEntryPoint()); - } if (getter.SafeToClosurize()) { + if (check_is_entrypoint) { + CHECK_ERROR( + getter.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } // Looking for a getter but found a regular method: closurize it. const Function& closure_function = Function::Handle(zone, getter.ImplicitClosureFunction()); @@ -4715,29 +4909,32 @@ ObjectPtr Class::InvokeGetter(const String& getter_name, } } } - if (throw_nsm_if_absent) { - return ThrowNoSuchMethod( - AbstractType::Handle(zone, RareType()), getter_name, - Object::null_array(), Object::null_array(), - InvocationMirror::kStatic, InvocationMirror::kGetter); - } - // Fall through case: Indicate that we didn't find any function or field - // using a special null instance. This is different from a field being - // null. Callers make sure that this null does not leak into Dartland. - return Object::sentinel().ptr(); + + return ThrowNoSuchMethod(AbstractType::Handle(zone, RareType()), + getter_name, Object::null_array(), + Object::null_array(), InvocationMirror::kStatic, + InvocationMirror::kGetter); + } + + if (check_is_entrypoint) { + CHECK_ERROR(getter.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); } // Invoke the getter and return the result. return DartEntry::InvokeFunction(getter, Object::empty_array()); } + if (check_is_entrypoint) { + CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } + return field.StaticValue(); } ObjectPtr Class::InvokeSetter(const String& setter_name, const Instance& value, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -4748,17 +4945,9 @@ ObjectPtr Class::InvokeSetter(const String& setter_name, const String& internal_setter_name = String::Handle(zone, Field::SetterName(setter_name)); - if (!field.IsNull() && check_is_entrypoint) { - CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); - } - - AbstractType& parameter_type = AbstractType::Handle(zone); if (field.IsNull()) { const Function& setter = Function::Handle(zone, LookupStaticFunction(internal_setter_name)); - if (!setter.IsNull() && check_is_entrypoint) { - CHECK_ERROR(setter.VerifyCallEntryPoint()); - } const int kNumArgs = 1; const Array& args = Array::Handle(zone, Array::New(kNumArgs)); args.SetAt(0, value); @@ -4768,14 +4957,15 @@ ObjectPtr Class::InvokeSetter(const String& setter_name, InvocationMirror::kStatic, InvocationMirror::kSetter); } - parameter_type = setter.ParameterTypeAt(0); - if (!value.RuntimeTypeIsSubtypeOf(parameter_type, - Object::null_type_arguments(), + const auto& type = AbstractType::Handle(zone, setter.ParameterTypeAt(0)); + if (!value.RuntimeTypeIsSubtypeOf(type, Object::null_type_arguments(), Object::null_type_arguments())) { const String& argument_name = String::Handle(zone, setter.ParameterNameAt(0)); - return ThrowTypeError(setter.token_pos(), value, parameter_type, - argument_name); + return ThrowTypeError(setter.token_pos(), value, type, argument_name); + } + if (check_is_entrypoint) { + CHECK_ERROR(setter.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); } // Invoke the setter and return the result. return DartEntry::InvokeFunction(setter, args); @@ -4791,13 +4981,14 @@ ObjectPtr Class::InvokeSetter(const String& setter_name, InvocationMirror::kSetter); } - parameter_type = field.type(); - if (!value.RuntimeTypeIsSubtypeOf(parameter_type, - Object::null_type_arguments(), + const auto& type = AbstractType::Handle(zone, field.type()); + if (!value.RuntimeTypeIsSubtypeOf(type, Object::null_type_arguments(), Object::null_type_arguments())) { const String& argument_name = String::Handle(zone, field.name()); - return ThrowTypeError(field.token_pos(), value, parameter_type, - argument_name); + return ThrowTypeError(field.token_pos(), value, type, argument_name); + } + if (check_is_entrypoint) { + CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); } field.SetStaticValue(value); return value.ptr(); @@ -4844,8 +5035,8 @@ static ArrayPtr CreateCallableArgumentsFromStatic( ObjectPtr Class::Invoke(const String& function_name, const Array& args, const Array& arg_names, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); CHECK_ERROR(EnsureIsFinalized(thread)); @@ -4861,19 +5052,15 @@ ObjectPtr Class::Invoke(const String& function_name, Function& function = Function::Handle(zone, LookupStaticFunction(function_name)); - if (!function.IsNull() && check_is_entrypoint) { - CHECK_ERROR(function.VerifyCallEntryPoint()); - } - if (function.IsNull()) { // Didn't find a method: try to find a getter and invoke call on its result. - const Object& getter_result = Object::Handle( - zone, InvokeGetter(function_name, false, respect_reflectable, - check_is_entrypoint)); - if (getter_result.ptr() != Object::sentinel().ptr()) { - if (check_is_entrypoint) { - CHECK_ERROR(EntryPointFieldInvocationError(function_name)); - } + const Object& getter_result = + Object::Handle(zone, InvokeGetter(function_name, check_is_entrypoint, + respect_reflectable, + /*for_invocation=*/true)); + if (getter_result.IsError()) { + return getter_result.ptr(); + } else if (getter_result.ptr() != Object::sentinel().ptr()) { const auto& call_args_descriptor_array = Array::Handle( zone, ArgumentsDescriptor::NewBoxed(args_descriptor.TypeArgsLen(), args_descriptor.Count() + 1, @@ -4901,6 +5088,9 @@ ObjectPtr Class::Invoke(const String& function_name, if (type_error != Error::null()) { return type_error; } + if (check_is_entrypoint) { + CHECK_ERROR(function.VerifyEntryPoint(EntryPointPragma::kCallOnly)); + } return DartEntry::InvokeFunction(function, args, args_descriptor_array); } @@ -6638,12 +6828,13 @@ FieldPtr Class::LookupStaticFieldAllowPrivate(const String& name) const { } const char* Class::ToCString() const { - NoSafepointScope no_safepoint; + ZoneTextBuffer buffer(Thread::Current()->zone()); const Library& lib = Library::Handle(library()); - const char* library_name = lib.IsNull() ? "" : lib.ToCString(); - const char* class_name = String::Handle(Name()).ToCString(); - return OS::SCreate(Thread::Current()->zone(), "%s Class: %s", library_name, - class_name); + if (!lib.IsNull()) { + buffer.Printf("%s ", lib.ToCString()); + } + buffer.Printf("Class: %s", String::Handle(Name()).ToCString()); + return buffer.buffer(); } // Thomas Wang, Integer Hash Functions. @@ -9708,7 +9899,7 @@ ObjectPtr Function::DoArgumentTypesMatch( #if defined(DART_PRECOMPILED_RUNTIME) if (signature() == FunctionType::null()) { // Precompiler deleted signature because of missing entry point pragma. - return EntryPointMemberInvocationError(*this); + return EntryPointFunctionInvocationError(*this); } #endif Thread* thread = Thread::Current(); @@ -9731,7 +9922,7 @@ ObjectPtr Function::DoArgumentTypesMatch( #if defined(DART_PRECOMPILED_RUNTIME) if (signature() == FunctionType::null()) { // Precompiler deleted signature because of missing entry point pragma. - return EntryPointMemberInvocationError(*this); + return EntryPointFunctionInvocationError(*this); } #endif Thread* thread = Thread::Current(); @@ -9758,7 +9949,7 @@ ObjectPtr Function::DoArgumentTypesMatch( #if defined(DART_PRECOMPILED_RUNTIME) if (signature() == FunctionType::null()) { // Precompiler deleted signature because of missing entry point pragma. - return EntryPointMemberInvocationError(*this); + return EntryPointFunctionInvocationError(*this); } #endif Thread* thread = Thread::Current(); @@ -10572,13 +10763,22 @@ bool Function::SafeToClosurize() const { #endif } -bool Function::IsDynamicClosureCallDispatcher(Thread* thread) const { +bool Function::IsDynamicClosureCallDispatcher() const { if (!IsInvokeFieldDispatcher()) return false; - if (thread->isolate_group()->object_store()->closure_class() != Owner()) { - return false; - } - const auto& handle = String::Handle(thread->zone(), name()); - return handle.Equals(Symbols::DynamicCall()); + if (!Class::IsClosureClass(Owner())) return false; + return name() == Symbols::DynamicCall().ptr(); +} + +bool Function::IsClosureCallDispatcher() const { + if (!IsInvokeFieldDispatcher()) return false; + if (!Class::IsClosureClass(Owner())) return false; + return name() == Symbols::call().ptr(); +} + +bool Function::IsClosureCallGetter() const { + if (!IsGetterFunction()) return false; + if (!Class::IsClosureClass(Owner())) return false; + return name() == Symbols::GetCall().ptr(); } FunctionPtr Function::ImplicitClosureFunction() const { @@ -11674,14 +11874,6 @@ bool Function::NeedsMonomorphicCheckedEntry(Zone* zone) const { bool Function::HasDynamicCallers(Zone* zone) const { #if !defined(DART_PRECOMPILED_RUNTIME) - // Issue(dartbug.com/42719): - // Right now the metadata of _Closure.call says there are no dynamic callers - - // even though there can be. To be conservative we return true. - if ((name() == Symbols::GetCall().ptr() || name() == Symbols::call().ptr()) && - Class::IsClosureClass(Owner())) { - return true; - } - // Use the results of TFA to determine whether this function is ever // called dynamically, i.e. using switchable calls. kernel::ProcedureAttributesMetadata metadata; @@ -14649,6 +14841,8 @@ static ObjectPtr InvokeInstanceFunction( const String& target_name, const Array& args, const Array& args_descriptor_array, + bool check_is_entrypoint, + EntryPointPragma pragma, bool respect_reflectable, const TypeArguments& instantiator_type_args) { // Note "args" is already the internal arguments with the receiver as the @@ -14665,21 +14859,30 @@ static ObjectPtr InvokeInstanceFunction( if (type_error != Error::null()) { return type_error; } + if (check_is_entrypoint) { + CHECK_ERROR(function.VerifyEntryPoint(pragma)); + } return DartEntry::InvokeFunction(function, args, args_descriptor_array); } +static bool IsLookupOfMainFunctionInRootLibrary(const Library& lib, + const String& name) { + return name.Equals(Symbols::main()) && + lib.ptr() == IsolateGroup::Current()->object_store()->root_library(); +} + ObjectPtr Library::InvokeGetter(const String& getter_name, - bool throw_nsm_if_absent, + bool check_is_entrypoint, bool respect_reflectable, - bool check_is_entrypoint) const { + bool for_invocation) const { Object& obj = Object::Handle(LookupLocalOrReExportObject(getter_name)); Function& getter = Function::Handle(); if (obj.IsField()) { const Field& field = Field::Cast(obj); - if (check_is_entrypoint) { - CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); - } if (!field.IsUninitialized()) { + if (check_is_entrypoint) { + CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } return field.StaticValue(); } // An uninitialized field was found. Check for a getter in the field's @@ -14695,51 +14898,53 @@ ObjectPtr Library::InvokeGetter(const String& getter_name, obj = LookupLocalOrReExportObject(internal_getter_name); if (obj.IsFunction()) { getter = Function::Cast(obj).ptr(); - if (check_is_entrypoint) { - CHECK_ERROR(getter.VerifyCallEntryPoint()); - } - } else { + } else if (!for_invocation) { + // No need to re-lookup the getter name if coming from Invoke(), since + // it already failed there. obj = LookupLocalOrReExportObject(getter_name); - // Normally static top-level methods cannot be closurized through the - // native API even if they are marked as entry-points, with the one - // exception of "main". - if (obj.IsFunction() && check_is_entrypoint) { - if (!getter_name.Equals(String::Handle(String::New("main"))) || - ptr() != IsolateGroup::Current()->object_store()->root_library()) { - CHECK_ERROR(Function::Cast(obj).VerifyClosurizedEntryPoint()); + if (obj.IsFunction()) { + const auto& function = Function::Cast(obj); + if (function.SafeToClosurize()) { + // The main function of the root library always has a retained + // implicit static closure. + if (check_is_entrypoint && + !IsLookupOfMainFunctionInRootLibrary(*this, getter_name)) { + CHECK_ERROR( + function.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } + // Looking for a getter but found a regular method: closurize it. + const auto& closure_function = + Function::Handle(function.ImplicitClosureFunction()); + return closure_function.ImplicitStaticClosure(); } } - if (obj.IsFunction() && Function::Cast(obj).SafeToClosurize()) { - // Looking for a getter but found a regular method: closurize it. - const Function& closure_function = - Function::Handle(Function::Cast(obj).ImplicitClosureFunction()); - return closure_function.ImplicitStaticClosure(); - } } } if (getter.IsNull() || (respect_reflectable && !getter.is_reflectable())) { - if (throw_nsm_if_absent) { - return ThrowNoSuchMethod(Object::null_string(), getter_name, - Object::null_array(), Object::null_array(), - InvocationMirror::kTopLevel, - InvocationMirror::kGetter); + if (for_invocation) { + // Instead of throwing an NSM, indicate that no getter was found by + // returning a value that cannot be returned by a getter (here, + // the sentinel value). + return Object::sentinel().ptr(); } - - // Fall through case: Indicate that we didn't find any function or field - // using a special null instance. This is different from a field being null. - // Callers make sure that this null does not leak into Dartland. - return Object::sentinel().ptr(); + return ThrowNoSuchMethod(Object::null_string(), getter_name, + Object::null_array(), Object::null_array(), + InvocationMirror::kTopLevel, + InvocationMirror::kGetter); } + if (check_is_entrypoint) { + CHECK_ERROR(getter.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } // Invoke the getter and return the result. return DartEntry::InvokeFunction(getter, Object::empty_array()); } ObjectPtr Library::InvokeSetter(const String& setter_name, const Instance& value, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Object& obj = Object::Handle(LookupLocalOrReExportObject(setter_name)); const String& internal_setter_name = String::Handle(Field::SetterName(setter_name)); @@ -14747,9 +14952,6 @@ ObjectPtr Library::InvokeSetter(const String& setter_name, AbstractType& argument_type = AbstractType::Handle(value.GetType(Heap::kOld)); if (obj.IsField()) { const Field& field = Field::Cast(obj); - if (check_is_entrypoint) { - CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); - } setter_type = field.type(); if (!argument_type.IsNullType() && !setter_type.IsDynamicType() && !value.IsInstanceOf(setter_type, Object::null_type_arguments(), @@ -14766,6 +14968,9 @@ ObjectPtr Library::InvokeSetter(const String& setter_name, InvocationMirror::kTopLevel, InvocationMirror::kSetter); } + if (check_is_entrypoint) { + CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); + } field.SetStaticValue(value); return value.ptr(); } @@ -14776,10 +14981,6 @@ ObjectPtr Library::InvokeSetter(const String& setter_name, setter ^= obj.ptr(); } - if (!setter.IsNull() && check_is_entrypoint) { - CHECK_ERROR(setter.VerifyCallEntryPoint()); - } - const int kNumArgs = 1; const Array& args = Array::Handle(Array::New(kNumArgs)); args.SetAt(0, value); @@ -14796,14 +14997,17 @@ ObjectPtr Library::InvokeSetter(const String& setter_name, return ThrowTypeError(setter.token_pos(), value, setter_type, setter_name); } + if (check_is_entrypoint) { + CHECK_ERROR(setter.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); + } return DartEntry::InvokeFunction(setter, args); } ObjectPtr Library::Invoke(const String& function_name, const Array& args, const Array& arg_names, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -14822,19 +15026,15 @@ ObjectPtr Library::Invoke(const String& function_name, function ^= result.ptr(); } - if (!function.IsNull() && check_is_entrypoint) { - CHECK_ERROR(function.VerifyCallEntryPoint()); - } - if (function.IsNull()) { // Didn't find a method: try to find a getter and invoke call on its result. - const Object& getter_result = Object::Handle( - zone, InvokeGetter(function_name, false, respect_reflectable, - check_is_entrypoint)); - if (getter_result.ptr() != Object::sentinel().ptr()) { - if (check_is_entrypoint) { - CHECK_ERROR(EntryPointFieldInvocationError(function_name)); - } + const Object& getter_result = + Object::Handle(zone, InvokeGetter(function_name, check_is_entrypoint, + respect_reflectable, + /*for_invocation=*/true)); + if (getter_result.IsError()) { + return getter_result.ptr(); + } else if (getter_result.ptr() != Object::sentinel().ptr()) { const auto& call_args_descriptor_array = Array::Handle( zone, ArgumentsDescriptor::NewBoxed(args_descriptor.TypeArgsLen(), args_descriptor.Count() + 1, @@ -14866,6 +15066,11 @@ ObjectPtr Library::Invoke(const String& function_name, if (type_error != Error::null()) { return type_error; } + // The main function of the root library is always callable. + if (check_is_entrypoint && + !IsLookupOfMainFunctionInRootLibrary(*this, function_name)) { + CHECK_ERROR(function.VerifyEntryPoint(EntryPointPragma::kCallOnly)); + } return DartEntry::InvokeFunction(function, args, args_descriptor_array); } @@ -20538,8 +20743,8 @@ const char* UnwindError::ToCString() const { } ObjectPtr Instance::InvokeGetter(const String& getter_name, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -20557,30 +20762,15 @@ ObjectPtr Instance::InvokeGetter(const String& getter_name, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name, /*allow_add=*/!FLAG_precompiled_mode)); - 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); - if (function.kind() == UntaggedFunction::kImplicitGetter) { - field = function.accessor_field(); - } - if (!field.IsNull()) { - CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); - } else { - CHECK_ERROR(function.VerifyCallEntryPoint()); - } - } - // Check for method extraction when method extractors are not lazily created. if (function.IsNull() && FLAG_precompiled_mode) { function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name, /*allow_add=*/false); - if (!function.IsNull() && check_is_entrypoint) { - CHECK_ERROR(function.VerifyClosurizedEntryPoint()); - } - if (!function.IsNull() && function.SafeToClosurize()) { + if (check_is_entrypoint) { + CHECK_ERROR(function.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } const Function& closure_function = Function::Handle(zone, function.ImplicitClosureFunction()); return closure_function.ImplicitInstanceClosure(*this); @@ -20596,14 +20786,15 @@ ObjectPtr Instance::InvokeGetter(const String& getter_name, ArgumentsDescriptor::NewBoxed(kTypeArgsLen, args.Length(), Heap::kNew)); return InvokeInstanceFunction(thread, *this, function, internal_getter_name, - args, args_descriptor, respect_reflectable, - inst_type_args); + args, args_descriptor, check_is_entrypoint, + EntryPointPragma::kGetterOnly, + respect_reflectable, inst_type_args); } ObjectPtr Instance::InvokeSetter(const String& setter_name, const Instance& value, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -20621,20 +20812,6 @@ ObjectPtr Instance::InvokeSetter(const String& setter_name, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name, /*allow_add=*/!FLAG_precompiled_mode)); - if (check_is_entrypoint) { - // The setter must correspond to either an entry-point field or a setter - // method explicitly marked. - Field& field = Field::Handle(zone); - if (setter.kind() == UntaggedFunction::kImplicitSetter) { - field = setter.accessor_field(); - } - if (!field.IsNull()) { - CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); - } else if (!setter.IsNull()) { - CHECK_ERROR(setter.VerifyCallEntryPoint()); - } - } - const int kTypeArgsLen = 0; const int kNumArgs = 2; const Array& args = Array::Handle(zone, Array::New(kNumArgs)); @@ -20645,15 +20822,16 @@ ObjectPtr Instance::InvokeSetter(const String& setter_name, ArgumentsDescriptor::NewBoxed(kTypeArgsLen, args.Length(), Heap::kNew)); return InvokeInstanceFunction(thread, *this, setter, internal_setter_name, - args, args_descriptor, respect_reflectable, - inst_type_args); + args, args_descriptor, check_is_entrypoint, + EntryPointPragma::kSetterOnly, + respect_reflectable, inst_type_args); } ObjectPtr Instance::Invoke(const String& function_name, const Array& args, const Array& arg_names, - bool respect_reflectable, - bool check_is_entrypoint) const { + bool check_is_entrypoint, + bool respect_reflectable) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); Class& klass = Class::Handle(zone, clazz()); @@ -20664,10 +20842,6 @@ ObjectPtr Instance::Invoke(const String& function_name, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name, /*allow_add=*/!FLAG_precompiled_mode)); - if (!function.IsNull() && check_is_entrypoint) { - CHECK_ERROR(function.VerifyCallEntryPoint()); - } - // We don't pass any explicit type arguments, which will be understood as // using dynamic for any function type arguments by lower layers. const int kTypeArgsLen = 0; @@ -20688,9 +20862,6 @@ ObjectPtr Instance::Invoke(const String& function_name, Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name, /*allow_add=*/!FLAG_precompiled_mode); if (!function.IsNull()) { - if (check_is_entrypoint) { - CHECK_ERROR(EntryPointFieldInvocationError(function_name)); - } ASSERT(function.kind() != UntaggedFunction::kMethodExtractor); // Invoke the getter. const int kNumArgs = 1; @@ -20702,6 +20873,8 @@ ObjectPtr Instance::Invoke(const String& function_name, const Object& getter_result = Object::Handle( zone, InvokeInstanceFunction(thread, *this, function, getter_name, getter_args, getter_args_descriptor, + check_is_entrypoint, + EntryPointPragma::kGetterOnly, respect_reflectable, inst_type_args)); if (getter_result.IsError()) { return getter_result.ptr(); @@ -20714,8 +20887,9 @@ ObjectPtr Instance::Invoke(const String& function_name, // Found an ordinary method. return InvokeInstanceFunction(thread, *this, function, function_name, args, - args_descriptor, respect_reflectable, - inst_type_args); + args_descriptor, check_is_entrypoint, + EntryPointPragma::kCallOnly, + respect_reflectable, inst_type_args); } ObjectPtr Instance::HashCode() const { @@ -27624,100 +27798,6 @@ EntryPointPragma FindEntryPointPragma(IsolateGroup* IG, return EntryPointPragma::kNever; } -DART_WARN_UNUSED_RESULT -ErrorPtr VerifyEntryPoint( - const Library& lib, - const Object& member, - const Object& annotated, - std::initializer_list 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 - // "has_pragma()" as a proxy, since that bit is usually retained. - bool is_marked_entrypoint = true; - if (annotated.IsClass() && !Class::Cast(annotated).has_pragma()) { - is_marked_entrypoint = false; - } else if (annotated.IsField() && !Field::Cast(annotated).has_pragma()) { - is_marked_entrypoint = false; - } else if (annotated.IsFunction() && - !Function::Cast(annotated).has_pragma()) { - is_marked_entrypoint = false; - } -#else - Object& metadata = Object::Handle(Object::empty_array().ptr()); - if (!annotated.IsNull()) { - metadata = lib.GetMetadata(annotated); - } - if (metadata.IsError()) return Error::RawCast(metadata.ptr()); - ASSERT(!metadata.IsNull() && metadata.IsArray()); - EntryPointPragma pragma = - FindEntryPointPragma(IsolateGroup::Current(), Array::Cast(metadata), - &Field::Handle(), &Object::Handle()); - 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) { - return EntryPointMemberInvocationError(member); - } - return Error::null(); -} - -DART_WARN_UNUSED_RESULT -ErrorPtr 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 invoking fields " - "(failure to resolve '%s')\n" - "ERROR: See " - "https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/" - "aot/entry_point_pragma.md\n", - getter_name.ToCString()); - OS::PrintErr("%s", error); - return ApiError::New(String::Handle(String::New(error))); -} - -DART_WARN_UNUSED_RESULT -ErrorPtr EntryPointMemberInvocationError(const Object& member) { - const char* member_cstring = - member.IsFunction() - ? OS::SCreate( - Thread::Current()->zone(), "%s (kind %s)", - Function::Cast(member).ToLibNamePrefixedQualifiedCString(), - Function::KindToCString(Function::Cast(member).kind())) - : member.ToCString(); - if (!FLAG_verify_entry_points) { - // Print a warning, but do not return an error. - char const* warning = OS::SCreate( - Thread::Current()->zone(), - "WARNING: '%s' is accessed through Dart C API without being marked as " - "an entry point; its tree-shaken signature cannot be verified.\n" - "WARNING: See " - "https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/" - "aot/entry_point_pragma.md\n", - member_cstring); - OS::PrintErr("%s", warning); - return Error::null(); - } - char const* error = OS::SCreate( - Thread::Current()->zone(), - "ERROR: It is illegal to access '%s' through Dart C API.\n" - "ERROR: See " - "https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/" - "aot/entry_point_pragma.md\n", - member_cstring); - OS::PrintErr("%s", error); - return ApiError::New(String::Handle(String::New(error))); -} - #if !defined(DART_PRECOMPILED_RUNTIME) // Note: see also [NeedsDynamicInvocationForwarder] which ensures that we // never land in a function which expects parameters in registers from a @@ -27784,74 +27864,34 @@ intptr_t Function::MaxNumberOfParametersInRegisters(Zone* zone) const { } #endif // !defined(DART_PRECOMPILED_RUNTIME) -ErrorPtr Function::VerifyCallEntryPoint() const { - if (!FLAG_verify_entry_points) return Error::null(); - +ErrorPtr Function::VerifyEntryPoint(EntryPointPragma pragma) const { +#if defined(DART_PRECOMPILED_RUNTIME) + const Library& lib = Library::Handle(); +#else const Class& cls = Class::Handle(Owner()); const Library& lib = Library::Handle(cls.library()); - switch (kind()) { - case UntaggedFunction::kRegularFunction: - case UntaggedFunction::kSetterFunction: - case UntaggedFunction::kConstructor: - return dart::VerifyEntryPoint(lib, *this, *this, - {EntryPointPragma::kCallOnly}); - break; - case UntaggedFunction::kGetterFunction: - return dart::VerifyEntryPoint( - lib, *this, *this, - {EntryPointPragma::kCallOnly, EntryPointPragma::kGetterOnly}); - break; - case UntaggedFunction::kImplicitGetter: - return dart::VerifyEntryPoint(lib, *this, Field::Handle(accessor_field()), - {EntryPointPragma::kGetterOnly}); - break; - case UntaggedFunction::kImplicitSetter: - return dart::VerifyEntryPoint(lib, *this, Field::Handle(accessor_field()), - {EntryPointPragma::kSetterOnly}); - case UntaggedFunction::kMethodExtractor: - return Function::Handle(extracted_method_closure()) - .VerifyClosurizedEntryPoint(); - break; - default: - return dart::VerifyEntryPoint(lib, *this, Object::Handle(), {}); - break; - } -} - -ErrorPtr 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 UntaggedFunction::kRegularFunction: - return dart::VerifyEntryPoint(lib, *this, *this, - {EntryPointPragma::kGetterOnly}); - case UntaggedFunction::kImplicitClosureFunction: { - const Function& parent = Function::Handle(parent_function()); - return dart::VerifyEntryPoint(lib, parent, parent, - {EntryPointPragma::kGetterOnly}); - } - default: - UNREACHABLE(); - } +#endif + return dart::VerifyEntryPoint(lib, *this, *this, pragma); } ErrorPtr Field::VerifyEntryPoint(EntryPointPragma pragma) const { - if (!FLAG_verify_entry_points) return Error::null(); +#if defined(DART_PRECOMPILED_RUNTIME) + const Library& lib = Library::Handle(); +#else const Class& cls = Class::Handle(Owner()); const Library& lib = Library::Handle(cls.library()); - return dart::VerifyEntryPoint(lib, *this, *this, {pragma}); +#endif + return dart::VerifyEntryPoint(lib, *this, *this, pragma); } ErrorPtr Class::VerifyEntryPoint() const { - if (!FLAG_verify_entry_points) return Error::null(); +#if defined(DART_PRECOMPILED_RUNTIME) + const Library& lib = Library::Handle(); +#else const Library& lib = Library::Handle(library()); - if (!lib.IsNull()) { - return dart::VerifyEntryPoint(lib, *this, *this, {}); - } else { - return Error::null(); - } + if (lib.IsNull()) return Error::null(); +#endif + return dart::VerifyEntryPoint(lib, *this, *this, EntryPointPragma::kAlways); } AbstractTypePtr RecordType::FieldTypeAt(intptr_t index) const { diff --git a/runtime/vm/object.h b/runtime/vm/object.h index dad3244e246..5f6ff4f2276 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -1878,16 +1878,16 @@ class Class : public Object { ObjectPtr Invoke(const String& selector, const Array& arguments, const Array& argument_names, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; ObjectPtr InvokeGetter(const String& selector, - bool throw_nsm_if_absent, + bool check_is_entrypoint = true, bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool for_invocation = false) const; ObjectPtr InvokeSetter(const String& selector, const Instance& argument, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; // Evaluate the given expression as if it appeared in a static method of this // class and return the resulting value, or an error object if evaluating the @@ -2973,6 +2973,14 @@ enum class FfiCallbackKind : uint8_t { kAsyncCallback, }; +enum class EntryPointPragma { + kAlways, + kNever, + kGetterOnly, + kSetterOnly, + kCallOnly +}; + class Function : public Object { public: StringPtr name() const { return untag()->name(); } @@ -3312,14 +3320,17 @@ class Function : public Object { IsDynamicInvocationForwarderName(name()); } - // Performs all the checks that don't require the current thread first, to - // avoid retrieving it unless they all pass. If you have a handle on the - // current thread, call the version that takes one instead. - bool IsDynamicClosureCallDispatcher() const { - if (!IsDynamicInvokeFieldDispatcher()) return false; - return IsDynamicClosureCallDispatcher(Thread::Current()); - } - bool IsDynamicClosureCallDispatcher(Thread* thread) const; + // Returns true if this function is _Closure.dyn:call, which implements + // dynamically checked closure calls. + bool IsDynamicClosureCallDispatcher() const; + + // Returns true if this function is _Closure.call, which implements the + // Function interface for closures. + bool IsClosureCallDispatcher() const; + + // Returns true if this function is _Closure.get:call, which returns the + // closure object for invocation. + bool IsClosureCallGetter() const; bool IsDynamicInvocationForwarder() const { return kind() == UntaggedFunction::kDynamicInvocationForwarder; @@ -3993,10 +4004,7 @@ class Function : public Object { bool IsUnmodifiableTypedDataViewFactory() const; DART_WARN_UNUSED_RESULT - ErrorPtr VerifyCallEntryPoint() const; - - DART_WARN_UNUSED_RESULT - ErrorPtr VerifyClosurizedEntryPoint() const; + ErrorPtr VerifyEntryPoint(EntryPointPragma pragma) const; static intptr_t InstanceSize() { return RoundedAllocationSize(sizeof(UntaggedFunction)); @@ -4365,14 +4373,6 @@ class ClosureData : public Object { friend class Precompiler; // To wrap parent functions in WSRs. }; -enum class EntryPointPragma { - kAlways, - kNever, - kGetterOnly, - kSetterOnly, - kCallOnly -}; - class FfiTrampolineData : public Object { public: static intptr_t InstanceSize() { @@ -5129,16 +5129,16 @@ class Library : public Object { ObjectPtr Invoke(const String& selector, const Array& arguments, const Array& argument_names, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; ObjectPtr InvokeGetter(const String& selector, - bool throw_nsm_if_absent, + bool check_is_entrypoint = true, bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool for_invocation = false) const; ObjectPtr InvokeSetter(const String& selector, const Instance& argument, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; // Evaluate the given expression as if it appeared in an top-level method of // this library and return the resulting value, or an error object if @@ -8393,15 +8393,15 @@ class Instance : public Object { ObjectPtr Invoke(const String& selector, const Array& arguments, const Array& argument_names, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; ObjectPtr InvokeGetter(const String& selector, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; ObjectPtr InvokeSetter(const String& selector, const Instance& argument, - bool respect_reflectable = true, - bool check_is_entrypoint = false) const; + bool check_is_entrypoint = true, + bool respect_reflectable = true) const; ObjectPtr EvaluateCompiledExpression( const Class& klass, @@ -13671,12 +13671,6 @@ EntryPointPragma FindEntryPointPragma(IsolateGroup* isolate_group, Field* reusable_field_handle, Object* reusable_object_handle); -DART_WARN_UNUSED_RESULT -ErrorPtr EntryPointFieldInvocationError(const String& getter_name); - -DART_WARN_UNUSED_RESULT -ErrorPtr EntryPointMemberInvocationError(const Object& member); - #undef PRECOMPILER_WSR_FIELD_DECLARATION } // namespace dart diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc index cf9a390f3bf..4d68968b5e3 100644 --- a/runtime/vm/object_test.cc +++ b/runtime/vm/object_test.cc @@ -5591,6 +5591,7 @@ TEST_CASE(FunctionWithBreakpointNotInlined) { " a();\n" // This is line 5. " }\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "test() {\n" " new A().b();\n" "}"; @@ -5634,7 +5635,7 @@ TEST_CASE(FunctionWithBreakpointNotInlined) { void SetBreakpoint(Dart_NativeArguments args) { // Refers to the DeoptimizeFramesWhenSettingBreakpoint function below. - const int kBreakpointLine = 9; + const int kBreakpointLine = 10; // This will force deoptimization of functions on stack. // Function on stack has to be optimized, since we want to trigger debuggers @@ -5657,7 +5658,9 @@ static Dart_NativeFunction SetBreakpointResolver(Dart_Handle name, } TEST_CASE(DeoptimizeFramesWhenSettingBreakpoint) { - const char* kOriginalScript = "test() {}"; + const char* kOriginalScript = + "@pragma('vm:entry-point', 'call')\n" + "test() {}"; Dart_Handle lib = TestCase::LoadTestScript(kOriginalScript, nullptr); EXPECT_VALID(lib); @@ -5686,6 +5689,7 @@ TEST_CASE(DeoptimizeFramesWhenSettingBreakpoint) { @pragma("vm:external-name", "setBreakpoint") external setBreakpoint(); baz() {} + @pragma('vm:entry-point', 'call') test() { if (true) { setBreakpoint(); @@ -5770,6 +5774,7 @@ TEST_CASE(DartAPI_BreakpointLockRace) { " a();\n" // This is line 5. " }\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "test() {\n" " new A().b();\n" "}"; @@ -6438,6 +6443,7 @@ TEST_CASE(InstanceEquality) { TEST_CASE(HashCode) { // Ensure C++ overrides of Instance::HashCode match the Dart implementations. const char* kScript = + "@pragma('vm:entry-point', 'call')\n" "foo() {\n" " return \"foo\".hashCode;\n" "}"; @@ -6470,18 +6476,21 @@ static bool HashCodeEqualsCanonicalizeHash( uint32_t hashcode_canonicalize_vm = kCalculateCanonicalizeHash, bool check_identity = true, bool check_hashcode = true) { - CStringUniquePtr kScriptChars( - OS::SCreate(nullptr, - "%s" - "\n" - "valueHashCode() {\n" - " return value().hashCode;\n" - "}\n" - "\n" - "valueIdentityHashCode() {\n" - " return identityHashCode(value());\n" - "}\n", - value_script)); + CStringUniquePtr kScriptChars(OS::SCreate(nullptr, + R"( + %s + + @pragma('vm:entry-point', 'call') + valueHashCode() { + return value().hashCode; + } + + @pragma('vm:entry-point', 'call') + valueIdentityHashCode() { + return identityHashCode(value()); + } + )", + value_script)); Dart_Handle lib = TestCase::LoadTestScript(kScriptChars.get(), nullptr); EXPECT_VALID(lib); @@ -6541,9 +6550,12 @@ static bool HashCodeEqualsCanonicalizeHash( TEST_CASE(HashCode_Double) { const char* kScript = - "value() {\n" - " return 1.0;\n" - "}\n"; + R"( + @pragma('vm:entry-point', 'call') + value() { + return 1.0; + } + )"; // Double VM CanonicalizeHash is not equal to hashCode, because doubles // cannot be used as keys in constant sets and maps. However, doubles // _can_ be used for lookups in which case they are equal to their integer @@ -6558,83 +6570,110 @@ TEST_CASE(HashCode_Double) { TEST_CASE(HashCode_Mint) { const char* kScript = - "value() {\n" - " return 0x8000000;\n" - "}\n"; + R"( + @pragma('vm:entry-point', 'call') + value() { + return 0x8000000; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript)); } TEST_CASE(HashCode_Null) { const char* kScript = - "value() {\n" - " return null;\n" - "}\n"; + R"( + @pragma('vm:entry-point', 'call') + value() { + return null; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript)); } TEST_CASE(HashCode_Smi) { const char* kScript = - "value() {\n" - " return 123;\n" - "}\n"; + R"( + @pragma('vm:entry-point', 'call') + value() { + return 123; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript)); } TEST_CASE(HashCode_String) { - const char* kScript = - "value() {\n" - " return 'asdf';\n" - "}\n"; + const char* kScript = R"( + @pragma('vm:entry-point', 'call') + value() { + return 'asdf'; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript)); } TEST_CASE(HashCode_Symbol) { const char* kScript = - "value() {\n" - " return #A;\n" - "}\n"; + R"( + @pragma('vm:entry-point', 'call') + value() { + return #A; + } + )"; + EXPECT(HashCodeEqualsCanonicalizeHash(kScript, kCalculateCanonicalizeHash, /*check_identity=*/false)); } TEST_CASE(HashCode_True) { const char* kScript = - "value() {\n" - " return true;\n" - "}\n"; + R"( + @pragma('vm:entry-point', 'call') + value() { + return true; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript)); } TEST_CASE(HashCode_Type_Dynamic) { const char* kScript = - "const type = dynamic;\n" - "\n" - "value() {\n" - " return type;\n" - "}\n"; + R"( + const type = dynamic; + + @pragma('vm:entry-point', 'call') + value() { + return type; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript, kCalculateCanonicalizeHash, /*check_identity=*/false)); } TEST_CASE(HashCode_Type_Int) { const char* kScript = - "const type = int;\n" - "\n" - "value() {\n" - " return type;\n" - "}\n"; + R"( + const type = int; + + @pragma('vm:entry-point', 'call') + value() { + return type; + } + )"; EXPECT(HashCodeEqualsCanonicalizeHash(kScript, kCalculateCanonicalizeHash, /*check_identity=*/false)); } TEST_CASE(Map_iteration) { const char* kScript = - "makeMap() {\n" - " var map = {'x': 3, 'y': 4, 'z': 5, 'w': 6};\n" - " map.remove('y');\n" - " map.remove('w');\n" - " return map;\n" - "}"; + R"( + @pragma('vm:entry-point', 'call') + makeMap() { + var map = {'x': 3, 'y': 4, 'z': 5, 'w': 6}; + map.remove('y'); + map.remove('w'); + return map; + } + )"; Dart_Handle h_lib = TestCase::LoadTestScript(kScript, nullptr); EXPECT_VALID(h_lib); Dart_Handle h_result = Dart_Invoke(h_lib, NewString("makeMap"), 0, nullptr); @@ -6820,25 +6859,31 @@ final Map expiredExperimentalFlagsNonConst = { ExperimentalFlag.variance: false, }; +@pragma('vm:entry-point', 'call') makeNonConstMap() { return expiredExperimentalFlagsNonConst; } +@pragma('vm:entry-point', 'call') firstKey() { return ExperimentalFlag.alternativeInvalidationStrategy; } +@pragma('vm:entry-point', 'call') firstKeyHashCode() { return firstKey().hashCode; } +@pragma('vm:entry-point', 'call') firstKeyIdentityHashCode() { return identityHashCode(firstKey()); } +@pragma('vm:entry-point', 'call') bool lookupSpreadCollections(Map map) => map[ExperimentalFlag.spreadCollections]; +@pragma('vm:entry-point', 'call') bool? lookupNull(Map map) => map[null]; )"; Dart_Handle lib = TestCase::LoadTestScript(kScript, nullptr); @@ -6916,15 +6961,17 @@ static void HashBaseNonConstEqualsConst(const char* script, bool check_data = true) { Dart_Handle lib = TestCase::LoadTestScript(script, nullptr); EXPECT_VALID(lib); - Dart_Handle init_result = Dart_Invoke(lib, NewString("init"), 0, nullptr); - EXPECT_VALID(init_result); - Dart_Handle non_const_result = - Dart_Invoke(lib, NewString("nonConstValue"), 0, nullptr); - EXPECT_VALID(non_const_result); - Dart_Handle const_result = - Dart_Invoke(lib, NewString("constValue"), 0, nullptr); - EXPECT_VALID(const_result); - + Dart_Handle non_const_result; + Dart_Handle const_result; + { + SetFlagScope sfs(&FLAG_verify_entry_points, false); + Dart_Handle init_result = Dart_Invoke(lib, NewString("init"), 0, nullptr); + EXPECT_VALID(init_result); + non_const_result = Dart_Invoke(lib, NewString("nonConstValue"), 0, nullptr); + EXPECT_VALID(non_const_result); + const_result = Dart_Invoke(lib, NewString("constValue"), 0, nullptr); + EXPECT_VALID(const_result); + } TransitionNativeToVM transition(Thread::Current()); const auto& non_const_object = Object::Handle(Api::UnwrapHandle(non_const_result)); @@ -7114,6 +7161,7 @@ void init() { TEST_CASE(Set_iteration) { const char* kScript = R"( +@pragma('vm:entry-point', 'call') makeSet() { var set = {'x', 'y', 'z', 'w'}; set.remove('y'); @@ -7170,10 +7218,12 @@ static SetPtr ConstructImmutableSet(const Array& input_data, TEST_CASE(ConstSet_vm) { const char* kScript = R"( +@pragma('vm:entry-point', 'call') makeNonConstSet() { return {1, 2, 3, 5, 8, 13}; } +@pragma('vm:entry-point', 'call') bool containsFive(Set set) => set.contains(5); )"; Dart_Handle lib = TestCase::LoadTestScript(kScript, nullptr); diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc index 7a8f9f3edf3..20db17db5a6 100644 --- a/runtime/vm/parser.cc +++ b/runtime/vm/parser.cc @@ -334,7 +334,7 @@ bool ParsedFunction::IsGenericCovariantImplParameter(intptr_t i) const { ParsedFunction::DynamicClosureCallVars* ParsedFunction::EnsureDynamicClosureCallVars() { - ASSERT(function().IsDynamicClosureCallDispatcher(thread())); + ASSERT(function().IsDynamicClosureCallDispatcher()); if (dynamic_closure_call_vars_ != nullptr) return dynamic_closure_call_vars_; const auto& saved_args_desc = Array::Handle(zone(), function().saved_args_desc()); diff --git a/runtime/vm/profiler_test.cc b/runtime/vm/profiler_test.cc index 74fbfee00f0..6106ff3e87e 100644 --- a/runtime/vm/profiler_test.cc +++ b/runtime/vm/profiler_test.cc @@ -6,6 +6,7 @@ #include "vm/dart_api_impl.h" #include "vm/dart_api_state.h" +#include "vm/flags.h" #include "vm/globals.h" #include "vm/profiler.h" #include "vm/profiler_service.h" @@ -242,8 +243,11 @@ static void Invoke(const Library& lib, Thread* thread = Thread::Current(); Dart_Handle api_lib = Api::NewHandle(thread, lib.ptr()); TransitionVMToNative transition(thread); - Dart_Handle result = Dart_Invoke(api_lib, NewString(name), argc, argv); - EXPECT_VALID(result); + { + SetFlagScope sfs(&FLAG_verify_entry_points, false); + Dart_Handle result = Dart_Invoke(api_lib, NewString(name), argc, argv); + EXPECT_VALID(result); + } } class AllocationFilter : public SampleFilter { diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc index 39f52edc49d..cea121d3090 100644 --- a/runtime/vm/service.cc +++ b/runtime/vm/service.cc @@ -2815,18 +2815,20 @@ static void Invoke(Thread* thread, JSONStream* js) { const Array& args = Array::Handle(zone, Array::MakeFixedLength(growable_args)); const Array& arg_names = Object::empty_array(); + // For debugging calls via vm-service, don't require entry point annotations. + const bool check_is_entrypoint = false; if (receiver.IsLibrary()) { const Library& lib = Library::Cast(receiver); - const Object& result = - Object::Handle(zone, lib.Invoke(selector, args, arg_names)); + const Object& result = Object::Handle( + zone, lib.Invoke(selector, args, arg_names, check_is_entrypoint)); result.PrintJSON(js, true); return; } if (receiver.IsClass()) { const Class& cls = Class::Cast(receiver); - const Object& result = - Object::Handle(zone, cls.Invoke(selector, args, arg_names)); + const Object& result = Object::Handle( + zone, cls.Invoke(selector, args, arg_names, check_is_entrypoint)); result.PrintJSON(js, true); return; } @@ -2834,8 +2836,8 @@ static void Invoke(Thread* thread, JSONStream* js) { // We don't use Instance::Cast here because it doesn't allow null. Instance& instance = Instance::Handle(zone); instance ^= receiver.ptr(); - const Object& result = - Object::Handle(zone, instance.Invoke(selector, args, arg_names)); + const Object& result = Object::Handle( + zone, instance.Invoke(selector, args, arg_names, check_is_entrypoint)); result.PrintJSON(js, true); return; } diff --git a/runtime/vm/service_isolate.cc b/runtime/vm/service_isolate.cc index acabf22706c..c484409143c 100644 --- a/runtime/vm/service_isolate.cc +++ b/runtime/vm/service_isolate.cc @@ -459,7 +459,7 @@ class RunServiceTask : public ThreadPool::Task { return Utils::StrDup("Service isolate is not supported by embedder."); } ASSERT(!root_library.IsNull()); - const String& entry_name = String::Handle(Z, String::New("main")); + const String& entry_name = Symbols::main(); ASSERT(!entry_name.IsNull()); const Function& entry = Function::Handle( Z, root_library.LookupFunctionAllowPrivate(entry_name)); diff --git a/runtime/vm/service_test.cc b/runtime/vm/service_test.cc index 7e254d6a932..c80b2c66f81 100644 --- a/runtime/vm/service_test.cc +++ b/runtime/vm/service_test.cc @@ -237,6 +237,7 @@ ISOLATE_UNIT_TEST_CASE(Service_Code) { " x();\n" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Isolate* isolate = thread->isolate(); isolate->set_is_runnable(true); Dart_Handle lib; @@ -362,6 +363,7 @@ ISOLATE_UNIT_TEST_CASE(Service_PcDescriptors) { " x();\n" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Isolate* isolate = thread->isolate(); isolate->set_is_runnable(true); Dart_Handle lib; @@ -433,6 +435,7 @@ ISOLATE_UNIT_TEST_CASE(Service_LocalVarDescriptors) { " x();\n" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Isolate* isolate = thread->isolate(); isolate->set_is_runnable(true); Dart_Handle lib; @@ -500,6 +503,7 @@ ISOLATE_UNIT_TEST_CASE(Service_PersistentHandles) { " return global;\n" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Isolate* isolate = thread->isolate(); isolate->set_is_runnable(true); @@ -591,6 +595,7 @@ ISOLATE_UNIT_TEST_CASE(Service_EmbedderRootHandler) { " x = (x / 13).floor();\n" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib; { TransitionVMToNative transition(thread); @@ -636,6 +641,7 @@ ISOLATE_UNIT_TEST_CASE(Service_EmbedderIsolateHandler) { " x = (x / 13).floor();\n" "}"; + SetFlagScope sfs(&FLAG_verify_entry_points, false); Dart_Handle lib; { TransitionVMToNative transition(thread); @@ -687,6 +693,7 @@ static void EnableProfiler() { ISOLATE_UNIT_TEST_CASE(Service_Profile) { EnableProfiler(); const char* kScript = + "@pragma('vm:entry-point', 'set')\n" "var port;\n" // Set to our mock port by C++. "\n" "var x = 7;\n" diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc index f1139ac5b8b..50c4d673b29 100644 --- a/runtime/vm/snapshot_test.cc +++ b/runtime/vm/snapshot_test.cc @@ -723,7 +723,9 @@ VM_UNIT_TEST_CASE(FullSnapshot) { " if (x != y) throw new ArgumentError('not equal');\n" " }\n" "}\n" + "@pragma('vm:entry-point')\n" "class FieldsTest {\n" + " @pragma('vm:entry-point', 'call')\n" " static Fields testMain() {\n" " Expect.equals(true, Fields.bigint_sfld == 0xfffffffffff);\n" " Fields obj = new Fields(10, 20);\n" @@ -795,8 +797,11 @@ static std::unique_ptr GetSerialized(Dart_Handle lib, Dart_Handle result; { TransitionVMToNative transition(Thread::Current()); - result = Dart_Invoke(lib, NewString(dart_function), 0, nullptr); - EXPECT_VALID(result); + { + SetFlagScope sfs(&FLAG_verify_entry_points, false); + result = Dart_Invoke(lib, NewString(dart_function), 0, nullptr); + EXPECT_VALID(result); + } } Object& obj = Object::Handle(Api::UnwrapHandle(result)); @@ -838,30 +843,39 @@ static void CheckStringInvalid(Dart_Handle dart_string) { VM_UNIT_TEST_CASE(DartGeneratedMessages) { static const char* kCustomIsolateScriptChars = "final int kArrayLength = 10;\n" + "@pragma('vm:entry-point', 'call')\n" "getSmi() {\n" " return 42;\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getAsciiString() {\n" " return \"Hello, world!\";\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getNonAsciiString() {\n" " return \"Blåbærgrød\";\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getNonBMPString() {\n" " return \"\\u{10000}\\u{1F601}\\u{1F637}\\u{20000}\";\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getLeadSurrogateString() {\n" " return String.fromCharCodes([0xd800]);\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getTrailSurrogateString() {\n" " return \"\\u{10000}\".substring(1);\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getSurrogatesString() {\n" " return String.fromCharCodes([0xdc00, 0xdc00, 0xd800, 0xd800]);\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getCrappyString() {\n" " return String.fromCharCodes([0xd800, 32, 0xdc00, 32]);\n" "}\n" + "@pragma('vm:entry-point', 'call')\n" "getList() {\n" " return List.filled(kArrayLength, null);\n" "}\n"; diff --git a/runtime/vm/stack_frame_test.cc b/runtime/vm/stack_frame_test.cc index 2f8cb5bed35..b2f8724cecc 100644 --- a/runtime/vm/stack_frame_test.cc +++ b/runtime/vm/stack_frame_test.cc @@ -234,7 +234,9 @@ TEST_CASE(ValidateStackFrameIteration) { " StackFrame.validateFrame(5, \"StackFrameTest_testMain\");" " }" "}" + "@pragma('vm:entry-point')\n" "class StackFrameTest {" + " @pragma('vm:entry-point', 'call')\n" " static testMain() {" " Second obj = new Second();" " obj.method1(1);" @@ -261,6 +263,7 @@ TEST_CASE(ValidateNoSuchMethodStackFrameIteration) { " @pragma('vm:external-name', 'StackFrame_validateFrame')\n" " external static validateFrame(int index, String name);" "} " + "@pragma('vm:entry-point')\n" "class StackFrame2Test {" " StackFrame2Test() {}" " noSuchMethod(Invocation im) {" @@ -281,6 +284,7 @@ TEST_CASE(ValidateNoSuchMethodStackFrameIteration) { " StackFrame.validateFrame(3, \"StackFrame2Test_testMain\");" " return 5;" " }" + " @pragma('vm:entry-point', 'call')\n" " static testMain() {" " /* Declare |obj| dynamic so that noSuchMethod can be" " * called in strong mode. */" diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h index 3902536a47d..8a26f385fad 100644 --- a/runtime/vm/symbols.h +++ b/runtime/vm/symbols.h @@ -529,6 +529,7 @@ class ObjectPointerVisitor; V(index_temp, ":index_temp") \ V(isLeaf, "isLeaf") \ V(isPaused, "isPaused") \ + V(main, "main") \ V(match_end_index, ":match_end_index") \ V(match_start_index, ":match_start_index") \ V(name, "name") \ diff --git a/sdk/lib/_internal/vm/bin/builtin.dart b/sdk/lib/_internal/vm/bin/builtin.dart index 82bce74adc6..b84147a1fb0 100644 --- a/sdk/lib/_internal/vm/bin/builtin.dart +++ b/sdk/lib/_internal/vm/bin/builtin.dart @@ -14,6 +14,7 @@ import 'dart:typed_data'; // Embedder sets this to true if the --trace-loading flag was passed on the // command line. +@pragma("vm:entry-point", "set") bool _traceLoading = false; // Before handling an embedder entrypoint we finalize the setup of the @@ -30,7 +31,7 @@ void _print(arg) { _printString(arg.toString()); } -@pragma("vm:entry-point") +@pragma("vm:entry-point", "call") _getPrintClosure() => _print; // The current working directory when the embedder was launched. @@ -60,7 +61,7 @@ Map? _packageMap = null; // Special handling for Windows paths so that they are compatible with URI // handling. // Embedder sets this to true if we are running on Windows. -@pragma("vm:entry-point") +@pragma("vm:entry-point", "set") bool _isWindows = false; // Logging from builtin.dart is prefixed with a '*'. diff --git a/sdk/lib/_internal/vm/bin/secure_socket_patch.dart b/sdk/lib/_internal/vm/bin/secure_socket_patch.dart index f6c2bdfafd4..9c5d2130b92 100644 --- a/sdk/lib/_internal/vm/bin/secure_socket_patch.dart +++ b/sdk/lib/_internal/vm/bin/secure_socket_patch.dart @@ -189,7 +189,7 @@ base class _SecureFilterImpl extends NativeFieldWrapperClass1 @pragma("vm:external-name", "SecureSocket_FilterPointer") external int _pointer(); - @pragma("vm:entry-point", "get") + @pragma("vm:entry-point") List<_ExternalBuffer>? buffers; } diff --git a/sdk/lib/_internal/vm/lib/errors_patch.dart b/sdk/lib/_internal/vm/lib/errors_patch.dart index 9f73a27ebd9..583992e0a67 100644 --- a/sdk/lib/_internal/vm/lib/errors_patch.dart +++ b/sdk/lib/_internal/vm/lib/errors_patch.dart @@ -108,6 +108,7 @@ class _TypeError extends Error implements TypeError { final String _message; } +@pragma("vm:entry-point") class _InternalError { @pragma("vm:entry-point") const _InternalError(this._msg); diff --git a/sdk/lib/_internal/vm/lib/print_patch.dart b/sdk/lib/_internal/vm/lib/print_patch.dart index bd6ab4546ef..972283970ea 100644 --- a/sdk/lib/_internal/vm/lib/print_patch.dart +++ b/sdk/lib/_internal/vm/lib/print_patch.dart @@ -19,5 +19,5 @@ void _unsupportedPrint(String line) { // _printClosure can be overwritten by the embedder to supply a different // print implementation. -@pragma("vm:entry-point") +@pragma("vm:entry-point", "set") _PrintClosure _printClosure = _unsupportedPrint; diff --git a/sdk/lib/core/errors.dart b/sdk/lib/core/errors.dart index 35e90539b6e..eae4fce4f38 100644 --- a/sdk/lib/core/errors.dart +++ b/sdk/lib/core/errors.dart @@ -159,6 +159,7 @@ class TypeError extends Error {} /// so the [ArgumentError.value] constructor is the preferred constructor. /// Use [ArgumentError.new] only when the value cannot be provided for some /// reason. +@pragma("vm:entry-point") class ArgumentError extends Error { /// Whether value was provided. final bool _hasValue; diff --git a/sdk/lib/core/exceptions.dart b/sdk/lib/core/exceptions.dart index c7a064d8ba1..c8f92cf331b 100644 --- a/sdk/lib/core/exceptions.dart +++ b/sdk/lib/core/exceptions.dart @@ -39,6 +39,7 @@ class _Exception implements Exception { /// Exception thrown when a string or some other data does not have an expected /// format and cannot be parsed or processed. +@pragma("vm:entry-point") class FormatException implements Exception { /// A message describing the format error. final String message; diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart index df37688c791..6252b27c103 100644 --- a/sdk/lib/io/secure_socket.dart +++ b/sdk/lib/io/secure_socket.dart @@ -1218,7 +1218,7 @@ class _RawSecureSocket extends Stream /// and one writing. All updates to start and end are done by Dart code. class _ExternalBuffer { // This will be an ExternalByteArray, backed by C allocated data. - @pragma("vm:entry-point", "set") + @pragma("vm:entry-point") List? data; @pragma("vm:entry-point") @@ -1367,6 +1367,7 @@ abstract class _SecureFilter { /// A secure networking exception caused by a failure in the /// TLS/SSL protocol. +@pragma("vm:entry-point") class TlsException implements IOException { final String type; final String message; diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart index eebfe3ee363..e20cf666e2b 100644 --- a/sdk/lib/isolate/isolate.dart +++ b/sdk/lib/isolate/isolate.dart @@ -73,6 +73,7 @@ class IsolateSpawnException implements Exception { /// An `Isolate` object cannot be sent over a `SendPort`, but the control port /// and capabilities can be sent, and can be used to create a new functioning /// `Isolate` object in the receiving port's isolate. +@pragma('vm:entry-point') final class Isolate { /// Argument to `ping` and `kill`: Ask for immediate action. static const int immediate = 0; diff --git a/tests/lib/lib_kernel.status b/tests/lib/lib_kernel.status index 2faf2b7a760..5b4993c4be3 100644 --- a/tests/lib/lib_kernel.status +++ b/tests/lib/lib_kernel.status @@ -42,7 +42,7 @@ isolate/deferred_in_isolate2_test: Skip # Times out. Deferred loading kernel iss isolate/deferred_in_isolate_test: Skip # Times out. Deferred loading kernel issue 28335. isolate/issue_21398_parent_isolate2_test/01: Skip # Times out. Deferred loading kernel issue 28335. isolate/static_function_test: Skip # Times out. Issue 31855. CompileTimeError. Issue 31402 -mirrors/invocation_fuzz_test: Crash +mirrors/invocation_fuzz_test/smi: Crash mirrors/metadata_allowed_values_test/16: Skip # Flaky, crashes. [ $compiler == dartk && $hot_reload_rollback ]