// Copyright (c) 2012, 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. #ifndef RUNTIME_VM_NATIVE_ARGUMENTS_H_ #define RUNTIME_VM_NATIVE_ARGUMENTS_H_ #include "platform/assert.h" #include "platform/memory_sanitizer.h" #include "vm/globals.h" #include "vm/simulator.h" #include "vm/stub_code.h" namespace dart { // Forward declarations. class BootstrapNatives; class Object; class Simulator; class Thread; #if defined(TESTING) || defined(DEBUG) #if defined(DART_INCLUDE_SIMULATOR) // The simulator/isolate might not exist yet for an async FFI callback, so do // not check. An equivalent check exists in the simulator itself. #define CHECK_STACK_ALIGNMENT \ { \ } #elif defined(DART_HOST_OS_WINDOWS) // The compiler may dynamically align the stack on Windows, so do not check. #define CHECK_STACK_ALIGNMENT \ { \ } #else #define CHECK_STACK_ALIGNMENT \ { \ ASSERT(Utils::IsAligned(OSThread::GetCurrentStackPointer(), \ OS::ActivationFrameAlignment())); \ } #endif #define DEOPTIMIZE_ALOT \ if (FLAG_deoptimize_alot) { \ DeoptimizeFunctionsOnStack(); \ } #else #define CHECK_STACK_ALIGNMENT \ { \ } #define DEOPTIMIZE_ALOT \ { \ } #endif // Class NativeArguments is used to access arguments passed in from // generated dart code to a runtime function or a dart library native // function. It is also used to set the return value if any at the slot // reserved for return values. // All runtime function/dart library native functions have the // following signature: // void function_name(NativeArguments arguments); // Inside the function, arguments are accessed as follows: // const Instance& arg0 = Instance::CheckedHandle(arguments.NativeArgAt(0)); // const Smi& arg1 = Smi::CheckedHandle(arguments.NativeArgAt(1)); // If the function is generic, type arguments are accessed as follows: // const TypeArguments& type_args = // TypeArguments::Handle(arguments.NativeTypeArgs()); // The return value is set as follows: // arguments.SetReturn(result); // NOTE: Since we pass 'this' as a pass-by-value argument in the stubs we don't // have DISALLOW_COPY_AND_ASSIGN in the class definition and do not make it a // subclass of ValueObject. class NativeArguments { private: using ArgcBits = BitField; using GenericFunctionBit = BitField; using ReverseArgOrderBit = BitField; public: Thread* thread() const { return thread_; } // Includes type arguments vector. int ArgCount() const { return ArgcBits::decode(argc_tag_); } ObjectPtr ArgAt(int index) const { ASSERT((index >= 0) && (index < ArgCount())); ObjectPtr* arg_ptr = &(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]); // Tell MemorySanitizer the ObjectPtr was initialized (by generated code). MSAN_UNPOISON(arg_ptr, kWordSize); return *arg_ptr; } void SetArgAt(int index, const Object& value) const { ASSERT(thread_->execution_state() == Thread::kThreadInVM); ASSERT((index >= 0) && (index < ArgCount())); argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index] = value.ptr(); } // Does not include hidden type arguments vector. int NativeArgCount() const { return ArgCount() - NumHiddenArgs(); } ObjectPtr NativeArg0() const { return ArgAt(NumHiddenArgs()); } ObjectPtr NativeArgAt(int index) const { ASSERT((index >= 0) && (index < NativeArgCount())); return ArgAt(index + NumHiddenArgs()); } TypeArgumentsPtr NativeTypeArgs() const { ASSERT(ToGenericFunction()); return TypeArguments::RawCast(ArgAt(0)); } int NativeTypeArgCount() const { if (ToGenericFunction()) { TypeArguments& type_args = TypeArguments::Handle(NativeTypeArgs()); if (type_args.IsNull()) { // null vector represents infinite list of dynamics return INT_MAX; } return type_args.Length(); } return 0; } AbstractTypePtr NativeTypeArgAt(int index) const { ASSERT((index >= 0) && (index < NativeTypeArgCount())); TypeArguments& type_args = TypeArguments::Handle(NativeTypeArgs()); if (type_args.IsNull()) { // null vector represents infinite list of dynamics return Type::dynamic_type().ptr(); } return type_args.TypeAt(index); } void SetReturn(const Object& value) const { ASSERT(thread_->execution_state() == Thread::kThreadInVM); *retval_ = value.ptr(); } ObjectPtr ReturnValue() const { // Tell MemorySanitizer the retval_ was initialized (by generated code). MSAN_UNPOISON(retval_, kWordSize); return *retval_; } uword GetCallerSP() const { return reinterpret_cast( retval_ + (ReverseArgOrderBit::decode(argc_tag_) ? 0 : 1)); } static intptr_t thread_offset() { return OFFSET_OF(NativeArguments, thread_); } static intptr_t argc_tag_offset() { return OFFSET_OF(NativeArguments, argc_tag_); } static intptr_t argv_offset() { return OFFSET_OF(NativeArguments, argv_); } static intptr_t retval_offset() { return OFFSET_OF(NativeArguments, retval_); } static intptr_t ParameterCountForResolution(const Function& function) { ASSERT(function.is_old_native()); ASSERT(!function.IsGenerativeConstructor()); // Not supported. ASSERT(!function.IsClosureFunction()); // Not supported. return function.NumParameters(); } static int ComputeArgcTag(const Function& function) { ASSERT(function.is_old_native()); ASSERT(!function.IsGenerativeConstructor()); // Not supported. ASSERT(!function.IsClosureFunction()); // Not supported. bool is_generic = function.IsGeneric(); return ArgcBits::encode(function.NumParameters() + (is_generic ? 1 : 0)) | GenericFunctionBit::encode(is_generic); } private: friend class Api; friend class Interpreter; friend class NativeEntry; friend class Simulator; #if defined(DART_BYTECODE_INTERPRETER) NativeArguments(Thread* thread, int argc_tag, ObjectPtr* argv, ObjectPtr* retval) : thread_(thread), argc_tag_(ReverseArgOrderBit::update(true, argc_tag)), argv_(argv), retval_(retval) {} NativeArguments() = default; #endif // defined(DART_BYTECODE_INTERPRETER) // Since this function is passed an ObjectPtr directly, we need to be // exceedingly careful when we use it. If there are any other side // effects in the statement that may cause GC, it could lead to // bugs. void SetReturnUnsafe(ObjectPtr value) const { ASSERT(thread_->execution_state() == Thread::kThreadInVM); *retval_ = value; } // Returns true if the arguments are those of a generic function call. bool ToGenericFunction() const { return GenericFunctionBit::decode(argc_tag_); } int NumHiddenArgs() const { return GenericFunctionBit::decode(argc_tag_) ? 1 : 0; } Thread* thread_; // Current thread pointer. intptr_t argc_tag_; // Encodes argument count and invoked native call type. ObjectPtr* argv_; // Pointer to an array of arguments to runtime call. ObjectPtr* retval_; // Pointer to the return value area. }; } // namespace dart #endif // RUNTIME_VM_NATIVE_ARGUMENTS_H_