| 1 | // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/native_entry.h" |
| 6 | |
| 7 | #include "include/dart_api.h" |
| 8 | |
| 9 | #include "vm/bootstrap.h" |
| 10 | #include "vm/code_patcher.h" |
| 11 | #include "vm/dart_api_impl.h" |
| 12 | #include "vm/dart_api_state.h" |
| 13 | #include "vm/heap/safepoint.h" |
| 14 | #include "vm/native_symbol.h" |
| 15 | #include "vm/object_store.h" |
| 16 | #include "vm/reusable_handles.h" |
| 17 | #include "vm/stack_frame.h" |
| 18 | #include "vm/symbols.h" |
| 19 | #include "vm/tags.h" |
| 20 | |
| 21 | namespace dart { |
| 22 | |
| 23 | void DartNativeThrowTypeArgumentCountException(int num_type_args, |
| 24 | int num_type_args_expected) { |
| 25 | const String& error = String::Handle(String::NewFormatted( |
| 26 | "Wrong number of type arguments (%i), expected %i type arguments" , |
| 27 | num_type_args, num_type_args_expected)); |
| 28 | Exceptions::ThrowArgumentError(error); |
| 29 | } |
| 30 | |
| 31 | void DartNativeThrowArgumentException(const Instance& instance) { |
| 32 | const Array& __args__ = Array::Handle(Array::New(1)); |
| 33 | __args__.SetAt(0, instance); |
| 34 | Exceptions::ThrowByType(Exceptions::kArgument, __args__); |
| 35 | } |
| 36 | |
| 37 | NativeFunction NativeEntry::ResolveNative(const Library& library, |
| 38 | const String& function_name, |
| 39 | int number_of_arguments, |
| 40 | bool* auto_setup_scope) { |
| 41 | // Now resolve the native function to the corresponding native entrypoint. |
| 42 | if (library.native_entry_resolver() == NULL) { |
| 43 | // Native methods are not allowed in the library to which this |
| 44 | // class belongs in. |
| 45 | return NULL; |
| 46 | } |
| 47 | Dart_NativeFunction native_function = NULL; |
| 48 | { |
| 49 | Thread* T = Thread::Current(); |
| 50 | Api::Scope api_scope(T); |
| 51 | Dart_Handle api_function_name = Api::NewHandle(T, function_name.raw()); |
| 52 | { |
| 53 | Dart_NativeEntryResolver resolver = library.native_entry_resolver(); |
| 54 | TransitionVMToNative transition(T); |
| 55 | native_function = |
| 56 | resolver(api_function_name, number_of_arguments, auto_setup_scope); |
| 57 | } |
| 58 | } |
| 59 | return reinterpret_cast<NativeFunction>(native_function); |
| 60 | } |
| 61 | |
| 62 | const uint8_t* NativeEntry::ResolveSymbolInLibrary(const Library& library, |
| 63 | uword pc) { |
| 64 | Dart_NativeEntrySymbol symbol_resolver = |
| 65 | library.native_entry_symbol_resolver(); |
| 66 | if (symbol_resolver == NULL) { |
| 67 | // Cannot reverse lookup native entries. |
| 68 | return NULL; |
| 69 | } |
| 70 | return symbol_resolver(reinterpret_cast<Dart_NativeFunction>(pc)); |
| 71 | } |
| 72 | |
| 73 | const uint8_t* NativeEntry::ResolveSymbol(uword pc) { |
| 74 | Thread* thread = Thread::Current(); |
| 75 | REUSABLE_GROWABLE_OBJECT_ARRAY_HANDLESCOPE(thread); |
| 76 | GrowableObjectArray& libs = reused_growable_object_array_handle.Handle(); |
| 77 | libs = thread->isolate()->object_store()->libraries(); |
| 78 | ASSERT(!libs.IsNull()); |
| 79 | intptr_t num_libs = libs.Length(); |
| 80 | for (intptr_t i = 0; i < num_libs; i++) { |
| 81 | REUSABLE_LIBRARY_HANDLESCOPE(thread); |
| 82 | Library& lib = reused_library_handle.Handle(); |
| 83 | lib ^= libs.At(i); |
| 84 | ASSERT(!lib.IsNull()); |
| 85 | const uint8_t* r = ResolveSymbolInLibrary(lib, pc); |
| 86 | if (r != NULL) { |
| 87 | return r; |
| 88 | } |
| 89 | } |
| 90 | return NULL; |
| 91 | } |
| 92 | |
| 93 | bool NativeEntry::ReturnValueIsError(NativeArguments* arguments) { |
| 94 | ObjectPtr retval = arguments->ReturnValue(); |
| 95 | return (retval->IsHeapObject() && IsErrorClassId(retval->GetClassId())); |
| 96 | } |
| 97 | |
| 98 | void NativeEntry::PropagateErrors(NativeArguments* arguments) { |
| 99 | Thread* thread = arguments->thread(); |
| 100 | thread->UnwindScopes(thread->top_exit_frame_info()); |
| 101 | TransitionNativeToVM transition(thread); |
| 102 | |
| 103 | // The thread->zone() is different here than before we unwound. |
| 104 | const Object& error = |
| 105 | Object::Handle(thread->zone(), arguments->ReturnValue()); |
| 106 | Exceptions::PropagateError(Error::Cast(error)); |
| 107 | UNREACHABLE(); |
| 108 | } |
| 109 | |
| 110 | uword NativeEntry::BootstrapNativeCallWrapperEntry() { |
| 111 | uword entry = |
| 112 | reinterpret_cast<uword>(NativeEntry::BootstrapNativeCallWrapper); |
| 113 | #if defined(USING_SIMULATOR) |
| 114 | entry = Simulator::RedirectExternalReference( |
| 115 | entry, Simulator::kNativeCallWrapper, |
| 116 | NativeEntry::kNumCallWrapperArguments); |
| 117 | #endif |
| 118 | return entry; |
| 119 | } |
| 120 | |
| 121 | void NativeEntry::BootstrapNativeCallWrapper(Dart_NativeArguments args, |
| 122 | Dart_NativeFunction func) { |
| 123 | CHECK_STACK_ALIGNMENT; |
| 124 | if (func == LinkNativeCall) { |
| 125 | func(args); |
| 126 | return; |
| 127 | } |
| 128 | |
| 129 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 130 | // Tell MemorySanitizer 'arguments' is initialized by generated code. |
| 131 | MSAN_UNPOISON(arguments, sizeof(*arguments)); |
| 132 | { |
| 133 | Thread* thread = arguments->thread(); |
| 134 | ASSERT(thread == Thread::Current()); |
| 135 | TransitionGeneratedToVM transition(thread); |
| 136 | StackZone zone(thread); |
| 137 | // Be careful holding return_value_unsafe without a handle here. |
| 138 | // A return of Object::sentinel means the return value has already |
| 139 | // been set. |
| 140 | ObjectPtr return_value_unsafe = reinterpret_cast<BootstrapNativeFunction>( |
| 141 | func)(thread, zone.GetZone(), arguments); |
| 142 | if (return_value_unsafe != Object::sentinel().raw()) { |
| 143 | ASSERT(return_value_unsafe->IsDartInstance()); |
| 144 | arguments->SetReturnUnsafe(return_value_unsafe); |
| 145 | } |
| 146 | DEOPTIMIZE_ALOT; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | uword NativeEntry::NoScopeNativeCallWrapperEntry() { |
| 151 | uword entry = reinterpret_cast<uword>(NativeEntry::NoScopeNativeCallWrapper); |
| 152 | #if defined(USING_SIMULATOR) |
| 153 | entry = Simulator::RedirectExternalReference( |
| 154 | entry, Simulator::kNativeCallWrapper, |
| 155 | NativeEntry::kNumCallWrapperArguments); |
| 156 | #endif |
| 157 | return entry; |
| 158 | } |
| 159 | |
| 160 | void NativeEntry::NoScopeNativeCallWrapper(Dart_NativeArguments args, |
| 161 | Dart_NativeFunction func) { |
| 162 | CHECK_STACK_ALIGNMENT; |
| 163 | NoScopeNativeCallWrapperNoStackCheck(args, func); |
| 164 | } |
| 165 | |
| 166 | void NativeEntry::NoScopeNativeCallWrapperNoStackCheck( |
| 167 | Dart_NativeArguments args, |
| 168 | Dart_NativeFunction func) { |
| 169 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 170 | // Tell MemorySanitizer 'arguments' is initialized by generated code. |
| 171 | MSAN_UNPOISON(arguments, sizeof(*arguments)); |
| 172 | Thread* thread = arguments->thread(); |
| 173 | ASSERT(thread->execution_state() == Thread::kThreadInGenerated); |
| 174 | { |
| 175 | TransitionGeneratedToNative transition(thread); |
| 176 | func(args); |
| 177 | if (ReturnValueIsError(arguments)) { |
| 178 | PropagateErrors(arguments); |
| 179 | } |
| 180 | } |
| 181 | ASSERT(thread->execution_state() == Thread::kThreadInGenerated); |
| 182 | } |
| 183 | |
| 184 | uword NativeEntry::AutoScopeNativeCallWrapperEntry() { |
| 185 | uword entry = |
| 186 | reinterpret_cast<uword>(NativeEntry::AutoScopeNativeCallWrapper); |
| 187 | #if defined(USING_SIMULATOR) |
| 188 | entry = Simulator::RedirectExternalReference( |
| 189 | entry, Simulator::kNativeCallWrapper, |
| 190 | NativeEntry::kNumCallWrapperArguments); |
| 191 | #endif |
| 192 | return entry; |
| 193 | } |
| 194 | |
| 195 | void NativeEntry::AutoScopeNativeCallWrapper(Dart_NativeArguments args, |
| 196 | Dart_NativeFunction func) { |
| 197 | CHECK_STACK_ALIGNMENT; |
| 198 | AutoScopeNativeCallWrapperNoStackCheck(args, func); |
| 199 | } |
| 200 | |
| 201 | void NativeEntry::AutoScopeNativeCallWrapperNoStackCheck( |
| 202 | Dart_NativeArguments args, |
| 203 | Dart_NativeFunction func) { |
| 204 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 205 | // Tell MemorySanitizer 'arguments' is initialized by generated code. |
| 206 | MSAN_UNPOISON(arguments, sizeof(*arguments)); |
| 207 | Thread* thread = arguments->thread(); |
| 208 | ASSERT(thread->execution_state() == Thread::kThreadInGenerated); |
| 209 | { |
| 210 | Isolate* isolate = thread->isolate(); |
| 211 | ApiState* state = isolate->group()->api_state(); |
| 212 | ASSERT(state != NULL); |
| 213 | TRACE_NATIVE_CALL("0x%" Px "" , reinterpret_cast<uintptr_t>(func)); |
| 214 | thread->EnterApiScope(); |
| 215 | { |
| 216 | TransitionGeneratedToNative transition(thread); |
| 217 | func(args); |
| 218 | if (ReturnValueIsError(arguments)) { |
| 219 | PropagateErrors(arguments); |
| 220 | } |
| 221 | } |
| 222 | thread->ExitApiScope(); |
| 223 | DEOPTIMIZE_ALOT; |
| 224 | } |
| 225 | ASSERT(thread->execution_state() == Thread::kThreadInGenerated); |
| 226 | } |
| 227 | |
| 228 | static NativeFunction ResolveNativeFunction(Zone* zone, |
| 229 | const Function& func, |
| 230 | bool* is_bootstrap_native, |
| 231 | bool* is_auto_scope) { |
| 232 | const Class& cls = Class::Handle(zone, func.Owner()); |
| 233 | const Library& library = Library::Handle(zone, cls.library()); |
| 234 | |
| 235 | *is_bootstrap_native = |
| 236 | Bootstrap::IsBootstrapResolver(library.native_entry_resolver()); |
| 237 | |
| 238 | const String& native_name = String::Handle(zone, func.native_name()); |
| 239 | ASSERT(!native_name.IsNull()); |
| 240 | |
| 241 | const int num_params = NativeArguments::ParameterCountForResolution(func); |
| 242 | NativeFunction native_function = NativeEntry::ResolveNative( |
| 243 | library, native_name, num_params, is_auto_scope); |
| 244 | if (native_function == NULL) { |
| 245 | FATAL2("Failed to resolve native function '%s' in '%s'\n" , |
| 246 | native_name.ToCString(), func.ToQualifiedCString()); |
| 247 | } |
| 248 | return native_function; |
| 249 | } |
| 250 | |
| 251 | uword NativeEntry::LinkNativeCallEntry() { |
| 252 | uword entry = reinterpret_cast<uword>(NativeEntry::LinkNativeCall); |
| 253 | return entry; |
| 254 | } |
| 255 | |
| 256 | void NativeEntry::LinkNativeCall(Dart_NativeArguments args) { |
| 257 | CHECK_STACK_ALIGNMENT; |
| 258 | NativeArguments* arguments = reinterpret_cast<NativeArguments*>(args); |
| 259 | // Tell MemorySanitizer 'arguments' is initialized by generated code. |
| 260 | MSAN_UNPOISON(arguments, sizeof(*arguments)); |
| 261 | TRACE_NATIVE_CALL("%s" , "LinkNative" ); |
| 262 | |
| 263 | NativeFunction target_function = NULL; |
| 264 | bool is_bootstrap_native = false; |
| 265 | bool is_auto_scope = true; |
| 266 | |
| 267 | { |
| 268 | TransitionGeneratedToVM transition(arguments->thread()); |
| 269 | StackZone stack_zone(arguments->thread()); |
| 270 | Zone* zone = stack_zone.GetZone(); |
| 271 | |
| 272 | DartFrameIterator iterator(arguments->thread(), |
| 273 | StackFrameIterator::kNoCrossThreadIteration); |
| 274 | StackFrame* caller_frame = iterator.NextFrame(); |
| 275 | |
| 276 | Code& code = Code::Handle(zone); |
| 277 | Bytecode& bytecode = Bytecode::Handle(zone); |
| 278 | Function& func = Function::Handle(zone); |
| 279 | if (caller_frame->is_interpreted()) { |
| 280 | bytecode = caller_frame->LookupDartBytecode(); |
| 281 | func = bytecode.function(); |
| 282 | } else { |
| 283 | code = caller_frame->LookupDartCode(); |
| 284 | func = code.function(); |
| 285 | } |
| 286 | |
| 287 | if (FLAG_trace_natives) { |
| 288 | THR_Print("Resolving native target for %s\n" , func.ToCString()); |
| 289 | } |
| 290 | |
| 291 | target_function = |
| 292 | ResolveNativeFunction(arguments->thread()->zone(), func, |
| 293 | &is_bootstrap_native, &is_auto_scope); |
| 294 | ASSERT(target_function != NULL); |
| 295 | |
| 296 | #if defined(DEBUG) |
| 297 | NativeFunction current_function = NULL; |
| 298 | if (caller_frame->is_interpreted()) { |
| 299 | #if !defined(DART_PRECOMPILED_RUNTIME) |
| 300 | ASSERT(FLAG_enable_interpreter); |
| 301 | NativeFunctionWrapper current_trampoline = KBCPatcher::GetNativeCallAt( |
| 302 | caller_frame->pc(), bytecode, ¤t_function); |
| 303 | ASSERT(current_function == |
| 304 | reinterpret_cast<NativeFunction>(LinkNativeCall)); |
| 305 | ASSERT(current_trampoline == &BootstrapNativeCallWrapper || |
| 306 | current_trampoline == &AutoScopeNativeCallWrapper || |
| 307 | current_trampoline == &NoScopeNativeCallWrapper); |
| 308 | #else |
| 309 | UNREACHABLE(); |
| 310 | #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| 311 | } else { |
| 312 | const Code& current_trampoline = |
| 313 | Code::Handle(zone, CodePatcher::GetNativeCallAt( |
| 314 | caller_frame->pc(), code, ¤t_function)); |
| 315 | // Some other isolate(with code being shared in AOT) might have updated |
| 316 | // target function/trampoline already. |
| 317 | ASSERT(current_function == |
| 318 | reinterpret_cast<NativeFunction>(LinkNativeCall) || |
| 319 | current_function == target_function); |
| 320 | ASSERT(current_trampoline.raw() == |
| 321 | StubCode::CallBootstrapNative().raw() || |
| 322 | current_function == target_function); |
| 323 | } |
| 324 | #endif |
| 325 | |
| 326 | NativeFunction patch_target_function = target_function; |
| 327 | if (caller_frame->is_interpreted()) { |
| 328 | #if !defined(DART_PRECOMPILED_RUNTIME) |
| 329 | ASSERT(FLAG_enable_interpreter); |
| 330 | NativeFunctionWrapper trampoline; |
| 331 | if (is_bootstrap_native) { |
| 332 | trampoline = &BootstrapNativeCallWrapper; |
| 333 | } else if (is_auto_scope) { |
| 334 | trampoline = &AutoScopeNativeCallWrapper; |
| 335 | } else { |
| 336 | trampoline = &NoScopeNativeCallWrapper; |
| 337 | } |
| 338 | KBCPatcher::PatchNativeCallAt(caller_frame->pc(), bytecode, |
| 339 | patch_target_function, trampoline); |
| 340 | #else |
| 341 | UNREACHABLE(); |
| 342 | #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| 343 | } else { |
| 344 | Code& trampoline = Code::Handle(zone); |
| 345 | if (is_bootstrap_native) { |
| 346 | trampoline = StubCode::CallBootstrapNative().raw(); |
| 347 | } else if (is_auto_scope) { |
| 348 | trampoline = StubCode::CallAutoScopeNative().raw(); |
| 349 | } else { |
| 350 | trampoline = StubCode::CallNoScopeNative().raw(); |
| 351 | } |
| 352 | CodePatcher::PatchNativeCallAt(caller_frame->pc(), code, |
| 353 | patch_target_function, trampoline); |
| 354 | } |
| 355 | |
| 356 | if (FLAG_trace_natives) { |
| 357 | THR_Print(" -> %p (%s)\n" , target_function, |
| 358 | is_bootstrap_native ? "bootstrap" : "non-bootstrap" ); |
| 359 | } |
| 360 | } |
| 361 | |
| 362 | // Tail-call resolved target. |
| 363 | if (is_bootstrap_native) { |
| 364 | NativeEntry::BootstrapNativeCallWrapper( |
| 365 | args, reinterpret_cast<Dart_NativeFunction>(target_function)); |
| 366 | } else if (is_auto_scope) { |
| 367 | // Because this call is within a compilation unit, Clang doesn't respect |
| 368 | // the ABI alignment here. |
| 369 | NativeEntry::AutoScopeNativeCallWrapperNoStackCheck( |
| 370 | args, reinterpret_cast<Dart_NativeFunction>(target_function)); |
| 371 | } else { |
| 372 | // Because this call is within a compilation unit, Clang doesn't respect |
| 373 | // the ABI alignment here. |
| 374 | NativeEntry::NoScopeNativeCallWrapperNoStackCheck( |
| 375 | args, reinterpret_cast<Dart_NativeFunction>(target_function)); |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | #if !defined(DART_PRECOMPILED_RUNTIME) |
| 380 | |
| 381 | // Note: not GC safe. Use with care. |
| 382 | NativeEntryData::Payload* NativeEntryData::FromTypedArray(TypedDataPtr data) { |
| 383 | return reinterpret_cast<Payload*>(data->ptr()->data()); |
| 384 | } |
| 385 | |
| 386 | MethodRecognizer::Kind NativeEntryData::kind() const { |
| 387 | return FromTypedArray(data_.raw())->kind; |
| 388 | } |
| 389 | |
| 390 | void NativeEntryData::set_kind(MethodRecognizer::Kind value) const { |
| 391 | FromTypedArray(data_.raw())->kind = value; |
| 392 | } |
| 393 | |
| 394 | MethodRecognizer::Kind NativeEntryData::GetKind(TypedDataPtr data) { |
| 395 | return FromTypedArray(data)->kind; |
| 396 | } |
| 397 | |
| 398 | NativeFunctionWrapper NativeEntryData::trampoline() const { |
| 399 | return FromTypedArray(data_.raw())->trampoline; |
| 400 | } |
| 401 | |
| 402 | void NativeEntryData::set_trampoline(NativeFunctionWrapper value) const { |
| 403 | FromTypedArray(data_.raw())->trampoline = value; |
| 404 | } |
| 405 | |
| 406 | NativeFunctionWrapper NativeEntryData::GetTrampoline(TypedDataPtr data) { |
| 407 | return FromTypedArray(data)->trampoline; |
| 408 | } |
| 409 | |
| 410 | NativeFunction NativeEntryData::native_function() const { |
| 411 | return FromTypedArray(data_.raw())->native_function; |
| 412 | } |
| 413 | |
| 414 | void NativeEntryData::set_native_function(NativeFunction value) const { |
| 415 | FromTypedArray(data_.raw())->native_function = value; |
| 416 | } |
| 417 | |
| 418 | NativeFunction NativeEntryData::GetNativeFunction(TypedDataPtr data) { |
| 419 | return FromTypedArray(data)->native_function; |
| 420 | } |
| 421 | |
| 422 | intptr_t NativeEntryData::argc_tag() const { |
| 423 | return FromTypedArray(data_.raw())->argc_tag; |
| 424 | } |
| 425 | |
| 426 | void NativeEntryData::set_argc_tag(intptr_t value) const { |
| 427 | FromTypedArray(data_.raw())->argc_tag = value; |
| 428 | } |
| 429 | |
| 430 | intptr_t NativeEntryData::GetArgcTag(TypedDataPtr data) { |
| 431 | return FromTypedArray(data)->argc_tag; |
| 432 | } |
| 433 | |
| 434 | TypedDataPtr NativeEntryData::New(MethodRecognizer::Kind kind, |
| 435 | NativeFunctionWrapper trampoline, |
| 436 | NativeFunction native_function, |
| 437 | intptr_t argc_tag) { |
| 438 | const TypedData& data = TypedData::Handle( |
| 439 | TypedData::New(kTypedDataUint8ArrayCid, sizeof(Payload), Heap::kOld)); |
| 440 | NativeEntryData native_entry(data); |
| 441 | native_entry.set_kind(kind); |
| 442 | native_entry.set_trampoline(trampoline); |
| 443 | native_entry.set_native_function(native_function); |
| 444 | native_entry.set_argc_tag(argc_tag); |
| 445 | return data.raw(); |
| 446 | } |
| 447 | |
| 448 | #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| 449 | |
| 450 | } // namespace dart |
| 451 | |