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