diff --git a/BUILD.gn b/BUILD.gn index 27a1cf8657d..ae4c230871b 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -46,6 +46,7 @@ group("runtime") { "runtime/bin:run_vm_tests", "runtime/bin:sample_extension", "runtime/bin:test_extension", + "runtime/bin:entrypoints_verification_test_extension", "runtime/vm:kernel_platform_files($host_toolchain)", "utils/kernel-service:kernel-service", ] diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index f220caa7f60..ec64042508f 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -4,15 +4,15 @@ import("../../build/dart/dart_action.gni") import("../runtime_args.gni") -import("../vm/heap/heap_sources.gni") import("../vm/compiler/compiler_sources.gni") +import("../vm/heap/heap_sources.gni") import("../vm/vm_sources.gni") import("builtin_impl_sources.gni") import("builtin_sources.gni") -import("io_impl_sources.gni") -import("io_sources.gni") import("cli_impl_sources.gni") import("cli_sources.gni") +import("io_impl_sources.gni") +import("io_sources.gni") config("libdart_builtin_config") { if (!is_win) { @@ -981,6 +981,29 @@ shared_library("test_extension") { } } +shared_library("entrypoints_verification_test_extension") { + deps = [ + ":dart", + ] + sources = [ + "entrypoints_verification_test_extension.cc", + "entrypoints_verification_test_extension_dllmain_win.cc", + ] + include_dirs = [ ".." ] + defines = [ + # The only effect of DART_SHARED_LIB is to export the Dart API. + "DART_SHARED_LIB", + ] + if (is_linux || is_android) { + cflags = [ "-fPIC" ] + } + if (is_win) { + libs = [ "dart.lib" ] + abs_root_out_dir = rebase_path(root_out_dir) + ldflags = [ "/LIBPATH:$abs_root_out_dir" ] + } +} + shared_library("sample_extension") { deps = [ ":dart", diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart index 2d663e06f7b..363469b333f 100644 --- a/runtime/bin/builtin.dart +++ b/runtime/bin/builtin.dart @@ -81,6 +81,7 @@ Uri _packageRoot; // 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") bool _isWindows = false; // Logging from builtin.dart is prefixed with a '*'. diff --git a/runtime/bin/entrypoints_verification_test_extension.cc b/runtime/bin/entrypoints_verification_test_extension.cc new file mode 100644 index 00000000000..e070996c3fb --- /dev/null +++ b/runtime/bin/entrypoints_verification_test_extension.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// 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/dart_api.h" +#include "./include/dart_native_api.h" + +#define CHECK(H) DART_CHECK_VALID(H) + +#define ASSERT(E) \ + if (!(E)) { \ + fprintf(stderr, "Assertion \"" #E "\" failed!"); \ + abort(); \ + } + +Dart_Handle GetCurrentLibrary() { + Dart_Handle libraries = Dart_GetLoadedLibraries(); + CHECK(libraries); + intptr_t length = 0; + CHECK(Dart_ListLength(libraries, &length)); + for (intptr_t i = 0; i < length; ++i) { + Dart_Handle library = Dart_ListGetAt(libraries, i); + CHECK(library); + Dart_Handle url = Dart_LibraryUrl(library); + CHECK(url); + const char* url_str; + CHECK(Dart_StringToCString(url, &url_str)); + if (strstr(url_str, "entrypoints_verification_test")) { + return library; + } + } + fprintf(stderr, "Could not find current library!"); + abort(); +} + +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")); +} + +void FailClosurize(const char* name, Dart_Handle result) { + ASSERT(Dart_IsApiError(result)); + const char* error = Dart_GetError(result); + ASSERT(strstr(error, name)); + ASSERT(strstr(error, "Entry-points do not allow closurizing methods")); +} + +void TestFields(Dart_Handle target) { + Fail("fld0", Dart_GetField(target, Dart_NewStringFromCString("fld0"))); + Fail("fld0", + Dart_SetField(target, Dart_NewStringFromCString("fld0"), Dart_Null())); + + Dart_Handle result = + Dart_Invoke(target, Dart_NewStringFromCString("fld0"), 0, nullptr); + FailClosurize("fld0", result); + + CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld1"))); + CHECK(Dart_SetField(target, Dart_NewStringFromCString("fld1"), Dart_Null())); + FailClosurize("fld1", Dart_Invoke(target, Dart_NewStringFromCString("fld1"), + 0, nullptr)); + + CHECK(Dart_GetField(target, Dart_NewStringFromCString("fld2"))); + Fail("fld2", + Dart_SetField(target, Dart_NewStringFromCString("fld2"), Dart_Null())); + FailClosurize("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())); + FailClosurize("fld3", Dart_Invoke(target, Dart_NewStringFromCString("fld3"), + 0, nullptr)); +} + +void RunTests(Dart_NativeArguments arguments) { + Dart_Handle lib = GetCurrentLibrary(); + + //////// Test allocation and constructor invocation. + + Fail("C", Dart_GetClass(lib, Dart_NewStringFromCString("C"))); + + Dart_Handle D_class = Dart_GetClass(lib, Dart_NewStringFromCString("D")); + CHECK(D_class); + + CHECK(Dart_Allocate(D_class)); + + Fail("D.", Dart_New(D_class, Dart_Null(), 0, nullptr)); + + CHECK(Dart_New(D_class, Dart_NewStringFromCString("defined"), 0, nullptr)); + Dart_Handle D = + Dart_New(D_class, Dart_NewStringFromCString("fact"), 0, nullptr); + CHECK(D); + + //////// Test actions against methods + + Fail("fn0", Dart_Invoke(D, Dart_NewStringFromCString("fn0"), 0, nullptr)); + + CHECK(Dart_Invoke(D, Dart_NewStringFromCString("fn1"), 0, nullptr)); + + Fail("get_fn0", Dart_GetField(D, Dart_NewStringFromCString("fn0"))); + + Fail("get_fn1", Dart_GetField(D, Dart_NewStringFromCString("fn1"))); + + Fail("fn2", + Dart_Invoke(D_class, Dart_NewStringFromCString("fn2"), 0, nullptr)); + + CHECK(Dart_Invoke(D_class, Dart_NewStringFromCString("fn3"), 0, nullptr)); + + FailClosurize("fn2", + Dart_GetField(D_class, Dart_NewStringFromCString("fn2"))); + + FailClosurize("fn3", + Dart_GetField(D_class, Dart_NewStringFromCString("fn3"))); + + Fail("fn0", Dart_Invoke(lib, Dart_NewStringFromCString("fn0"), 0, nullptr)); + + CHECK(Dart_Invoke(lib, Dart_NewStringFromCString("fn1"), 0, nullptr)); + + FailClosurize("fn0", Dart_GetField(lib, Dart_NewStringFromCString("fn0"))); + + FailClosurize("fn1", Dart_GetField(lib, Dart_NewStringFromCString("fn1"))); + + //////// Test actions against fields + + TestFields(D); + + Dart_Handle F_class = Dart_GetClass(lib, Dart_NewStringFromCString("F")); + TestFields(F_class); + + TestFields(lib); +} + +Dart_NativeFunction ResolveName(Dart_Handle name, + int argc, + bool* auto_setup_scope) { + if (auto_setup_scope == NULL) { + return NULL; + } + *auto_setup_scope = true; + return RunTests; +} + +DART_EXPORT Dart_Handle +entrypoints_verification_test_extension_Init(Dart_Handle parent_library) { + if (Dart_IsError(parent_library)) { + return parent_library; + } + + Dart_Handle result_code = + Dart_SetNativeResolver(parent_library, ResolveName, NULL); + if (Dart_IsError(result_code)) { + return result_code; + } + + return Dart_Null(); +} diff --git a/runtime/bin/entrypoints_verification_test_extension_dllmain_win.cc b/runtime/bin/entrypoints_verification_test_extension_dllmain_win.cc new file mode 100644 index 00000000000..cbf42d53bee --- /dev/null +++ b/runtime/bin/entrypoints_verification_test_extension_dllmain_win.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// 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 "platform/globals.h" +#if defined(HOST_OS_WINDOWS) + +#define WIN32_LEAN_AND_MEAN +#include // NOLINT + +BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) { + return true; +} + +#endif // defined(HOST_OS_WINDOWS) diff --git a/runtime/docs/compiler/aot/entry_point_pragma.md b/runtime/docs/compiler/aot/entry_point_pragma.md index 69cf760898e..c560070ee95 100644 --- a/runtime/docs/compiler/aot/entry_point_pragma.md +++ b/runtime/docs/compiler/aot/entry_point_pragma.md @@ -60,8 +60,11 @@ void foo() { ... } If the second parameter is missing, `null` or `true`, the procedure will available for lookup and invocation directly from native or VM code. If the -procedure is a *generative* constructor, the enclosing class will also be marked -for allocation from native or VM code. +procedure is a *generative* constructor, the enclosing class must also be +annotated for allocation from native or VM code. + +Note that annotating a procedure does not allow closurizing it, e.g. access a +non-getter via `Dart_GetField`. ### Fields diff --git a/runtime/lib/growable_array.dart b/runtime/lib/growable_array.dart index 0dbe0083ad6..e22e8bf9133 100644 --- a/runtime/lib/growable_array.dart +++ b/runtime/lib/growable_array.dart @@ -150,6 +150,7 @@ class _GrowableList extends ListBase { void _setIndexed(int index, T value) native "GrowableList_setIndexed"; + @pragma("vm:entry-point") void add(T value) { var len = length; if (len == _capacity) { diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc index 0f3c66f1e8b..bc61d2a6f2b 100644 --- a/runtime/vm/compiler/aot/precompiler.cc +++ b/runtime/vm/compiler/aot/precompiler.cc @@ -329,8 +329,11 @@ void Precompiler::DoCompileAll() { I->object_store()->set_unique_dynamic_targets(Array::null_array()); Class& null_class = Class::Handle(Z); Function& null_function = Function::Handle(Z); + Field& null_field = Field::Handle(Z); I->object_store()->set_future_class(null_class); I->object_store()->set_pragma_class(null_class); + I->object_store()->set_pragma_name(null_field); + I->object_store()->set_pragma_options(null_field); I->object_store()->set_completer_class(null_class); I->object_store()->set_symbol_class(null_class); I->object_store()->set_compiletime_error_class(null_class); @@ -979,21 +982,16 @@ void Precompiler::AddInstantiatedClass(const Class& cls) { } } -enum class EntryPointPragma { kAlways, kNever, kGetterOnly, kSetterOnly }; - // Adds all values annotated with @pragma('vm:entry-point') as roots. void Precompiler::AddAnnotatedRoots() { auto& lib = Library::Handle(Z); - auto& cls = Class::Handle(isolate()->object_store()->pragma_class()); + auto& cls = Class::Handle(Z); auto& members = Array::Handle(Z); auto& function = Function::Handle(Z); auto& field = Field::Handle(Z); auto& metadata = Array::Handle(Z); - auto& pragma = Object::Handle(Z); - auto& pragma_options = Object::Handle(Z); - auto& pragma_name_field = Field::Handle(Z, cls.LookupField(Symbols::name())); - auto& pragma_options_field = - Field::Handle(Z, cls.LookupField(Symbols::options())); + auto& reusable_object_handle = Object::Handle(Z); + auto& reusable_field_handle = Field::Handle(Z); // Lists of fields which need implicit getter/setter/static final getter // added. @@ -1001,33 +999,6 @@ void Precompiler::AddAnnotatedRoots() { auto& implicit_setters = GrowableObjectArray::Handle(Z); auto& implicit_static_getters = GrowableObjectArray::Handle(Z); - // Local function allows easy reuse of handles above. - auto metadata_defines_entrypoint = [&]() { - for (intptr_t i = 0; i < metadata.Length(); i++) { - pragma = metadata.At(i); - if (pragma.clazz() != isolate()->object_store()->pragma_class()) { - continue; - } - if (Instance::Cast(pragma).GetField(pragma_name_field) != - Symbols::vm_entry_point().raw()) { - continue; - } - pragma_options = Instance::Cast(pragma).GetField(pragma_options_field); - if (pragma_options.raw() == Bool::null() || - pragma_options.raw() == Bool::True().raw()) { - return EntryPointPragma::kAlways; - break; - } - if (pragma_options.raw() == Symbols::Get().raw()) { - return EntryPointPragma::kGetterOnly; - } - if (pragma_options.raw() == Symbols::Set().raw()) { - return EntryPointPragma::kSetterOnly; - } - } - return EntryPointPragma::kNever; - }; - for (intptr_t i = 0; i < libraries_.Length(); i++) { lib ^= libraries_.At(i); ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate); @@ -1037,7 +1008,9 @@ void Precompiler::AddAnnotatedRoots() { // Check for @pragma on the class itself. if (cls.has_pragma()) { metadata ^= lib.GetMetadata(cls); - if (metadata_defines_entrypoint() == EntryPointPragma::kAlways) { + if (FindEntryPointPragma(isolate(), metadata, &reusable_field_handle, + &reusable_object_handle) == + EntryPointPragma::kAlways) { AddInstantiatedClass(cls); } } @@ -1052,7 +1025,9 @@ void Precompiler::AddAnnotatedRoots() { if (field.has_pragma()) { metadata ^= lib.GetMetadata(field); if (metadata.IsNull()) continue; - EntryPointPragma pragma = metadata_defines_entrypoint(); + EntryPointPragma pragma = + FindEntryPointPragma(isolate(), metadata, &reusable_field_handle, + &reusable_object_handle); if (pragma == EntryPointPragma::kNever) continue; AddField(field); @@ -1077,7 +1052,9 @@ void Precompiler::AddAnnotatedRoots() { if (function.has_pragma()) { metadata ^= lib.GetMetadata(function); if (metadata.IsNull()) continue; - if (metadata_defines_entrypoint() != EntryPointPragma::kAlways) { + if (FindEntryPointPragma(isolate(), metadata, &reusable_field_handle, + &reusable_object_handle) != + EntryPointPragma::kAlways) { continue; } diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 72a7990b8ff..520d39ac17a 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -74,6 +74,14 @@ DEFINE_FLAG(bool, false, "Dump common hash tables before snapshotting."); +#define CHECK_ERROR_HANDLE(error) \ + { \ + RawError* err = (error); \ + if (err != Error::null()) { \ + return Api::NewHandle(T, err); \ + } \ + } + ThreadLocalKey Api::api_native_key_ = kUnsetThreadLocalKey; Dart_Handle Api::true_handle_ = NULL; Dart_Handle Api::false_handle_ = NULL; @@ -3741,6 +3749,8 @@ static RawObject* ResolveConstructor(const char* current_func, current_func, constr_name.ToCString(), error_message.ToCString())); return ApiError::New(message); } + RawError* error = constructor.VerifyEntryPoint(); + if (error != Error::null()) return error; return constructor.raw(); } @@ -3822,6 +3832,7 @@ DART_EXPORT Dart_Handle Dart_New(Dart_Handle type, cls = type_obj.type_class(); } if (constructor.IsGenerativeConstructor()) { + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); #if defined(DEBUG) if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) { @@ -3916,16 +3927,13 @@ DART_EXPORT Dart_Handle Dart_Allocate(Dart_Handle type) { RETURN_TYPE_ERROR(Z, type, Type); } const Class& cls = Class::Handle(Z, type_obj.type_class()); + 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()); } #endif - const Error& error = Error::Handle(Z, cls.EnsureIsFinalized(T)); - if (!error.IsNull()) { - // An error occurred, return error object. - return Api::NewHandle(T, error.raw()); - } + CHECK_ERROR_HANDLE(cls.EnsureIsFinalized(T)); return Api::NewHandle(T, AllocateObject(T, cls)); } @@ -3945,16 +3953,13 @@ 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 defined(DEBUG) if (!cls.is_allocated() && (Dart::vm_snapshot_kind() == Snapshot::kFullAOT)) { return Api::NewError("Precompilation dropped '%s'", cls.ToCString()); } #endif - const Error& error = Error::Handle(Z, cls.EnsureIsFinalized(T)); - if (!error.IsNull()) { - // An error occurred, return error object. - return Api::NewHandle(T, error.raw()); - } + CHECK_ERROR_HANDLE(cls.EnsureIsFinalized(T)); if (num_native_fields != cls.num_native_fields()) { return Api::NewError( "%s: invalid number of native fields %" Pd " passed in, expected %d", @@ -4038,6 +4043,7 @@ DART_EXPORT Dart_Handle Dart_InvokeConstructor(Dart_Handle object, if (!constructor.IsNull() && constructor.IsGenerativeConstructor() && constructor.AreValidArgumentCounts( kTypeArgsLen, number_of_arguments + extra_args, 0, NULL)) { + CHECK_ERROR_HANDLE(constructor.VerifyEntryPoint()); // Create the argument list. // Constructors get the uninitialized object. if (!type_arguments.IsNull()) { @@ -4094,6 +4100,7 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, // This API does not provide a way to pass named parameters. const Array& arg_names = Object::empty_array(); const bool respect_reflectable = false; + const bool check_is_entrypoint = FLAG_verify_entry_points; if (obj.IsType()) { if (!Type::Cast(obj).IsFinalized()) { return Api::NewError( @@ -4113,7 +4120,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)); + T, cls.Invoke(function_name, args, arg_names, respect_reflectable, + check_is_entrypoint)); } 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 @@ -4127,8 +4135,9 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, return result; } args.SetAt(0, instance); - return Api::NewHandle(T, instance.Invoke(function_name, args, arg_names, - respect_reflectable)); + return Api::NewHandle( + T, instance.Invoke(function_name, args, arg_names, respect_reflectable, + check_is_entrypoint)); } else if (obj.IsLibrary()) { // Check whether class finalization is needed. const Library& lib = Library::Cast(obj); @@ -4150,7 +4159,8 @@ DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target, } return Api::NewHandle( - T, lib.Invoke(function_name, args, arg_names, respect_reflectable)); + T, lib.Invoke(function_name, args, arg_names, respect_reflectable, + check_is_entrypoint)); } else { return Api::NewError( "%s expects argument 'target' to be an object, type, or library.", @@ -4202,6 +4212,7 @@ DART_EXPORT Dart_Handle Dart_GetField(Dart_Handle container, Dart_Handle name) { 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; if (obj.IsType()) { if (!Type::Cast(obj).IsFinalized()) { @@ -4214,8 +4225,9 @@ 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)); + return Api::NewHandle( + T, cls.InvokeGetter(field_name, throw_nsm_if_absent, + respect_reflectable, check_is_entrypoint)); } else if (obj.IsNull() || obj.IsInstance()) { Instance& instance = Instance::Handle(Z); instance ^= obj.raw(); @@ -4224,8 +4236,9 @@ 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, instance.InvokeGetter(field_name, respect_reflectable)); + return Api::NewHandle(T, + instance.InvokeGetter(field_name, respect_reflectable, + check_is_entrypoint)); } else if (obj.IsLibrary()) { const Library& lib = Library::Cast(obj); // Check that the library is loaded. @@ -4237,8 +4250,9 @@ 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)); + return Api::NewHandle( + T, lib.InvokeGetter(field_name, throw_nsm_if_absent, + respect_reflectable, check_is_entrypoint)); } else if (obj.IsError()) { return container; } else { @@ -4271,6 +4285,7 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container, const Object& obj = Object::Handle(Z, Api::UnwrapHandle(container)); const bool respect_reflectable = false; + const bool check_is_entrypoint = FLAG_verify_entry_points; if (obj.IsType()) { if (!Type::Cast(obj).IsFinalized()) { @@ -4287,7 +4302,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)); + T, cls.InvokeSetter(field_name, value_instance, respect_reflectable, + check_is_entrypoint)); } else if (obj.IsNull() || obj.IsInstance()) { Instance& instance = Instance::Handle(Z); instance ^= obj.raw(); @@ -4296,8 +4312,9 @@ DART_EXPORT Dart_Handle Dart_SetField(Dart_Handle container, const Library& lib = Library::Handle(Z, cls.library()); field_name = lib.PrivateName(field_name); } - return Api::NewHandle(T, instance.InvokeSetter(field_name, value_instance, - respect_reflectable)); + return Api::NewHandle( + T, instance.InvokeSetter(field_name, value_instance, + respect_reflectable, check_is_entrypoint)); } 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 @@ -4314,7 +4331,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)); + T, lib.InvokeSetter(field_name, value_instance, respect_reflectable, + check_is_entrypoint)); } else if (obj.IsError()) { return container; } @@ -4972,22 +4990,6 @@ DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library) { RETURN_TYPE_ERROR(Z, library, Library); } -static void CheckIsEntryPoint(const Class& klass) { -#if defined(DART_PRECOMPILED_RUNTIME) - // This is not a completely thorough check that the class is an entry-point, - // but it catches most missing annotations. - if (!klass.has_pragma()) { - OS::PrintErr( - "ERROR: It is illegal to access class %s through Dart C API " - "because it was not annotated with @pragma('vm:entry-point').\n" - "ERROR: See " - "https://github.com/dart-lang/sdk/blob/master/runtime/docs/compiler/" - "aot/entry_point_pragma.md\n", - String::Handle(klass.UserVisibleName()).ToCString()); - } -#endif -} - DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library, Dart_Handle class_name) { DARTSCOPE(Thread::Current()); @@ -5006,7 +5008,7 @@ DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library, return Api::NewError("Class '%s' not found in library '%s'.", cls_name.ToCString(), lib_name.ToCString()); } - CheckIsEntryPoint(cls); + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); return Api::NewHandle(T, cls.RareType()); } @@ -5035,7 +5037,7 @@ DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library, return Api::NewError("Type '%s' not found in library '%s'.", name_str.ToCString(), lib_name.ToCString()); } - CheckIsEntryPoint(cls); + CHECK_ERROR_HANDLE(cls.VerifyEntryPoint()); if (cls.NumTypeArguments() == 0) { if (number_of_type_arguments != 0) { return Api::NewError( @@ -6028,10 +6030,7 @@ DART_EXPORT Dart_Handle Dart_Precompile() { return result; } CHECK_CALLBACK_STATE(T); - const Error& error = Error::Handle(Precompiler::CompileAll()); - if (!error.IsNull()) { - return Api::NewHandle(T, error.raw()); - } + CHECK_ERROR_HANDLE(Precompiler::CompileAll()); return Api::Success(); #endif } diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h index 36ececabac0..ab60d21f1c8 100644 --- a/runtime/vm/flag_list.h +++ b/runtime/vm/flag_list.h @@ -212,7 +212,10 @@ constexpr bool kDartPrecompiledRuntime = false; R(eliminate_type_checks, true, bool, true, \ "Eliminate type checks when allowed by static type analysis.") \ P(enable_interpreter, bool, false, "Enable interpreting kernel bytecode.") \ - D(support_rr, bool, false, "Support running within RR.") + D(support_rr, bool, false, "Support running within RR.") \ + P(verify_entry_points, bool, false, \ + "Throw API error on invalid member access throuh native API. See " \ + "entry_point_pragma.md") // List of VM-global (i.e. non-isolate specific) flags. // diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc index e0072d7e8f0..b53d63afd01 100644 --- a/runtime/vm/object.cc +++ b/runtime/vm/object.cc @@ -104,6 +104,14 @@ cpp_vtable Smi::handle_vtable_ = 0; #endif #define RAW_NULL kHeapObjectTag +#define CHECK_ERROR(error) \ + { \ + RawError* err = (error); \ + if (err != Error::null()) { \ + return err; \ + } \ + } + #define DEFINE_SHARED_READONLY_HANDLE(Type, name) \ Type* Object::name##_ = nullptr; SHARED_READONLY_HANDLES_LIST(DEFINE_SHARED_READONLY_HANDLE) @@ -464,7 +472,7 @@ void Object::Init(Isolate* isolate) { Heap* heap = isolate->heap(); - // Allocate the read only object handles here. +// Allocate the read only object handles here. #define INITIALIZE_SHARED_READONLY_HANDLE(Type, name) \ name##_ = Type::ReadOnlyHandle(); SHARED_READONLY_HANDLES_LIST(INITIALIZE_SHARED_READONLY_HANDLE) @@ -3199,31 +3207,43 @@ static RawObject* ThrowTypeError(const TokenPosition token_pos, RawObject* Class::InvokeGetter(const String& getter_name, bool throw_nsm_if_absent, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); - const Error& error = Error::Handle(zone, EnsureIsFinalized(thread)); - if (!error.IsNull()) { - return error.raw(); - } + CHECK_ERROR(EnsureIsFinalized(thread)); // 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.VerifyEntryPoint()); + } + if (getter.IsNull() || (respect_reflectable && !getter.is_reflectable())) { if (getter.IsNull()) { getter = LookupStaticFunction(getter_name); if (!getter.IsNull()) { - // Looking for a getter but found a regular method: closurize it. - const Function& closure_function = - Function::Handle(zone, getter.ImplicitClosureFunction()); - return closure_function.ImplicitStaticClosure(); + if (check_is_entrypoint) { + CHECK_ERROR(EntryPointClosurizationError(getter_name)); + } + if (getter.SafeToClosurize()) { + // Looking for a getter but found a regular method: closurize it. + const Function& closure_function = + Function::Handle(zone, getter.ImplicitClosureFunction()); + return closure_function.ImplicitStaticClosure(); + } } } if (throw_nsm_if_absent) { @@ -3247,7 +3267,8 @@ RawObject* Class::InvokeGetter(const String& getter_name, RawObject* Class::InvokeSetter(const String& setter_name, const Instance& value, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); @@ -3261,6 +3282,10 @@ RawObject* 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); AbstractType& argument_type = AbstractType::Handle(zone, value.GetType(Heap::kOld)); @@ -3268,6 +3293,9 @@ RawObject* Class::InvokeSetter(const String& setter_name, if (field.IsNull()) { const Function& setter = Function::Handle(zone, LookupStaticFunction(internal_setter_name)); + if (!setter.IsNull() && check_is_entrypoint) { + CHECK_ERROR(setter.VerifyEntryPoint()); + } const int kNumArgs = 1; const Array& args = Array::Handle(zone, Array::New(kNumArgs)); args.SetAt(0, value); @@ -3315,31 +3343,30 @@ RawObject* Class::InvokeSetter(const String& setter_name, RawObject* Class::Invoke(const String& function_name, const Array& args, const Array& arg_names, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); // TODO(regis): Support invocation of generic functions with type arguments. const int kTypeArgsLen = 0; - const Error& error = Error::Handle(zone, EnsureIsFinalized(thread)); - if (!error.IsNull()) { - return error.raw(); - } + CHECK_ERROR(EnsureIsFinalized(thread)); Function& function = Function::Handle(zone, LookupStaticFunction(function_name)); + if (!function.IsNull() && check_is_entrypoint) { + CHECK_ERROR(function.VerifyEntryPoint()); + } + if (function.IsNull()) { // Didn't find a method: try to find a getter and invoke call on its result. - const String& getter_name = - String::Handle(zone, Field::GetterName(function_name)); - function = LookupStaticFunction(getter_name); - if (!function.IsNull()) { - // Invoke the getter. - const Object& getter_result = Object::Handle( - zone, DartEntry::InvokeFunction(function, Object::empty_array())); - if (getter_result.IsError()) { - return getter_result.raw(); + const Object& getter_result = Object::Handle( + zone, InvokeGetter(function_name, false, respect_reflectable, + check_is_entrypoint)); + if (getter_result.raw() != Object::sentinel().raw()) { + if (check_is_entrypoint) { + CHECK_ERROR(EntryPointClosurizationError(function_name)); } // Make room for the closure (receiver) in the argument list. const intptr_t num_args = args.Length(); @@ -7136,6 +7163,14 @@ RawFunction* Function::NewEvalFunction(const Class& owner, return result.raw(); } +bool Function::SafeToClosurize() const { +#if defined(DART_PRECOMPILED_RUNTIME) + return HasImplicitClosureFunction(); +#else + return true; +#endif +} + RawFunction* Function::ImplicitClosureFunction() const { // Return the existing implicit closure function if any. if (implicit_closure_function() != Function::null()) { @@ -7143,7 +7178,7 @@ RawFunction* Function::ImplicitClosureFunction() const { } #if defined(DART_PRECOMPILED_RUNTIME) // In AOT mode all implicit closures are pre-created. - UNREACHABLE(); + FATAL("Cannot create implicit closure in AOT!"); return Function::null(); #else ASSERT(!IsSignatureFunction() && !IsClosureFunction()); @@ -10706,11 +10741,15 @@ static RawObject* InvokeInstanceFunction( RawObject* Library::InvokeGetter(const String& getter_name, bool throw_nsm_if_absent, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) 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()) { return field.StaticValue(); } @@ -10727,9 +10766,21 @@ RawObject* Library::InvokeGetter(const String& getter_name, obj = LookupLocalOrReExportObject(internal_getter_name); if (obj.IsFunction()) { getter = Function::Cast(obj).raw(); + if (check_is_entrypoint) { + CHECK_ERROR(getter.VerifyEntryPoint()); + } } else { obj = LookupLocalOrReExportObject(getter_name); - if (obj.IsFunction()) { + // 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"))) || + raw() != Isolate::Current()->object_store()->root_library()) { + CHECK_ERROR(EntryPointClosurizationError(getter_name)); + } + } + 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()); @@ -10758,7 +10809,8 @@ RawObject* Library::InvokeGetter(const String& getter_name, RawObject* Library::InvokeSetter(const String& setter_name, const Instance& value, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Object& obj = Object::Handle(LookupLocalOrReExportObject(setter_name)); const String& internal_setter_name = String::Handle(Field::SetterName(setter_name)); @@ -10766,6 +10818,9 @@ RawObject* 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(), @@ -10792,6 +10847,10 @@ RawObject* Library::InvokeSetter(const String& setter_name, setter ^= obj.raw(); } + if (!setter.IsNull() && check_is_entrypoint) { + CHECK_ERROR(setter.VerifyEntryPoint()); + } + const int kNumArgs = 1; const Array& args = Array::Handle(Array::New(kNumArgs)); args.SetAt(0, value); @@ -10815,7 +10874,8 @@ RawObject* Library::InvokeSetter(const String& setter_name, RawObject* Library::Invoke(const String& function_name, const Array& args, const Array& arg_names, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { // TODO(regis): Support invocation of generic functions with type arguments. const int kTypeArgsLen = 0; @@ -10825,11 +10885,18 @@ RawObject* Library::Invoke(const String& function_name, function ^= obj.raw(); } + if (!function.IsNull() && check_is_entrypoint) { + CHECK_ERROR(function.VerifyEntryPoint()); + } + 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(InvokeGetter(function_name, false)); + const Object& getter_result = Object::Handle(InvokeGetter( + function_name, false, respect_reflectable, check_is_entrypoint)); if (getter_result.raw() != Object::sentinel().raw()) { + if (check_is_entrypoint) { + CHECK_ERROR(EntryPointClosurizationError(function_name)); + } // Make room for the closure (receiver) in arguments. intptr_t numArgs = args.Length(); const Array& call_args = Array::Handle(Array::New(numArgs + 1)); @@ -15542,7 +15609,8 @@ const char* UnwindError::ToCString() const { } RawObject* Instance::InvokeGetter(const String& getter_name, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Zone* zone = Thread::Current()->zone(); Class& klass = Class::Handle(zone, clazz()); @@ -15556,10 +15624,29 @@ RawObject* Instance::InvokeGetter(const String& getter_name, Function& function = Function::Handle( zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_getter_name)); + if (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() == RawFunction::kImplicitGetter) { + field = function.accessor_field(); + } + if (!field.IsNull()) { + CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kGetterOnly)); + } else if (!function.IsNull()) { + CHECK_ERROR(function.VerifyEntryPoint()); + } + } + // Check for method extraction when method extractors are not created. if (function.IsNull() && !FLAG_lazy_dispatchers) { function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name); - if (!function.IsNull()) { + + if (!function.IsNull() && check_is_entrypoint) { + CHECK_ERROR(EntryPointClosurizationError(getter_name)); + } + + if (!function.IsNull() && function.SafeToClosurize()) { const Function& closure_function = Function::Handle(zone, function.ImplicitClosureFunction()); return closure_function.ImplicitInstanceClosure(*this); @@ -15580,7 +15667,8 @@ RawObject* Instance::InvokeGetter(const String& getter_name, RawObject* Instance::InvokeSetter(const String& setter_name, const Instance& value, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Zone* zone = Thread::Current()->zone(); const Class& klass = Class::Handle(zone, clazz()); @@ -15594,6 +15682,20 @@ RawObject* Instance::InvokeSetter(const String& setter_name, const Function& setter = Function::Handle( zone, Resolver::ResolveDynamicAnyArgs(zone, klass, internal_setter_name)); + 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() == RawFunction::kImplicitSetter) { + field = setter.accessor_field(); + } + if (!field.IsNull()) { + CHECK_ERROR(field.VerifyEntryPoint(EntryPointPragma::kSetterOnly)); + } else if (!setter.IsNull()) { + CHECK_ERROR(setter.VerifyEntryPoint()); + } + } + const int kTypeArgsLen = 0; const int kNumArgs = 2; const Array& args = Array::Handle(zone, Array::New(kNumArgs)); @@ -15610,12 +15712,17 @@ RawObject* Instance::InvokeSetter(const String& setter_name, RawObject* Instance::Invoke(const String& function_name, const Array& args, const Array& arg_names, - bool respect_reflectable) const { + bool respect_reflectable, + bool check_is_entrypoint) const { Zone* zone = Thread::Current()->zone(); Class& klass = Class::Handle(zone, clazz()); Function& function = Function::Handle( zone, Resolver::ResolveDynamicAnyArgs(zone, klass, function_name)); + if (!function.IsNull() && check_is_entrypoint) { + CHECK_ERROR(function.VerifyEntryPoint()); + } + // TODO(regis): Support invocation of generic functions with type arguments. const int kTypeArgsLen = 0; const Array& args_descriptor = Array::Handle( @@ -15632,6 +15739,9 @@ RawObject* Instance::Invoke(const String& function_name, String::Handle(zone, Field::GetterName(function_name)); function = Resolver::ResolveDynamicAnyArgs(zone, klass, getter_name); if (!function.IsNull()) { + if (check_is_entrypoint) { + CHECK_ERROR(EntryPointClosurizationError(function_name)); + } ASSERT(function.kind() != RawFunction::kMethodExtractor); // Invoke the getter. const int kNumArgs = 1; @@ -21433,4 +21543,148 @@ void DumpTypeArgumentsTable(Isolate* isolate) { table.Release(); } +EntryPointPragma FindEntryPointPragma(Isolate* I, + const Array& metadata, + Field* reusable_field_handle, + Object* pragma) { + for (intptr_t i = 0; i < metadata.Length(); i++) { + *pragma = metadata.At(i); + if (pragma->clazz() != I->object_store()->pragma_class()) { + continue; + } + *reusable_field_handle = I->object_store()->pragma_name(); + if (Instance::Cast(*pragma).GetField(*reusable_field_handle) != + Symbols::vm_entry_point().raw()) { + continue; + } + *reusable_field_handle = I->object_store()->pragma_options(); + *pragma = Instance::Cast(*pragma).GetField(*reusable_field_handle); + if (pragma->raw() == Bool::null() || pragma->raw() == Bool::True().raw()) { + return EntryPointPragma::kAlways; + break; + } + if (pragma->raw() == Symbols::Get().raw()) { + return EntryPointPragma::kGetterOnly; + } + if (pragma->raw() == Symbols::Set().raw()) { + return EntryPointPragma::kSetterOnly; + } + } + return EntryPointPragma::kNever; +} + +DART_WARN_UNUSED_RESULT +RawError* VerifyEntryPoint(const Library& lib, + const Object& member, + const Object& annotated, + EntryPointPragma kind) { +#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().raw()); + if (!annotated.IsNull()) { + metadata = lib.GetMetadata(annotated); + } + if (metadata.IsError()) return Error::RawCast(metadata.raw()); + ASSERT(!metadata.IsNull() && metadata.IsArray()); + EntryPointPragma pragma = + FindEntryPointPragma(Isolate::Current(), Array::Cast(metadata), + &Field::Handle(), &Object::Handle()); + const bool is_marked_entrypoint = + pragma == kind || pragma == EntryPointPragma::kAlways; +#endif + if (!is_marked_entrypoint) { + const char* member_cstring = + member.IsFunction() + ? Function::Cast(member).ToLibNamePrefixedQualifiedCString() + : member.ToCString(); + 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))); + } + return Error::null(); +} + +DART_WARN_UNUSED_RESULT +RawError* EntryPointClosurizationError(const String& getter_name) { + if (!FLAG_verify_entry_points) return Error::null(); + + char const* error = OS::SCreate( + Thread::Current()->zone(), + "ERROR: Entry-points do not allow closurizing methods " + "(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))); +} + +RawError* Function::VerifyEntryPoint() const { + if (!FLAG_verify_entry_points) return Error::null(); + + const Class& cls = Class::Handle(Owner()); + const Library& lib = Library::Handle(cls.library()); + switch (kind()) { + case RawFunction::kRegularFunction: + case RawFunction::kGetterFunction: + case RawFunction::kSetterFunction: + case RawFunction::kConstructor: + return dart::VerifyEntryPoint(lib, *this, *this, + EntryPointPragma::kAlways); + break; + case RawFunction::kImplicitGetter: { + const Field& accessed = Field::Handle(accessor_field()); + return dart::VerifyEntryPoint(lib, *this, accessed, + EntryPointPragma::kGetterOnly); + break; + } + case RawFunction::kImplicitSetter: { + const Field& accessed = Field::Handle(accessor_field()); + return dart::VerifyEntryPoint(lib, *this, accessed, + EntryPointPragma::kSetterOnly); + break; + } + default: + return dart::VerifyEntryPoint(lib, *this, Object::Handle(), + EntryPointPragma::kAlways); + break; + } +} + +RawError* Field::VerifyEntryPoint(EntryPointPragma pragma) const { + if (!FLAG_verify_entry_points) return Error::null(); + const Class& cls = Class::Handle(Owner()); + const Library& lib = Library::Handle(cls.library()); + return dart::VerifyEntryPoint(lib, *this, *this, pragma); +} + +RawError* Class::VerifyEntryPoint() const { + if (!FLAG_verify_entry_points) return Error::null(); + const Library& lib = Library::Handle(library()); + if (!lib.IsNull()) { + return dart::VerifyEntryPoint(lib, *this, *this, EntryPointPragma::kAlways); + } else { + return Error::null(); + } +} + } // namespace dart diff --git a/runtime/vm/object.h b/runtime/vm/object.h index b2cadbf0971..8f789d96b88 100644 --- a/runtime/vm/object.h +++ b/runtime/vm/object.h @@ -1031,6 +1031,9 @@ class Class : public Object { bool IsPrivate() const; + DART_WARN_UNUSED_RESULT + RawError* VerifyEntryPoint() const; + // Returns an array of instance and static fields defined by this class. RawArray* fields() const { return raw_ptr()->fields_; } void SetFields(const Array& value) const; @@ -1206,13 +1209,16 @@ class Class : public Object { RawObject* Invoke(const String& selector, const Array& arguments, const Array& argument_names, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; RawObject* InvokeGetter(const String& selector, bool throw_nsm_if_absent, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; RawObject* InvokeSetter(const String& selector, const Instance& argument, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) 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 @@ -2226,6 +2232,8 @@ class Function : public Object { // It is not the only Code object that points to this function. RawCode* CurrentCode() const { return CurrentCodeOf(raw()); } + bool SafeToClosurize() const; + static RawCode* CurrentCodeOf(const RawFunction* function) { return function->ptr()->code_; } @@ -2706,6 +2714,9 @@ class Function : public Object { return modifier() != RawFunction::kNoModifier; } + DART_WARN_UNUSED_RESULT + RawError* VerifyEntryPoint() const; + static intptr_t InstanceSize() { return RoundedAllocationSize(sizeof(RawFunction)); } @@ -3068,6 +3079,8 @@ class RedirectionData : public Object { friend class HeapProfiler; }; +enum class EntryPointPragma { kAlways, kNever, kGetterOnly, kSetterOnly }; + class Field : public Object { public: RawField* Original() const; @@ -3166,6 +3179,9 @@ class Field : public Object { // Used by class finalizer, otherwise initialized in constructor. void SetFieldType(const AbstractType& value) const; + DART_WARN_UNUSED_RESULT + RawError* VerifyEntryPoint(EntryPointPragma kind) const; + static intptr_t InstanceSize() { return RoundedAllocationSize(sizeof(RawField)); } @@ -3640,13 +3656,16 @@ class Library : public Object { RawObject* Invoke(const String& selector, const Array& arguments, const Array& argument_names, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; RawObject* InvokeGetter(const String& selector, bool throw_nsm_if_absent, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; RawObject* InvokeSetter(const String& selector, const Instance& argument, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) 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 @@ -5797,12 +5816,15 @@ class Instance : public Object { RawObject* Invoke(const String& selector, const Array& arguments, const Array& argument_names, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; RawObject* InvokeGetter(const String& selector, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; RawObject* InvokeSetter(const String& selector, const Instance& argument, - bool respect_reflectable = true) const; + bool respect_reflectable = true, + bool check_is_entrypoint = false) const; // Evaluate the given expression as if it appeared in an instance method of // this instance and return the resulting value, or an error object if @@ -9384,6 +9406,14 @@ using SubtypeTestCacheTable = ArrayOfTuplesView E.ctor(); + + void fn0() {} + + @pragma("vm:entry-point") + void fn1() {} + + static void fn2() {} + + @pragma("vm:entry-point") + static void fn3() {} + + void Function() fld0; + + @pragma("vm:entry-point") + void Function() fld1; + + @pragma("vm:entry-point", "get") + void Function() fld2; + + @pragma("vm:entry-point", "set") + void Function() fld3; +} + +void fn0() {} + +@pragma("vm:entry-point") +void fn1() {} + +class E extends D { + E.ctor(); +} + +@pragma("vm:entry-point") +class F { + static void Function() fld0; + + @pragma("vm:entry-point") + static void Function() fld1; + + @pragma("vm:entry-point", "get") + static void Function() fld2; + + @pragma("vm:entry-point", "set") + static void Function() fld3; +} + +void Function() fld0; + +@pragma("vm:entry-point") +void Function() fld1; + +@pragma("vm:entry-point", "get") +void Function() fld2; + +@pragma("vm:entry-point", "set") +void Function() fld3; diff --git a/tests/standalone_2/standalone_2_kernel.status b/tests/standalone_2/standalone_2_kernel.status index fa50ccf32b4..c0e887455a9 100644 --- a/tests/standalone_2/standalone_2_kernel.status +++ b/tests/standalone_2/standalone_2_kernel.status @@ -63,6 +63,9 @@ io/test_extension_fail_test: Fail # Memory leak (issue 34724) [ $builder_tag == optimization_counter_threshold && ($compiler == dartk || $compiler == dartkb) ] map_insert_remove_oom_test: Skip # Heap limit too low. +[ $compiler != dartk && $compiler != dartkb ] +io/entrypoints_verification_test: Skip # Test expects JIT mode dart executable + # The failures below still need to be investigated and possibly fixed, or marked as skipped. [ $compiler == dartkb && $strong ] io/code_collection_test: RuntimeError # Reruns the same script (dill file without AST) without passing --enable-interpreter or --use-bytecode-compiler.