From cb9ecbc3636aaa4e8c8301caa4bab2d903825bf3 Mon Sep 17 00:00:00 2001 From: Tess Strickland Date: Tue, 22 Oct 2024 09:34:22 +0000 Subject: [PATCH] [vm] Enforce that entry points must be annotated by default. Changes the default value of the --verify-entry-points flag to true. Changes the default value for the check_is_entrypoint argument to to the Invoke/InvokeGetter/InvokeSetter flags to true. The mirrors library implementation and calls via vm-service explicitly pass false for this argument now. Add annotations as needed, such as annotating classes with annotated generative constructors. In some cases, the annotations were more general than needed (e.g., annotating with a no-argument entry point annotation when only the setter is needed), so make those annotations more specific. As this pattern is already common in downstream code, allow Dart_Invoke on fields as long as the field is annotated for getter access. (That is, calling Dart_Invoke for a field is equivalent to retrieving the closure value via Dart_GetField and then calling Dart_InvokeClosure.) TEST=vm/cc/DartAPI_MissingEntryPoints vm/dart/entrypoints_verification_test Issue: https://github.com/dart-lang/sdk/issues/50649 Issue: https://github.com/flutter/flutter/issues/118608 Change-Id: Ibb3bf15632ab2958d8791b449af8651d47f871a5 Cq-Include-Trybots: luci.dart.try:vm-aot-linux-product-x64-try,vm-aot-linux-debug-x64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-product-arm64-try,vm-aot-dwarf-linux-product-x64-try CoreLibraryReviewExempt: adding/editing vm-only pragma annotations Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/363566 Reviewed-by: Martin Kustermann Commit-Queue: Tess Strickland --- runtime/bin/entrypoints_verification_test.cc | 285 ++++--- .../docs/compiler/aot/entry_point_pragma.md | 17 +- runtime/lib/isolate.cc | 2 +- runtime/lib/mirrors.cc | 29 +- .../dart/entrypoints_verification_test.dart | 32 +- runtime/vm/benchmark_test.cc | 5 + runtime/vm/compiler/aot/precompiler.cc | 12 +- runtime/vm/compiler/backend/il_test_helper.cc | 5 +- runtime/vm/compiler/backend/il_test_helper.h | 4 +- runtime/vm/compiler/backend/inliner_test.cc | 2 + .../backend/redundancy_elimination_test.cc | 1 + runtime/vm/compiler_test.cc | 17 +- runtime/vm/custom_isolate_test.cc | 2 + runtime/vm/dart_api_impl.cc | 68 +- runtime/vm/dart_api_impl_test.cc | 302 +++++++- runtime/vm/debugger.cc | 3 +- runtime/vm/exceptions_test.cc | 2 + runtime/vm/flag_list.h | 2 +- runtime/vm/isolate_reload_test.cc | 9 + runtime/vm/isolate_test.cc | 3 + runtime/vm/json_test.cc | 1 + runtime/vm/kernel_isolate.cc | 3 +- runtime/vm/object.cc | 710 +++++++++--------- runtime/vm/object.h | 82 +- runtime/vm/object_test.cc | 172 +++-- runtime/vm/parser.cc | 2 +- runtime/vm/profiler_test.cc | 8 +- runtime/vm/service.cc | 14 +- runtime/vm/service_isolate.cc | 2 +- runtime/vm/service_test.cc | 7 + runtime/vm/snapshot_test.cc | 18 +- runtime/vm/stack_frame_test.cc | 4 + runtime/vm/symbols.h | 1 + sdk/lib/_internal/vm/bin/builtin.dart | 5 +- .../_internal/vm/bin/secure_socket_patch.dart | 2 +- sdk/lib/_internal/vm/lib/errors_patch.dart | 1 + sdk/lib/_internal/vm/lib/print_patch.dart | 2 +- sdk/lib/core/errors.dart | 1 + sdk/lib/core/exceptions.dart | 1 + sdk/lib/io/secure_socket.dart | 3 +- sdk/lib/isolate/isolate.dart | 1 + tests/lib/lib_kernel.status | 2 +- 42 files changed, 1194 insertions(+), 650 deletions(-) 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 ]