Re-land "[vm] Prevent access to non-annotated members through native API."

Two fixes:

  - Make entrypoints_verification_test work on configurations where CFE compilation
    is separate from execution.
  - Add missing entry-point annotation for Windows.

The original revision is in patchset 1.

Change-Id: I6c4b52b1bae7bc730546dad6a3e31d8625f850b1
Cq-Include-Trybots: luci.dart.try:vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-precomp-mac-release-simarm64-try, vm-kernel-precomp-win-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/90942
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Samir Jindel 2019-01-29 15:10:34 +00:00 committed by commit-bot@chromium.org
parent d73d3183a2
commit 267e91a0ea
16 changed files with 698 additions and 135 deletions

View file

@ -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",
]

View file

@ -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",

View file

@ -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 '*'.

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#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();
}

View file

@ -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 <windows.h> // NOLINT
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
return true;
}
#endif // defined(HOST_OS_WINDOWS)

View file

@ -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

View file

@ -150,6 +150,7 @@ class _GrowableList<T> extends ListBase<T> {
void _setIndexed(int index, T value) native "GrowableList_setIndexed";
@pragma("vm:entry-point")
void add(T value) {
var len = length;
if (len == _capacity) {

View file

@ -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;
}

View file

@ -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
}

View file

@ -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.
//

View file

@ -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

View file

@ -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<SubtypeTestCache::Entries,
void DumpTypeTable(Isolate* isolate);
void DumpTypeArgumentsTable(Isolate* isolate);
EntryPointPragma FindEntryPointPragma(Isolate* I,
const Array& metadata,
Field* reusable_field_handle,
Object* reusable_object_handle);
DART_WARN_UNUSED_RESULT
RawError* EntryPointClosurizationError(const String& getter_name);
} // namespace dart
#endif // RUNTIME_VM_OBJECT_H_

View file

@ -220,6 +220,8 @@ void ObjectStore::InitKnownObjects() {
cls = core_lib.LookupClassAllowPrivate(Symbols::Pragma());
ASSERT(!cls.IsNull());
set_pragma_class(cls);
set_pragma_name(Field::Handle(cls.LookupField(Symbols::name())));
set_pragma_options(Field::Handle(cls.LookupField(Symbols::options())));
cls = core_lib.LookupClassAllowPrivate(Symbols::_GrowableList());
ASSERT(!cls.IsNull());

View file

@ -59,6 +59,8 @@ class ObjectPointerVisitor;
RW(TypeArguments, type_argument_string_string) \
RW(Class, compiletime_error_class) \
RW(Class, pragma_class) \
RW(Field, pragma_name) \
RW(Field, pragma_options) \
RW(Class, future_class) \
RW(Class, completer_class) \
RW(Class, symbol_class) \

View file

@ -0,0 +1,89 @@
// 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.
//
// VMOptions=--verify-entry-points=true
import 'dart:io';
import 'dart:convert';
import 'dart:math';
import 'package:path/path.dart';
import 'package:expect/expect.dart';
import 'dart-ext:entrypoints_verification_test_extension';
void RunTest() native "RunTest";
main() {
RunTest();
new C();
new D();
}
class C {}
@pragma("vm:entry-point")
class D {
D();
@pragma("vm:entry-point")
D.defined();
@pragma("vm:entry-point")
factory D.fact() => 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;

View file

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