7514ce941e
Threads in the native or blocked states don't prevent safepoints, so they may run concurrently with a safepoint operation like GC. It is not safe for handles to be allocated while the GC is visiting them, so these threads must not allocate handles. Assert only threads in the VM or generated states, which prevent safepoints until they check in, may allocate handles. (Generated code does not allocate handles, but leaf runtime entries remain in the generated state.) Bug: https://github.com/dart-lang/sdk/issues/34883 Change-Id: I1a211778f7ef96b53a2405f0ee9dde7871b122b6 Reviewed-on: https://dart-review.googlesource.com/c/81540 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Siva Annamalai <asiva@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
445 lines
16 KiB
C++
445 lines
16 KiB
C++
// Copyright (c) 2011, 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 "vm/native_entry.h"
|
|
|
|
#include "include/dart_api.h"
|
|
|
|
#include "vm/bootstrap.h"
|
|
#include "vm/code_patcher.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/dart_api_state.h"
|
|
#include "vm/heap/safepoint.h"
|
|
#include "vm/native_symbol.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/reusable_handles.h"
|
|
#include "vm/stack_frame.h"
|
|
#include "vm/symbols.h"
|
|
#include "vm/tags.h"
|
|
|
|
namespace dart {
|
|
|
|
DEFINE_FLAG(bool,
|
|
trace_natives,
|
|
false,
|
|
"Trace invocation of natives (debug mode only)");
|
|
|
|
void DartNativeThrowArgumentException(const Instance& instance) {
|
|
const Array& __args__ = Array::Handle(Array::New(1));
|
|
__args__.SetAt(0, instance);
|
|
Exceptions::ThrowByType(Exceptions::kArgument, __args__);
|
|
}
|
|
|
|
NativeFunction NativeEntry::ResolveNative(const Library& library,
|
|
const String& function_name,
|
|
int number_of_arguments,
|
|
bool* auto_setup_scope) {
|
|
// Now resolve the native function to the corresponding native entrypoint.
|
|
if (library.native_entry_resolver() == NULL) {
|
|
// Native methods are not allowed in the library to which this
|
|
// class belongs in.
|
|
return NULL;
|
|
}
|
|
Dart_NativeFunction native_function = NULL;
|
|
{
|
|
Thread* T = Thread::Current();
|
|
Api::Scope api_scope(T);
|
|
Dart_Handle api_function_name = Api::NewHandle(T, function_name.raw());
|
|
{
|
|
TransitionVMToNative transition(T);
|
|
Dart_NativeEntryResolver resolver = library.native_entry_resolver();
|
|
native_function =
|
|
resolver(api_function_name, number_of_arguments, auto_setup_scope);
|
|
}
|
|
}
|
|
return reinterpret_cast<NativeFunction>(native_function);
|
|
}
|
|
|
|
const uint8_t* NativeEntry::ResolveSymbolInLibrary(const Library& library,
|
|
uword pc) {
|
|
Dart_NativeEntrySymbol symbol_resolver =
|
|
library.native_entry_symbol_resolver();
|
|
if (symbol_resolver == NULL) {
|
|
// Cannot reverse lookup native entries.
|
|
return NULL;
|
|
}
|
|
return symbol_resolver(reinterpret_cast<Dart_NativeFunction>(pc));
|
|
}
|
|
|
|
const uint8_t* NativeEntry::ResolveSymbol(uword pc) {
|
|
Thread* thread = Thread::Current();
|
|
REUSABLE_GROWABLE_OBJECT_ARRAY_HANDLESCOPE(thread);
|
|
GrowableObjectArray& libs = reused_growable_object_array_handle.Handle();
|
|
libs ^= thread->isolate()->object_store()->libraries();
|
|
ASSERT(!libs.IsNull());
|
|
intptr_t num_libs = libs.Length();
|
|
for (intptr_t i = 0; i < num_libs; i++) {
|
|
REUSABLE_LIBRARY_HANDLESCOPE(thread);
|
|
Library& lib = reused_library_handle.Handle();
|
|
lib ^= libs.At(i);
|
|
ASSERT(!lib.IsNull());
|
|
const uint8_t* r = ResolveSymbolInLibrary(lib, pc);
|
|
if (r != NULL) {
|
|
return r;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
bool NativeEntry::ReturnValueIsError(NativeArguments* arguments) {
|
|
RawObject* retval = arguments->ReturnValue();
|
|
return (retval->IsHeapObject() &&
|
|
RawObject::IsErrorClassId(retval->GetClassId()));
|
|
}
|
|
|
|
void NativeEntry::PropagateErrors(NativeArguments* arguments) {
|
|
Thread* thread = arguments->thread();
|
|
thread->UnwindScopes(thread->top_exit_frame_info());
|
|
TransitionNativeToVM transition(thread);
|
|
|
|
// The thread->zone() is different here than before we unwound.
|
|
const Object& error =
|
|
Object::Handle(thread->zone(), arguments->ReturnValue());
|
|
Exceptions::PropagateError(Error::Cast(error));
|
|
UNREACHABLE();
|
|
}
|
|
|
|
uword NativeEntry::BootstrapNativeCallWrapperEntry() {
|
|
uword entry =
|
|
reinterpret_cast<uword>(NativeEntry::BootstrapNativeCallWrapper);
|
|
return entry;
|
|
}
|
|
|
|
void NativeEntry::BootstrapNativeCallWrapper(Dart_NativeArguments args,
|
|
Dart_NativeFunction func) {
|
|
func(args);
|
|
}
|
|
|
|
uword NativeEntry::NoScopeNativeCallWrapperEntry() {
|
|
uword entry = reinterpret_cast<uword>(NativeEntry::NoScopeNativeCallWrapper);
|
|
#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC)
|
|
// DBC does not use redirections unlike other simulators.
|
|
entry = Simulator::RedirectExternalReference(
|
|
entry, Simulator::kNativeCall, NativeEntry::kNumCallWrapperArguments);
|
|
#endif
|
|
return entry;
|
|
}
|
|
|
|
void NativeEntry::NoScopeNativeCallWrapper(Dart_NativeArguments args,
|
|
Dart_NativeFunction func) {
|
|
CHECK_STACK_ALIGNMENT;
|
|
NoScopeNativeCallWrapperNoStackCheck(args, func);
|
|
}
|
|
|
|
void NativeEntry::NoScopeNativeCallWrapperNoStackCheck(
|
|
Dart_NativeArguments args,
|
|
Dart_NativeFunction func) {
|
|
VERIFY_ON_TRANSITION;
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
/* Tell MemorySanitizer 'arguments' is initialized by generated code. */
|
|
MSAN_UNPOISON(arguments, sizeof(*arguments));
|
|
Thread* thread = arguments->thread();
|
|
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
|
|
{
|
|
TransitionGeneratedToNative transition(thread);
|
|
func(args);
|
|
if (ReturnValueIsError(arguments)) {
|
|
PropagateErrors(arguments);
|
|
}
|
|
}
|
|
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
|
|
VERIFY_ON_TRANSITION;
|
|
}
|
|
|
|
uword NativeEntry::AutoScopeNativeCallWrapperEntry() {
|
|
uword entry =
|
|
reinterpret_cast<uword>(NativeEntry::AutoScopeNativeCallWrapper);
|
|
#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC)
|
|
// DBC does not use redirections unlike other simulators.
|
|
entry = Simulator::RedirectExternalReference(
|
|
entry, Simulator::kNativeCall, NativeEntry::kNumCallWrapperArguments);
|
|
#endif
|
|
return entry;
|
|
}
|
|
|
|
void NativeEntry::AutoScopeNativeCallWrapper(Dart_NativeArguments args,
|
|
Dart_NativeFunction func) {
|
|
CHECK_STACK_ALIGNMENT;
|
|
AutoScopeNativeCallWrapperNoStackCheck(args, func);
|
|
}
|
|
|
|
void NativeEntry::AutoScopeNativeCallWrapperNoStackCheck(
|
|
Dart_NativeArguments args,
|
|
Dart_NativeFunction func) {
|
|
VERIFY_ON_TRANSITION;
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
/* Tell MemorySanitizer 'arguments' is initialized by generated code. */
|
|
MSAN_UNPOISON(arguments, sizeof(*arguments));
|
|
Thread* thread = arguments->thread();
|
|
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
|
|
{
|
|
Isolate* isolate = thread->isolate();
|
|
ApiState* state = isolate->api_state();
|
|
ASSERT(state != NULL);
|
|
TRACE_NATIVE_CALL("0x%" Px "", reinterpret_cast<uintptr_t>(func));
|
|
thread->EnterApiScope();
|
|
{
|
|
TransitionGeneratedToNative transition(thread);
|
|
func(args);
|
|
if (ReturnValueIsError(arguments)) {
|
|
PropagateErrors(arguments);
|
|
}
|
|
}
|
|
thread->ExitApiScope();
|
|
DEOPTIMIZE_ALOT;
|
|
}
|
|
ASSERT(thread->execution_state() == Thread::kThreadInGenerated);
|
|
VERIFY_ON_TRANSITION;
|
|
}
|
|
|
|
static NativeFunction ResolveNativeFunction(Zone* zone,
|
|
const Function& func,
|
|
bool* is_bootstrap_native,
|
|
bool* is_auto_scope) {
|
|
const Class& cls = Class::Handle(zone, func.Owner());
|
|
const Library& library = Library::Handle(zone, cls.library());
|
|
|
|
*is_bootstrap_native =
|
|
Bootstrap::IsBootstrapResolver(library.native_entry_resolver());
|
|
|
|
const String& native_name = String::Handle(zone, func.native_name());
|
|
ASSERT(!native_name.IsNull());
|
|
|
|
const int num_params = NativeArguments::ParameterCountForResolution(func);
|
|
NativeFunction native_function = NativeEntry::ResolveNative(
|
|
library, native_name, num_params, is_auto_scope);
|
|
if (native_function == NULL) {
|
|
FATAL2("Failed to resolve native function '%s' in '%s'\n",
|
|
native_name.ToCString(), func.ToQualifiedCString());
|
|
}
|
|
return native_function;
|
|
}
|
|
|
|
uword NativeEntry::LinkNativeCallEntry() {
|
|
uword entry = reinterpret_cast<uword>(NativeEntry::LinkNativeCall);
|
|
#if defined(USING_SIMULATOR) && !defined(TARGET_ARCH_DBC)
|
|
// DBC does not use redirections unlike other simulators.
|
|
entry = Simulator::RedirectExternalReference(
|
|
entry, Simulator::kBootstrapNativeCall, NativeEntry::kNumArguments);
|
|
#endif
|
|
return entry;
|
|
}
|
|
|
|
void NativeEntry::LinkNativeCall(Dart_NativeArguments args) {
|
|
CHECK_STACK_ALIGNMENT;
|
|
VERIFY_ON_TRANSITION;
|
|
NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args);
|
|
/* Tell MemorySanitizer 'arguments' is initialized by generated code. */
|
|
MSAN_UNPOISON(arguments, sizeof(*arguments));
|
|
TRACE_NATIVE_CALL("%s", "LinkNative");
|
|
|
|
NativeFunction target_function = NULL;
|
|
bool is_bootstrap_native = false;
|
|
bool is_auto_scope = true;
|
|
|
|
{
|
|
TransitionGeneratedToVM transition(arguments->thread());
|
|
StackZone stack_zone(arguments->thread());
|
|
Zone* zone = stack_zone.GetZone();
|
|
|
|
DartFrameIterator iterator(arguments->thread(),
|
|
StackFrameIterator::kNoCrossThreadIteration);
|
|
StackFrame* caller_frame = iterator.NextFrame();
|
|
|
|
const Code& code = Code::Handle(zone, caller_frame->LookupDartCode());
|
|
const Function& func = Function::Handle(zone, code.function());
|
|
|
|
if (FLAG_trace_natives) {
|
|
THR_Print("Resolving native target for %s\n", func.ToCString());
|
|
}
|
|
|
|
target_function =
|
|
ResolveNativeFunction(arguments->thread()->zone(), func,
|
|
&is_bootstrap_native, &is_auto_scope);
|
|
ASSERT(target_function != NULL);
|
|
|
|
#if defined(DEBUG) && !defined(TARGET_ARCH_DBC)
|
|
NativeFunction current_function = NULL;
|
|
if (caller_frame->is_interpreted()) {
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(FLAG_enable_interpreter);
|
|
NativeFunctionWrapper current_trampoline = KBCPatcher::GetNativeCallAt(
|
|
caller_frame->pc(), code, ¤t_function);
|
|
ASSERT(current_function ==
|
|
reinterpret_cast<NativeFunction>(LinkNativeCall));
|
|
ASSERT(current_trampoline == &BootstrapNativeCallWrapper ||
|
|
current_trampoline == &AutoScopeNativeCallWrapper ||
|
|
current_trampoline == &NoScopeNativeCallWrapper);
|
|
#else
|
|
UNREACHABLE();
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
} else {
|
|
const Code& current_trampoline =
|
|
Code::Handle(zone, CodePatcher::GetNativeCallAt(
|
|
caller_frame->pc(), code, ¤t_function));
|
|
#if !defined(USING_SIMULATOR)
|
|
ASSERT(current_function ==
|
|
reinterpret_cast<NativeFunction>(LinkNativeCall));
|
|
#else
|
|
ASSERT(
|
|
current_function ==
|
|
reinterpret_cast<NativeFunction>(Simulator::RedirectExternalReference(
|
|
reinterpret_cast<uword>(LinkNativeCall),
|
|
Simulator::kBootstrapNativeCall, NativeEntry::kNumArguments)));
|
|
#endif
|
|
ASSERT(current_trampoline.raw() ==
|
|
StubCode::CallBootstrapNative_entry()->code());
|
|
}
|
|
#endif
|
|
|
|
NativeFunction patch_target_function = target_function;
|
|
#if defined(TARGET_ARCH_DBC)
|
|
NativeFunctionWrapper trampoline;
|
|
if (is_bootstrap_native) {
|
|
trampoline = &BootstrapNativeCallWrapper;
|
|
} else if (is_auto_scope) {
|
|
trampoline = &AutoScopeNativeCallWrapper;
|
|
} else {
|
|
trampoline = &NoScopeNativeCallWrapper;
|
|
}
|
|
CodePatcher::PatchNativeCallAt(caller_frame->pc(), code,
|
|
patch_target_function, trampoline);
|
|
#else
|
|
if (caller_frame->is_interpreted()) {
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
ASSERT(FLAG_enable_interpreter);
|
|
NativeFunctionWrapper trampoline;
|
|
if (is_bootstrap_native) {
|
|
trampoline = &BootstrapNativeCallWrapper;
|
|
} else if (is_auto_scope) {
|
|
trampoline = &AutoScopeNativeCallWrapper;
|
|
} else {
|
|
trampoline = &NoScopeNativeCallWrapper;
|
|
}
|
|
KBCPatcher::PatchNativeCallAt(caller_frame->pc(), code,
|
|
patch_target_function, trampoline);
|
|
#else
|
|
UNREACHABLE();
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
} else {
|
|
Code& trampoline = Code::Handle(zone);
|
|
if (is_bootstrap_native) {
|
|
trampoline = StubCode::CallBootstrapNative_entry()->code();
|
|
#if defined(USING_SIMULATOR)
|
|
patch_target_function = reinterpret_cast<NativeFunction>(
|
|
Simulator::RedirectExternalReference(
|
|
reinterpret_cast<uword>(patch_target_function),
|
|
Simulator::kBootstrapNativeCall, NativeEntry::kNumArguments));
|
|
#endif // defined USING_SIMULATOR
|
|
} else if (is_auto_scope) {
|
|
trampoline = StubCode::CallAutoScopeNative_entry()->code();
|
|
} else {
|
|
trampoline = StubCode::CallNoScopeNative_entry()->code();
|
|
}
|
|
CodePatcher::PatchNativeCallAt(caller_frame->pc(), code,
|
|
patch_target_function, trampoline);
|
|
}
|
|
#endif // defined TARGET_ARCH_DBC
|
|
|
|
if (FLAG_trace_natives) {
|
|
THR_Print(" -> %p (%s)\n", target_function,
|
|
is_bootstrap_native ? "bootstrap" : "non-bootstrap");
|
|
}
|
|
}
|
|
VERIFY_ON_TRANSITION;
|
|
|
|
// Tail-call resolved target.
|
|
if (is_bootstrap_native) {
|
|
target_function(arguments);
|
|
} else if (is_auto_scope) {
|
|
// Because this call is within a compilation unit, Clang doesn't respect
|
|
// the ABI alignment here.
|
|
NativeEntry::AutoScopeNativeCallWrapperNoStackCheck(
|
|
args, reinterpret_cast<Dart_NativeFunction>(target_function));
|
|
} else {
|
|
// Because this call is within a compilation unit, Clang doesn't respect
|
|
// the ABI alignment here.
|
|
NativeEntry::NoScopeNativeCallWrapperNoStackCheck(
|
|
args, reinterpret_cast<Dart_NativeFunction>(target_function));
|
|
}
|
|
}
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
// Note: not GC safe. Use with care.
|
|
NativeEntryData::Payload* NativeEntryData::FromTypedArray(RawTypedData* data) {
|
|
return reinterpret_cast<Payload*>(data->ptr()->data());
|
|
}
|
|
|
|
MethodRecognizer::Kind NativeEntryData::kind() const {
|
|
return FromTypedArray(data_.raw())->kind;
|
|
}
|
|
|
|
void NativeEntryData::set_kind(MethodRecognizer::Kind value) const {
|
|
FromTypedArray(data_.raw())->kind = value;
|
|
}
|
|
|
|
MethodRecognizer::Kind NativeEntryData::GetKind(RawTypedData* data) {
|
|
return FromTypedArray(data)->kind;
|
|
}
|
|
|
|
NativeFunctionWrapper NativeEntryData::trampoline() const {
|
|
return FromTypedArray(data_.raw())->trampoline;
|
|
}
|
|
|
|
void NativeEntryData::set_trampoline(NativeFunctionWrapper value) const {
|
|
FromTypedArray(data_.raw())->trampoline = value;
|
|
}
|
|
|
|
NativeFunctionWrapper NativeEntryData::GetTrampoline(RawTypedData* data) {
|
|
return FromTypedArray(data)->trampoline;
|
|
}
|
|
|
|
NativeFunction NativeEntryData::native_function() const {
|
|
return FromTypedArray(data_.raw())->native_function;
|
|
}
|
|
|
|
void NativeEntryData::set_native_function(NativeFunction value) const {
|
|
FromTypedArray(data_.raw())->native_function = value;
|
|
}
|
|
|
|
NativeFunction NativeEntryData::GetNativeFunction(RawTypedData* data) {
|
|
return FromTypedArray(data)->native_function;
|
|
}
|
|
|
|
intptr_t NativeEntryData::argc_tag() const {
|
|
return FromTypedArray(data_.raw())->argc_tag;
|
|
}
|
|
|
|
void NativeEntryData::set_argc_tag(intptr_t value) const {
|
|
FromTypedArray(data_.raw())->argc_tag = value;
|
|
}
|
|
|
|
intptr_t NativeEntryData::GetArgcTag(RawTypedData* data) {
|
|
return FromTypedArray(data)->argc_tag;
|
|
}
|
|
|
|
RawTypedData* NativeEntryData::New(MethodRecognizer::Kind kind,
|
|
NativeFunctionWrapper trampoline,
|
|
NativeFunction native_function,
|
|
intptr_t argc_tag) {
|
|
const TypedData& data = TypedData::Handle(
|
|
TypedData::New(kTypedDataUint8ArrayCid, sizeof(Payload), Heap::kOld));
|
|
NativeEntryData native_entry(data);
|
|
native_entry.set_kind(kind);
|
|
native_entry.set_trampoline(trampoline);
|
|
native_entry.set_native_function(native_function);
|
|
native_entry.set_argc_tag(argc_tag);
|
|
return data.raw();
|
|
}
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
} // namespace dart
|