| 1 | // Copyright (c) 2012, 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 | #ifndef RUNTIME_VM_NATIVE_ARGUMENTS_H_ |
| 6 | #define RUNTIME_VM_NATIVE_ARGUMENTS_H_ |
| 7 | |
| 8 | #include "platform/assert.h" |
| 9 | #include "platform/memory_sanitizer.h" |
| 10 | #include "vm/globals.h" |
| 11 | #include "vm/simulator.h" |
| 12 | #include "vm/stub_code.h" |
| 13 | |
| 14 | namespace dart { |
| 15 | |
| 16 | // Forward declarations. |
| 17 | class BootstrapNatives; |
| 18 | class Object; |
| 19 | class Simulator; |
| 20 | class Thread; |
| 21 | |
| 22 | #if defined(TESTING) || defined(DEBUG) |
| 23 | |
| 24 | #if defined(USING_SIMULATOR) |
| 25 | #define CHECK_STACK_ALIGNMENT \ |
| 26 | { \ |
| 27 | uword current_sp = Simulator::Current()->get_register(SPREG); \ |
| 28 | ASSERT(Utils::IsAligned(current_sp, OS::ActivationFrameAlignment())); \ |
| 29 | } |
| 30 | #elif defined(HOST_OS_WINDOWS) |
| 31 | // The compiler may dynamically align the stack on Windows, so do not check. |
| 32 | #define CHECK_STACK_ALIGNMENT \ |
| 33 | {} |
| 34 | #else |
| 35 | #define CHECK_STACK_ALIGNMENT \ |
| 36 | { \ |
| 37 | uword (*func)() = reinterpret_cast<uword (*)()>( \ |
| 38 | StubCode::GetCStackPointer().EntryPoint()); \ |
| 39 | uword current_sp = func(); \ |
| 40 | ASSERT(Utils::IsAligned(current_sp, OS::ActivationFrameAlignment())); \ |
| 41 | } |
| 42 | #endif |
| 43 | |
| 44 | void VerifyOnTransition(); |
| 45 | |
| 46 | #define DEOPTIMIZE_ALOT \ |
| 47 | if (FLAG_deoptimize_alot) { \ |
| 48 | DeoptimizeFunctionsOnStack(); \ |
| 49 | } |
| 50 | |
| 51 | #else |
| 52 | |
| 53 | #define CHECK_STACK_ALIGNMENT \ |
| 54 | {} |
| 55 | #define DEOPTIMIZE_ALOT \ |
| 56 | {} |
| 57 | |
| 58 | #endif |
| 59 | |
| 60 | // Class NativeArguments is used to access arguments passed in from |
| 61 | // generated dart code to a runtime function or a dart library native |
| 62 | // function. It is also used to set the return value if any at the slot |
| 63 | // reserved for return values. |
| 64 | // All runtime function/dart library native functions have the |
| 65 | // following signature: |
| 66 | // void function_name(NativeArguments arguments); |
| 67 | // Inside the function, arguments are accessed as follows: |
| 68 | // const Instance& arg0 = Instance::CheckedHandle(arguments.NativeArgAt(0)); |
| 69 | // const Smi& arg1 = Smi::CheckedHandle(arguments.NativeArgAt(1)); |
| 70 | // If the function is generic, type arguments are accessed as follows: |
| 71 | // const TypeArguments& type_args = |
| 72 | // TypeArguments::Handle(arguments.NativeTypeArgs()); |
| 73 | // The return value is set as follows: |
| 74 | // arguments.SetReturn(result); |
| 75 | // NOTE: Since we pass 'this' as a pass-by-value argument in the stubs we don't |
| 76 | // have DISALLOW_COPY_AND_ASSIGN in the class definition and do not make it a |
| 77 | // subclass of ValueObject. |
| 78 | class NativeArguments { |
| 79 | public: |
| 80 | Thread* thread() const { return thread_; } |
| 81 | |
| 82 | // Includes type arguments vector. |
| 83 | int ArgCount() const { return ArgcBits::decode(argc_tag_); } |
| 84 | |
| 85 | ObjectPtr ArgAt(int index) const { |
| 86 | ASSERT((index >= 0) && (index < ArgCount())); |
| 87 | ObjectPtr* arg_ptr = |
| 88 | &(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]); |
| 89 | // Tell MemorySanitizer the ObjectPtr was initialized (by generated code). |
| 90 | MSAN_UNPOISON(arg_ptr, kWordSize); |
| 91 | return *arg_ptr; |
| 92 | } |
| 93 | |
| 94 | void SetArgAt(int index, const Object& value) const { |
| 95 | ASSERT(thread_->execution_state() == Thread::kThreadInVM); |
| 96 | ASSERT((index >= 0) && (index < ArgCount())); |
| 97 | ObjectPtr* arg_ptr = |
| 98 | &(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]); |
| 99 | *arg_ptr = value.raw(); |
| 100 | } |
| 101 | |
| 102 | // Does not include hidden type arguments vector. |
| 103 | int NativeArgCount() const { |
| 104 | int function_bits = FunctionBits::decode(argc_tag_); |
| 105 | return ArgCount() - NumHiddenArgs(function_bits); |
| 106 | } |
| 107 | |
| 108 | ObjectPtr NativeArg0() const { |
| 109 | int function_bits = FunctionBits::decode(argc_tag_); |
| 110 | if ((function_bits & (kClosureFunctionBit | kInstanceFunctionBit)) == |
| 111 | (kClosureFunctionBit | kInstanceFunctionBit)) { |
| 112 | // Retrieve the receiver from the context. |
| 113 | const int closure_index = |
| 114 | (function_bits & kGenericFunctionBit) != 0 ? 1 : 0; |
| 115 | const Object& closure = Object::Handle(ArgAt(closure_index)); |
| 116 | const Context& context = |
| 117 | Context::Handle(Closure::Cast(closure).context()); |
| 118 | return context.At(0); |
| 119 | } |
| 120 | return ArgAt(NumHiddenArgs(function_bits)); |
| 121 | } |
| 122 | |
| 123 | ObjectPtr NativeArgAt(int index) const { |
| 124 | ASSERT((index >= 0) && (index < NativeArgCount())); |
| 125 | if (index == 0) { |
| 126 | return NativeArg0(); |
| 127 | } |
| 128 | int function_bits = FunctionBits::decode(argc_tag_); |
| 129 | const int actual_index = index + NumHiddenArgs(function_bits); |
| 130 | return ArgAt(actual_index); |
| 131 | } |
| 132 | |
| 133 | TypeArgumentsPtr NativeTypeArgs() const { |
| 134 | ASSERT(ToGenericFunction()); |
| 135 | return TypeArguments::RawCast(ArgAt(0)); |
| 136 | } |
| 137 | |
| 138 | int NativeTypeArgCount() const { |
| 139 | if (ToGenericFunction()) { |
| 140 | TypeArguments& type_args = TypeArguments::Handle(NativeTypeArgs()); |
| 141 | if (type_args.IsNull()) { |
| 142 | // null vector represents infinite list of dynamics |
| 143 | return INT_MAX; |
| 144 | } |
| 145 | return type_args.Length(); |
| 146 | } |
| 147 | return 0; |
| 148 | } |
| 149 | |
| 150 | AbstractTypePtr NativeTypeArgAt(int index) const { |
| 151 | ASSERT((index >= 0) && (index < NativeTypeArgCount())); |
| 152 | TypeArguments& type_args = TypeArguments::Handle(NativeTypeArgs()); |
| 153 | if (type_args.IsNull()) { |
| 154 | // null vector represents infinite list of dynamics |
| 155 | return Type::dynamic_type().raw(); |
| 156 | } |
| 157 | return type_args.TypeAt(index); |
| 158 | } |
| 159 | |
| 160 | void SetReturn(const Object& value) const { |
| 161 | ASSERT(thread_->execution_state() == Thread::kThreadInVM); |
| 162 | *retval_ = value.raw(); |
| 163 | } |
| 164 | |
| 165 | ObjectPtr ReturnValue() const { |
| 166 | // Tell MemorySanitizer the retval_ was initialized (by generated code). |
| 167 | MSAN_UNPOISON(retval_, kWordSize); |
| 168 | return *retval_; |
| 169 | } |
| 170 | |
| 171 | static intptr_t thread_offset() { |
| 172 | return OFFSET_OF(NativeArguments, thread_); |
| 173 | } |
| 174 | static intptr_t argc_tag_offset() { |
| 175 | return OFFSET_OF(NativeArguments, argc_tag_); |
| 176 | } |
| 177 | static intptr_t argv_offset() { return OFFSET_OF(NativeArguments, argv_); } |
| 178 | static intptr_t retval_offset() { |
| 179 | return OFFSET_OF(NativeArguments, retval_); |
| 180 | } |
| 181 | |
| 182 | static intptr_t ParameterCountForResolution(const Function& function) { |
| 183 | ASSERT(function.is_native()); |
| 184 | ASSERT(!function.IsGenerativeConstructor()); // Not supported. |
| 185 | intptr_t count = function.NumParameters(); |
| 186 | if (function.is_static() && function.IsClosureFunction()) { |
| 187 | // The closure object is hidden and not accessible from native code. |
| 188 | // However, if the function is an instance closure function, the captured |
| 189 | // receiver located in the context is made accessible in native code at |
| 190 | // index 0, thereby hiding the closure object at index 0. |
| 191 | count--; |
| 192 | } |
| 193 | return count; |
| 194 | } |
| 195 | |
| 196 | static int ComputeArgcTag(const Function& function) { |
| 197 | ASSERT(function.is_native()); |
| 198 | ASSERT(!function.IsGenerativeConstructor()); // Not supported. |
| 199 | int argc = function.NumParameters(); |
| 200 | int function_bits = 0; |
| 201 | if (!function.is_static()) { |
| 202 | function_bits |= kInstanceFunctionBit; |
| 203 | } |
| 204 | if (function.IsClosureFunction()) { |
| 205 | function_bits |= kClosureFunctionBit; |
| 206 | } |
| 207 | if (function.IsGeneric()) { |
| 208 | function_bits |= kGenericFunctionBit; |
| 209 | argc++; |
| 210 | } |
| 211 | int tag = ArgcBits::encode(argc); |
| 212 | tag = FunctionBits::update(function_bits, tag); |
| 213 | return tag; |
| 214 | } |
| 215 | |
| 216 | private: |
| 217 | enum { |
| 218 | kInstanceFunctionBit = 1, |
| 219 | kClosureFunctionBit = 2, |
| 220 | kGenericFunctionBit = 4, |
| 221 | }; |
| 222 | enum ArgcTagBits { |
| 223 | kArgcBit = 0, |
| 224 | kArgcSize = 24, |
| 225 | kFunctionBit = kArgcBit + kArgcSize, |
| 226 | kFunctionSize = 3, |
| 227 | kReverseArgOrderBit = kFunctionBit + kFunctionSize, |
| 228 | kReverseArgOrderSize = 1, |
| 229 | }; |
| 230 | class ArgcBits : public BitField<intptr_t, int32_t, kArgcBit, kArgcSize> {}; |
| 231 | class FunctionBits |
| 232 | : public BitField<intptr_t, int, kFunctionBit, kFunctionSize> {}; |
| 233 | class ReverseArgOrderBit |
| 234 | : public BitField<intptr_t, bool, kReverseArgOrderBit, 1> {}; |
| 235 | friend class Api; |
| 236 | friend class NativeEntry; |
| 237 | friend class Interpreter; |
| 238 | friend class Simulator; |
| 239 | |
| 240 | // Allow simulator and interpreter to create NativeArguments in reverse order |
| 241 | // on the stack. |
| 242 | NativeArguments(Thread* thread, |
| 243 | int argc_tag, |
| 244 | ObjectPtr* argv, |
| 245 | ObjectPtr* retval) |
| 246 | : thread_(thread), |
| 247 | argc_tag_(ReverseArgOrderBit::update(true, argc_tag)), |
| 248 | argv_(argv), |
| 249 | retval_(retval) {} |
| 250 | |
| 251 | // Since this function is passed a RawObject directly, we need to be |
| 252 | // exceedingly careful when we use it. If there are any other side |
| 253 | // effects in the statement that may cause GC, it could lead to |
| 254 | // bugs. |
| 255 | void SetReturnUnsafe(ObjectPtr value) const { |
| 256 | ASSERT(thread_->execution_state() == Thread::kThreadInVM); |
| 257 | *retval_ = value; |
| 258 | } |
| 259 | |
| 260 | // Returns true if the arguments are those of an instance function call. |
| 261 | bool ToInstanceFunction() const { |
| 262 | return (FunctionBits::decode(argc_tag_) & kInstanceFunctionBit) != 0; |
| 263 | } |
| 264 | |
| 265 | // Returns true if the arguments are those of a closure function call. |
| 266 | bool ToClosureFunction() const { |
| 267 | return (FunctionBits::decode(argc_tag_) & kClosureFunctionBit) != 0; |
| 268 | } |
| 269 | |
| 270 | // Returns true if the arguments are those of a generic function call. |
| 271 | bool ToGenericFunction() const { |
| 272 | return (FunctionBits::decode(argc_tag_) & kGenericFunctionBit) != 0; |
| 273 | } |
| 274 | |
| 275 | int NumHiddenArgs(int function_bits) const { |
| 276 | int num_hidden_args = 0; |
| 277 | // For static closure functions, the closure at index 0 is hidden. |
| 278 | // In the instance closure function case, the receiver is accessed from |
| 279 | // the context and the closure at index 0 is hidden, so the apparent |
| 280 | // argument count remains unchanged. |
| 281 | if ((function_bits & kClosureFunctionBit) == kClosureFunctionBit) { |
| 282 | num_hidden_args++; |
| 283 | } |
| 284 | if ((function_bits & kGenericFunctionBit) == kGenericFunctionBit) { |
| 285 | num_hidden_args++; |
| 286 | } |
| 287 | return num_hidden_args; |
| 288 | } |
| 289 | |
| 290 | Thread* thread_; // Current thread pointer. |
| 291 | intptr_t argc_tag_; // Encodes argument count and invoked native call type. |
| 292 | ObjectPtr* argv_; // Pointer to an array of arguments to runtime call. |
| 293 | ObjectPtr* retval_; // Pointer to the return value area. |
| 294 | }; |
| 295 | |
| 296 | } // namespace dart |
| 297 | |
| 298 | #endif // RUNTIME_VM_NATIVE_ARGUMENTS_H_ |
| 299 | |