mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 01:25:41 +00:00
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:
parent
d73d3183a2
commit
267e91a0ea
1
BUILD.gn
1
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",
|
||||
]
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 '*'.
|
||||
|
|
160
runtime/bin/entrypoints_verification_test_extension.cc
Normal file
160
runtime/bin/entrypoints_verification_test_extension.cc
Normal 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();
|
||||
}
|
|
@ -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)
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
//
|
||||
|
|
|
@ -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,33 +3207,45 @@ 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()) {
|
||||
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) {
|
||||
return ThrowNoSuchMethod(
|
||||
AbstractType::Handle(zone, RareType()), getter_name,
|
||||
|
@ -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();
|
||||
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
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) \
|
||||
|
|
89
tests/standalone_2/io/entrypoints_verification_test.dart
Normal file
89
tests/standalone_2/io/entrypoints_verification_test.dart
Normal 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;
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue