| // 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/dart_entry.h" |
| |
| #include "platform/safe_stack.h" |
| #include "vm/class_finalizer.h" |
| #include "vm/debugger.h" |
| #include "vm/dispatch_table.h" |
| #include "vm/heap/safepoint.h" |
| #include "vm/interpreter.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| #include "vm/runtime_entry.h" |
| #include "vm/simulator.h" |
| #include "vm/stub_code.h" |
| #include "vm/symbols.h" |
| #include "vm/zone_text_buffer.h" |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| #include "vm/compiler/frontend/bytecode_reader.h" |
| #include "vm/compiler/jit/compiler.h" |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| namespace dart { |
| |
| DECLARE_FLAG(bool, enable_interpreter); |
| DECLARE_FLAG(bool, precompiled_mode); |
| |
| // A cache of VM heap allocated arguments descriptors. |
| ArrayPtr ArgumentsDescriptor::cached_args_descriptors_[kCachedDescriptorCount]; |
| |
| ObjectPtr DartEntry::InvokeFunction(const Function& function, |
| const Array& arguments) { |
| ASSERT(Thread::Current()->IsMutatorThread()); |
| const int kTypeArgsLen = 0; // No support to pass type args to generic func. |
| const Array& arguments_descriptor = Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, arguments.Length())); |
| return InvokeFunction(function, arguments, arguments_descriptor); |
| } |
| |
| class ScopedIsolateStackLimits : public ValueObject { |
| public: |
| NO_SANITIZE_SAFE_STACK |
| explicit ScopedIsolateStackLimits(Thread* thread, uword current_sp) |
| : thread_(thread) { |
| ASSERT(thread != NULL); |
| // Save the Thread's current stack limit and adjust the stack limit. |
| ASSERT(thread->isolate() == Isolate::Current()); |
| saved_stack_limit_ = thread->saved_stack_limit(); |
| #if defined(USING_SIMULATOR) |
| thread->SetStackLimit(Simulator::Current()->overflow_stack_limit()); |
| #else |
| thread->SetStackLimit(OSThread::Current()->overflow_stack_limit()); |
| // TODO(regis): For now, the interpreter is using its own stack limit. |
| #endif |
| |
| #if defined(USING_SAFE_STACK) |
| saved_safestack_limit_ = OSThread::GetCurrentSafestackPointer(); |
| thread->set_saved_safestack_limit(saved_safestack_limit_); |
| #endif |
| } |
| |
| ~ScopedIsolateStackLimits() { |
| ASSERT(thread_->isolate() == Isolate::Current()); |
| // Since we started with a stack limit of 0 we should be getting back |
| // to a stack limit of 0 when all nested invocations are done and |
| // we have bottomed out. |
| thread_->SetStackLimit(saved_stack_limit_); |
| #if defined(USING_SAFE_STACK) |
| thread_->set_saved_safestack_limit(saved_safestack_limit_); |
| #endif |
| } |
| |
| private: |
| Thread* thread_; |
| #if defined(USING_SAFE_STACK) |
| uword saved_safestack_limit_ = 0; |
| #endif |
| uword saved_stack_limit_ = 0; |
| }; |
| |
| // Clears/restores Thread::long_jump_base on construction/destruction. |
| // Ensures that we do not attempt to long jump across Dart frames. |
| class SuspendLongJumpScope : public ThreadStackResource { |
| public: |
| explicit SuspendLongJumpScope(Thread* thread) |
| : ThreadStackResource(thread), |
| saved_long_jump_base_(thread->long_jump_base()) { |
| thread->set_long_jump_base(NULL); |
| } |
| |
| ~SuspendLongJumpScope() { |
| ASSERT(thread()->long_jump_base() == NULL); |
| thread()->set_long_jump_base(saved_long_jump_base_); |
| } |
| |
| private: |
| LongJumpScope* saved_long_jump_base_; |
| }; |
| |
| ObjectPtr DartEntry::InvokeFunction(const Function& function, |
| const Array& arguments, |
| const Array& arguments_descriptor, |
| uword current_sp) { |
| // We use a kernel2kernel constant evaluator in Dart 2.0 AOT compilation |
| // and never start the VM service isolate. So we should never end up invoking |
| // any dart code in the Dart 2.0 AOT compiler. |
| if (FLAG_precompiled_mode) { |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| UNREACHABLE(); |
| #else |
| if (FLAG_use_bare_instructions) { |
| Thread* thread = Thread::Current(); |
| thread->set_global_object_pool( |
| thread->isolate()->object_store()->global_object_pool()); |
| const DispatchTable* dispatch_table = thread->isolate()->dispatch_table(); |
| if (dispatch_table != nullptr) { |
| thread->set_dispatch_table_array(dispatch_table->ArrayOrigin()); |
| } |
| ASSERT(thread->global_object_pool() != Object::null()); |
| } |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| } |
| |
| ASSERT(!function.IsNull()); |
| |
| // Get the entrypoint corresponding to the function specified, this |
| // will result in a compilation of the function if it is not already |
| // compiled. |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| ASSERT(thread->IsMutatorThread()); |
| ScopedIsolateStackLimits stack_limit(thread, current_sp); |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| if (!function.HasCode()) { |
| if (FLAG_enable_interpreter && function.IsBytecodeAllowed(zone)) { |
| if (!function.HasBytecode()) { |
| ErrorPtr error = |
| kernel::BytecodeReader::ReadFunctionBytecode(thread, function); |
| if (error != Error::null()) { |
| return error; |
| } |
| } |
| |
| // If we have bytecode but no native code then invoke the interpreter. |
| if (function.HasBytecode() && (FLAG_compilation_counter_threshold != 0)) { |
| ASSERT(thread->no_callback_scope_depth() == 0); |
| SuspendLongJumpScope suspend_long_jump_scope(thread); |
| TransitionToGenerated transition(thread); |
| return Interpreter::Current()->Call(function, arguments_descriptor, |
| arguments, thread); |
| } |
| |
| // Fall back to compilation. |
| } |
| |
| const Object& result = |
| Object::Handle(zone, Compiler::CompileFunction(thread, function)); |
| if (result.IsError()) { |
| return Error::Cast(result).raw(); |
| } |
| |
| // At this point we should have native code. |
| ASSERT(function.HasCode()); |
| } |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |
| |
| // Now Call the invoke stub which will invoke the dart function. |
| const Code& code = Code::Handle(zone, function.CurrentCode()); |
| return InvokeCode(code, arguments_descriptor, arguments, thread); |
| } |
| |
| extern "C" { |
| // Note: The invocation stub follows the C ABI, so we cannot pass C++ struct |
| // values like ObjectPtr. In some calling conventions (IA32), ObjectPtr is |
| // passed/returned different from a pointer. |
| typedef uword /*ObjectPtr*/ (*invokestub)(const Code& target_code, |
| const Array& arguments_descriptor, |
| const Array& arguments, |
| Thread* thread); |
| } |
| |
| NO_SANITIZE_SAFE_STACK |
| ObjectPtr DartEntry::InvokeCode(const Code& code, |
| const Array& arguments_descriptor, |
| const Array& arguments, |
| Thread* thread) { |
| ASSERT(!code.IsNull()); |
| ASSERT(thread->no_callback_scope_depth() == 0); |
| ASSERT(!Isolate::Current()->null_safety_not_set()); |
| |
| invokestub entrypoint = |
| reinterpret_cast<invokestub>(StubCode::InvokeDartCode().EntryPoint()); |
| SuspendLongJumpScope suspend_long_jump_scope(thread); |
| TransitionToGenerated transition(thread); |
| #if defined(USING_SIMULATOR) |
| return bit_copy<ObjectPtr, int64_t>(Simulator::Current()->Call( |
| reinterpret_cast<intptr_t>(entrypoint), reinterpret_cast<intptr_t>(&code), |
| reinterpret_cast<intptr_t>(&arguments_descriptor), |
| reinterpret_cast<intptr_t>(&arguments), |
| reinterpret_cast<intptr_t>(thread))); |
| #else |
| return static_cast<ObjectPtr>( |
| entrypoint(code, arguments_descriptor, arguments, thread)); |
| #endif |
| } |
| |
| ObjectPtr DartEntry::ResolveCallable(const Array& arguments, |
| const Array& arguments_descriptor) { |
| auto thread = Thread::Current(); |
| auto isolate = thread->isolate(); |
| auto zone = thread->zone(); |
| |
| const ArgumentsDescriptor args_desc(arguments_descriptor); |
| const intptr_t receiver_index = args_desc.FirstArgIndex(); |
| const intptr_t type_args_len = args_desc.TypeArgsLen(); |
| const auto& getter_name = Symbols::GetCall(); |
| |
| auto& instance = Instance::Handle(zone); |
| auto& function = Function::Handle(zone); |
| auto& cls = Class::Handle(zone); |
| |
| // The null instance cannot resolve to a callable, so we can stop there. |
| for (instance ^= arguments.At(receiver_index); !instance.IsNull(); |
| instance ^= arguments.At(receiver_index)) { |
| // The instance is a callable, so check that its function is compatible. |
| if (instance.IsCallable(&function)) { |
| bool matches = function.AreValidArguments(args_desc, nullptr); |
| |
| if (matches && type_args_len > 0 && function.IsClosureFunction()) { |
| // Though the closure function is generic, the closure itself may |
| // not be because it closes over delayed function type arguments. |
| matches = Closure::Cast(instance).IsGeneric(thread); |
| } |
| |
| if (matches) { |
| return function.raw(); |
| } |
| } |
| |
| // Special case: closures are implemented with a call getter instead of a |
| // call method, so checking for a call getter would cause an infinite loop. |
| if (instance.IsClosure()) { |
| break; |
| } |
| |
| cls = instance.clazz(); |
| // Find a call getter, if any, in the class hierarchy. |
| function = Resolver::ResolveDynamicAnyArgs(zone, cls, getter_name, |
| /*allow_add=*/false); |
| if (function.IsNull()) { |
| break; |
| } |
| if (!OSThread::Current()->HasStackHeadroom()) { |
| const Instance& exception = |
| Instance::Handle(zone, isolate->object_store()->stack_overflow()); |
| return UnhandledException::New(exception, StackTrace::Handle(zone)); |
| } |
| |
| const Array& getter_arguments = Array::Handle(zone, Array::New(1)); |
| getter_arguments.SetAt(0, instance); |
| const Object& getter_result = Object::Handle( |
| zone, DartEntry::InvokeFunction(function, getter_arguments)); |
| if (getter_result.IsError()) { |
| return getter_result.raw(); |
| } |
| ASSERT(getter_result.IsNull() || getter_result.IsInstance()); |
| |
| // We have a new possibly compatible callable, so set the first argument |
| // accordingly so it gets picked up in the main loop. |
| arguments.SetAt(receiver_index, getter_result); |
| } |
| |
| // No compatible callable was found. |
| return Function::null(); |
| } |
| |
| ObjectPtr DartEntry::InvokeCallable(const Function& callable_function, |
| const Array& arguments, |
| const Array& arguments_descriptor) { |
| if (!callable_function.IsNull()) { |
| return InvokeFunction(callable_function, arguments, arguments_descriptor); |
| } |
| |
| // No compatible callable was found, so invoke noSuchMethod. |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| const ArgumentsDescriptor args_desc(arguments_descriptor); |
| auto& instance = |
| Instance::CheckedHandle(zone, arguments.At(args_desc.FirstArgIndex())); |
| auto& target_name = String::Handle(zone, Symbols::Call().raw()); |
| if (instance.IsClosure()) { |
| const auto& closure = Closure::Cast(instance); |
| // For closures, use the name of the closure, not 'call'. |
| const auto& function = Function::Handle(zone, closure.function()); |
| target_name = function.QualifiedUserVisibleName(); |
| } |
| return InvokeNoSuchMethod(instance, target_name, arguments, |
| arguments_descriptor); |
| } |
| |
| ObjectPtr DartEntry::InvokeClosure(const Array& arguments) { |
| const int kTypeArgsLen = 0; // No support to pass type args to generic func. |
| |
| // Closures always have boxed parameters |
| const Array& arguments_descriptor = Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, arguments.Length())); |
| return InvokeClosure(arguments, arguments_descriptor); |
| } |
| |
| ObjectPtr DartEntry::InvokeClosure(const Array& arguments, |
| const Array& arguments_descriptor) { |
| const Object& resolved_result = |
| Object::Handle(ResolveCallable(arguments, arguments_descriptor)); |
| if (resolved_result.IsError()) { |
| return resolved_result.raw(); |
| } |
| |
| const auto& function = |
| Function::Handle(Function::RawCast(resolved_result.raw())); |
| return InvokeCallable(function, arguments, arguments_descriptor); |
| } |
| |
| ObjectPtr DartEntry::InvokeNoSuchMethod(const Instance& receiver, |
| const String& target_name, |
| const Array& arguments, |
| const Array& arguments_descriptor) { |
| const ArgumentsDescriptor args_desc(arguments_descriptor); |
| ASSERT(receiver.raw() == arguments.At(args_desc.FirstArgIndex())); |
| // Allocate an Invocation object. |
| const Library& core_lib = Library::Handle(Library::CoreLibrary()); |
| |
| Class& invocation_mirror_class = Class::Handle(core_lib.LookupClass( |
| String::Handle(core_lib.PrivateName(Symbols::InvocationMirror())))); |
| ASSERT(!invocation_mirror_class.IsNull()); |
| Thread* thread = Thread::Current(); |
| const auto& error = invocation_mirror_class.EnsureIsFinalized(thread); |
| ASSERT(error == Error::null()); |
| const String& function_name = |
| String::Handle(core_lib.PrivateName(Symbols::AllocateInvocationMirror())); |
| const Function& allocation_function = Function::Handle( |
| invocation_mirror_class.LookupStaticFunction(function_name)); |
| ASSERT(!allocation_function.IsNull()); |
| const int kNumAllocationArgs = 4; |
| const Array& allocation_args = Array::Handle(Array::New(kNumAllocationArgs)); |
| allocation_args.SetAt(0, target_name); |
| allocation_args.SetAt(1, arguments_descriptor); |
| allocation_args.SetAt(2, arguments); |
| allocation_args.SetAt(3, Bool::False()); // Not a super invocation. |
| const Object& invocation_mirror = |
| Object::Handle(InvokeFunction(allocation_function, allocation_args)); |
| if (invocation_mirror.IsError()) { |
| Exceptions::PropagateError(Error::Cast(invocation_mirror)); |
| UNREACHABLE(); |
| } |
| |
| // Now use the invocation mirror object and invoke NoSuchMethod. |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 2; |
| ArgumentsDescriptor nsm_args_desc(Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments))); |
| Function& function = Function::Handle(Resolver::ResolveDynamic( |
| receiver, Symbols::NoSuchMethod(), nsm_args_desc)); |
| if (function.IsNull()) { |
| ASSERT(!FLAG_lazy_dispatchers); |
| // If noSuchMethod(invocation) is not found, call Object::noSuchMethod. |
| function = Resolver::ResolveDynamicForReceiverClass( |
| Class::Handle(thread->zone(), |
| thread->isolate()->object_store()->object_class()), |
| Symbols::NoSuchMethod(), nsm_args_desc); |
| } |
| ASSERT(!function.IsNull()); |
| const Array& args = Array::Handle(Array::New(kNumArguments)); |
| args.SetAt(0, receiver); |
| args.SetAt(1, invocation_mirror); |
| return InvokeFunction(function, args); |
| } |
| |
| ArgumentsDescriptor::ArgumentsDescriptor(const Array& array) : array_(array) {} |
| |
| intptr_t ArgumentsDescriptor::TypeArgsLen() const { |
| return Smi::Value(Smi::RawCast(array_.At(kTypeArgsLenIndex))); |
| } |
| |
| intptr_t ArgumentsDescriptor::Count() const { |
| return Smi::Value(Smi::RawCast(array_.At(kCountIndex))); |
| } |
| |
| intptr_t ArgumentsDescriptor::Size() const { |
| return Smi::Value(Smi::RawCast(array_.At(kSizeIndex))); |
| } |
| |
| intptr_t ArgumentsDescriptor::PositionalCount() const { |
| return Smi::Value(Smi::RawCast(array_.At(kPositionalCountIndex))); |
| } |
| |
| StringPtr ArgumentsDescriptor::NameAt(intptr_t index) const { |
| const intptr_t offset = |
| kFirstNamedEntryIndex + (index * kNamedEntrySize) + kNameOffset; |
| String& result = String::Handle(); |
| result ^= array_.At(offset); |
| return result.raw(); |
| } |
| |
| intptr_t ArgumentsDescriptor::PositionAt(intptr_t index) const { |
| const intptr_t offset = |
| kFirstNamedEntryIndex + (index * kNamedEntrySize) + kPositionOffset; |
| return Smi::Value(Smi::RawCast(array_.At(offset))); |
| } |
| |
| bool ArgumentsDescriptor::MatchesNameAt(intptr_t index, |
| const String& other) const { |
| return NameAt(index) == other.raw(); |
| } |
| |
| ArrayPtr ArgumentsDescriptor::GetArgumentNames() const { |
| const intptr_t num_named_args = NamedCount(); |
| if (num_named_args == 0) { |
| return Array::null(); |
| } |
| |
| Zone* zone = Thread::Current()->zone(); |
| const Array& names = |
| Array::Handle(zone, Array::New(num_named_args, Heap::kOld)); |
| String& name = String::Handle(zone); |
| const intptr_t num_pos_args = PositionalCount(); |
| for (intptr_t i = 0; i < num_named_args; ++i) { |
| const intptr_t index = PositionAt(i) - num_pos_args; |
| name = NameAt(i); |
| ASSERT(names.At(index) == Object::null()); |
| names.SetAt(index, name); |
| } |
| return names.raw(); |
| } |
| |
| void ArgumentsDescriptor::PrintTo(BaseTextBuffer* buffer) const { |
| buffer->Printf("%" Pd " arg%s", Count(), Count() == 1 ? "" : "s"); |
| if (TypeArgsLen() > 0) { |
| buffer->Printf(", %" Pd " type arg%s", TypeArgsLen(), |
| TypeArgsLen() == 1 ? "" : "s"); |
| } |
| if (NamedCount() > 0) { |
| buffer->AddString(", names ["); |
| auto& str = String::Handle(); |
| for (intptr_t i = 0; i < NamedCount(); i++) { |
| if (i != 0) { |
| buffer->AddString(", "); |
| } |
| str = NameAt(i); |
| buffer->Printf("'%s'", str.ToCString()); |
| } |
| buffer->Printf("]"); |
| } |
| } |
| |
| const char* ArgumentsDescriptor::ToCString() const { |
| ZoneTextBuffer buf(Thread::Current()->zone()); |
| PrintTo(&buf); |
| return buf.buffer(); |
| } |
| |
| ArrayPtr ArgumentsDescriptor::New(intptr_t type_args_len, |
| intptr_t num_arguments, |
| intptr_t size_arguments, |
| const Array& optional_arguments_names, |
| Heap::Space space) { |
| const intptr_t num_named_args = |
| optional_arguments_names.IsNull() ? 0 : optional_arguments_names.Length(); |
| if (num_named_args == 0) { |
| return ArgumentsDescriptor::New(type_args_len, num_arguments, |
| size_arguments, space); |
| } |
| ASSERT(type_args_len >= 0); |
| ASSERT(num_arguments >= 0); |
| const intptr_t num_pos_args = num_arguments - num_named_args; |
| |
| // Build the arguments descriptor array, which consists of the the type |
| // argument vector length (0 if none); total argument count; the positional |
| // argument count; a sequence of (name, position) pairs, sorted by name, for |
| // each named optional argument; and a terminating null to simplify iterating |
| // in generated code. |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| const intptr_t descriptor_len = LengthFor(num_named_args); |
| Array& descriptor = Array::Handle(zone, Array::New(descriptor_len, space)); |
| |
| // Set length of type argument vector. |
| descriptor.SetAt(kTypeArgsLenIndex, Smi::Handle(Smi::New(type_args_len))); |
| // Set total number of passed arguments. |
| descriptor.SetAt(kCountIndex, Smi::Handle(Smi::New(num_arguments))); |
| // Set total number of passed arguments. |
| descriptor.SetAt(kSizeIndex, Smi::Handle(Smi::New(size_arguments))); |
| |
| // Set number of positional arguments. |
| descriptor.SetAt(kPositionalCountIndex, Smi::Handle(Smi::New(num_pos_args))); |
| |
| // Set alphabetically sorted entries for named arguments. |
| String& name = String::Handle(zone); |
| Smi& pos = Smi::Handle(zone); |
| String& previous_name = String::Handle(zone); |
| Smi& previous_pos = Smi::Handle(zone); |
| for (intptr_t i = 0; i < num_named_args; i++) { |
| name ^= optional_arguments_names.At(i); |
| pos = Smi::New(num_pos_args + i); |
| intptr_t insert_index = kFirstNamedEntryIndex + (kNamedEntrySize * i); |
| // Shift already inserted pairs with "larger" names. |
| while (insert_index > kFirstNamedEntryIndex) { |
| intptr_t previous_index = insert_index - kNamedEntrySize; |
| previous_name ^= descriptor.At(previous_index + kNameOffset); |
| intptr_t result = name.CompareTo(previous_name); |
| ASSERT(result != 0); // Duplicate argument names checked in parser. |
| if (result > 0) break; |
| previous_pos ^= descriptor.At(previous_index + kPositionOffset); |
| descriptor.SetAt(insert_index + kNameOffset, previous_name); |
| descriptor.SetAt(insert_index + kPositionOffset, previous_pos); |
| insert_index = previous_index; |
| } |
| // Insert pair in descriptor array. |
| descriptor.SetAt(insert_index + kNameOffset, name); |
| descriptor.SetAt(insert_index + kPositionOffset, pos); |
| } |
| // Set terminating null. |
| descriptor.SetAt(descriptor_len - 1, Object::null_object()); |
| |
| // Share the immutable descriptor when possible by canonicalizing it. |
| descriptor.MakeImmutable(); |
| const char* error_str = NULL; |
| descriptor ^= descriptor.CheckAndCanonicalize(thread, &error_str); |
| if (error_str != NULL) { |
| FATAL1("Failed to canonicalize: %s", error_str); |
| } |
| ASSERT(!descriptor.IsNull()); |
| return descriptor.raw(); |
| } |
| |
| ArrayPtr ArgumentsDescriptor::New(intptr_t type_args_len, |
| intptr_t num_arguments, |
| intptr_t size_arguments, |
| Heap::Space space) { |
| ASSERT(type_args_len >= 0); |
| ASSERT(num_arguments >= 0); |
| |
| if ((type_args_len == 0) && (num_arguments < kCachedDescriptorCount) && |
| (num_arguments == size_arguments)) { |
| return cached_args_descriptors_[num_arguments]; |
| } |
| return NewNonCached(type_args_len, num_arguments, size_arguments, true, |
| space); |
| } |
| |
| ArrayPtr ArgumentsDescriptor::NewNonCached(intptr_t type_args_len, |
| intptr_t num_arguments, |
| intptr_t size_arguments, |
| bool canonicalize, |
| Heap::Space space) { |
| // Build the arguments descriptor array, which consists of the length of the |
| // type argument vector, total argument count; the positional argument count; |
| // and a terminating null to simplify iterating in generated code. |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| const intptr_t descriptor_len = LengthFor(0); |
| Array& descriptor = Array::Handle(zone, Array::New(descriptor_len, space)); |
| const Smi& arg_count = Smi::Handle(zone, Smi::New(num_arguments)); |
| const Smi& arg_size = Smi::Handle(zone, Smi::New(size_arguments)); |
| |
| // Set type argument vector length. |
| descriptor.SetAt(kTypeArgsLenIndex, |
| Smi::Handle(zone, Smi::New(type_args_len))); |
| |
| // Set total number of passed arguments. |
| descriptor.SetAt(kCountIndex, arg_count); |
| |
| // Set total size of passed arguments. |
| descriptor.SetAt(kSizeIndex, arg_size); |
| |
| // Set number of positional arguments. |
| descriptor.SetAt(kPositionalCountIndex, arg_count); |
| |
| // Set terminating null. |
| descriptor.SetAt((descriptor_len - 1), Object::null_object()); |
| |
| // Share the immutable descriptor when possible by canonicalizing it. |
| descriptor.MakeImmutable(); |
| if (canonicalize) { |
| const char* error_str = NULL; |
| descriptor ^= descriptor.CheckAndCanonicalize(thread, &error_str); |
| if (error_str != NULL) { |
| FATAL1("Failed to canonicalize: %s", error_str); |
| } |
| } |
| ASSERT(!descriptor.IsNull()); |
| return descriptor.raw(); |
| } |
| |
| void ArgumentsDescriptor::Init() { |
| for (int i = 0; i < kCachedDescriptorCount; i++) { |
| cached_args_descriptors_[i] = |
| NewNonCached(/*type_args_len=*/0, i, i, false, Heap::kOld); |
| } |
| } |
| |
| void ArgumentsDescriptor::Cleanup() { |
| for (int i = 0; i < kCachedDescriptorCount; i++) { |
| // Don't free pointers to RawArray objects managed by the VM. |
| cached_args_descriptors_[i] = NULL; |
| } |
| } |
| |
| ObjectPtr DartLibraryCalls::InstanceCreate(const Library& lib, |
| const String& class_name, |
| const String& constructor_name, |
| const Array& arguments) { |
| const Class& cls = Class::Handle(lib.LookupClassAllowPrivate(class_name)); |
| ASSERT(!cls.IsNull()); |
| // For now, we only support a non-parameterized or raw type. |
| const int kNumExtraArgs = 1; // implicit rcvr arg. |
| const Instance& exception_object = Instance::Handle(Instance::New(cls)); |
| const Array& constructor_arguments = |
| Array::Handle(Array::New(arguments.Length() + kNumExtraArgs)); |
| constructor_arguments.SetAt(0, exception_object); |
| Object& obj = Object::Handle(); |
| for (intptr_t i = 0; i < arguments.Length(); i++) { |
| obj = arguments.At(i); |
| constructor_arguments.SetAt((i + kNumExtraArgs), obj); |
| } |
| |
| const String& function_name = |
| String::Handle(String::Concat(class_name, constructor_name)); |
| const Function& constructor = |
| Function::Handle(cls.LookupConstructorAllowPrivate(function_name)); |
| ASSERT(!constructor.IsNull()); |
| const Object& retval = Object::Handle( |
| DartEntry::InvokeFunction(constructor, constructor_arguments)); |
| ASSERT(retval.IsNull() || retval.IsError()); |
| if (retval.IsError()) { |
| return retval.raw(); |
| } |
| return exception_object.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::ToString(const Instance& receiver) { |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 1; // Receiver. |
| ArgumentsDescriptor args_desc(Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments))); |
| const Class& receiver_class = Class::Handle(receiver.clazz()); |
| const auto& error = receiver_class.EnsureIsFinalized(Thread::Current()); |
| ASSERT(error == Error::null()); |
| const Function& function = Function::Handle( |
| Resolver::ResolveDynamic(receiver, Symbols::toString(), args_desc)); |
| ASSERT(!function.IsNull()); |
| const Array& args = Array::Handle(Array::New(kNumArguments)); |
| args.SetAt(0, receiver); |
| const Object& result = |
| Object::Handle(DartEntry::InvokeFunction(function, args)); |
| ASSERT(result.IsInstance() || result.IsError()); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::HashCode(const Instance& receiver) { |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 1; // Receiver. |
| ArgumentsDescriptor args_desc(Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments))); |
| const Function& function = Function::Handle( |
| Resolver::ResolveDynamic(receiver, Symbols::hashCode(), args_desc)); |
| ASSERT(!function.IsNull()); |
| const Array& args = Array::Handle(Array::New(kNumArguments)); |
| args.SetAt(0, receiver); |
| const Object& result = |
| Object::Handle(DartEntry::InvokeFunction(function, args)); |
| ASSERT(result.IsInstance() || result.IsError()); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::Equals(const Instance& left, |
| const Instance& right) { |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 2; |
| ArgumentsDescriptor args_desc(Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments))); |
| const Function& function = Function::Handle( |
| Resolver::ResolveDynamic(left, Symbols::EqualOperator(), args_desc)); |
| ASSERT(!function.IsNull()); |
| |
| const Array& args = Array::Handle(Array::New(kNumArguments)); |
| args.SetAt(0, left); |
| args.SetAt(1, right); |
| const Object& result = |
| Object::Handle(DartEntry::InvokeFunction(function, args)); |
| ASSERT(result.IsInstance() || result.IsError()); |
| return result.raw(); |
| } |
| |
| // On success, returns a RawInstance. On failure, a RawError. |
| ObjectPtr DartLibraryCalls::IdentityHashCode(const Instance& object) { |
| const int kNumArguments = 1; |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| const Library& libcore = Library::Handle(zone, Library::CoreLibrary()); |
| ASSERT(!libcore.IsNull()); |
| const Function& function = Function::Handle( |
| zone, libcore.LookupFunctionAllowPrivate(Symbols::identityHashCode())); |
| ASSERT(!function.IsNull()); |
| const Array& args = Array::Handle(zone, Array::New(kNumArguments)); |
| args.SetAt(0, object); |
| const Object& result = |
| Object::Handle(zone, DartEntry::InvokeFunction(function, args)); |
| ASSERT(result.IsInstance() || result.IsError()); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::LookupHandler(Dart_Port port_id) { |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| Function& function = Function::Handle( |
| zone, thread->isolate()->object_store()->lookup_port_handler()); |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 1; |
| if (function.IsNull()) { |
| Library& isolate_lib = Library::Handle(zone, Library::IsolateLibrary()); |
| ASSERT(!isolate_lib.IsNull()); |
| const String& class_name = String::Handle( |
| zone, isolate_lib.PrivateName(Symbols::_RawReceivePortImpl())); |
| const String& function_name = String::Handle( |
| zone, isolate_lib.PrivateName(Symbols::_lookupHandler())); |
| function = Resolver::ResolveStatic(isolate_lib, class_name, function_name, |
| kTypeArgsLen, kNumArguments, |
| Object::empty_array()); |
| ASSERT(!function.IsNull()); |
| thread->isolate()->object_store()->set_lookup_port_handler(function); |
| } |
| Array& args = Array::Handle( |
| zone, thread->isolate()->isolate_object_store()->dart_args_1()); |
| if (args.IsNull()) { |
| args = Array::New(kNumArguments); |
| thread->isolate()->isolate_object_store()->set_dart_args_1(args); |
| } |
| args.SetAt(0, Integer::Handle(zone, Integer::New(port_id))); |
| const Object& result = |
| Object::Handle(zone, DartEntry::InvokeFunction(function, args)); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::HandleMessage(const Object& handler, |
| const Instance& message) { |
| Thread* thread = Thread::Current(); |
| Zone* zone = thread->zone(); |
| Isolate* isolate = thread->isolate(); |
| Function& function = Function::Handle( |
| zone, isolate->object_store()->handle_message_function()); |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 2; |
| if (function.IsNull()) { |
| Library& isolate_lib = Library::Handle(zone, Library::IsolateLibrary()); |
| ASSERT(!isolate_lib.IsNull()); |
| const String& class_name = String::Handle( |
| zone, isolate_lib.PrivateName(Symbols::_RawReceivePortImpl())); |
| const String& function_name = String::Handle( |
| zone, isolate_lib.PrivateName(Symbols::_handleMessage())); |
| function = Resolver::ResolveStatic(isolate_lib, class_name, function_name, |
| kTypeArgsLen, kNumArguments, |
| Object::empty_array()); |
| ASSERT(!function.IsNull()); |
| isolate->object_store()->set_handle_message_function(function); |
| } |
| Array& args = Array::Handle( |
| zone, thread->isolate()->isolate_object_store()->dart_args_2()); |
| if (args.IsNull()) { |
| args = Array::New(kNumArguments); |
| thread->isolate()->isolate_object_store()->set_dart_args_2(args); |
| } |
| args.SetAt(0, handler); |
| args.SetAt(1, message); |
| #if !defined(PRODUCT) |
| if (isolate->debugger()->IsStepping()) { |
| // If the isolate is being debugged and the debugger was stepping |
| // through code, enable single stepping so debugger will stop |
| // at the first location the user is interested in. |
| isolate->debugger()->SetResumeAction(Debugger::kStepInto); |
| } |
| #endif |
| const Object& result = |
| Object::Handle(zone, DartEntry::InvokeFunction(function, args)); |
| ASSERT(result.IsNull() || result.IsError()); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::DrainMicrotaskQueue() { |
| Zone* zone = Thread::Current()->zone(); |
| Library& isolate_lib = Library::Handle(zone, Library::IsolateLibrary()); |
| ASSERT(!isolate_lib.IsNull()); |
| Function& function = |
| Function::Handle(zone, isolate_lib.LookupFunctionAllowPrivate( |
| Symbols::_runPendingImmediateCallback())); |
| const Object& result = Object::Handle( |
| zone, DartEntry::InvokeFunction(function, Object::empty_array())); |
| ASSERT(result.IsNull() || result.IsError()); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::EnsureScheduleImmediate() { |
| Zone* zone = Thread::Current()->zone(); |
| const Library& async_lib = Library::Handle(zone, Library::AsyncLibrary()); |
| ASSERT(!async_lib.IsNull()); |
| const Function& function = |
| Function::Handle(zone, async_lib.LookupFunctionAllowPrivate( |
| Symbols::_ensureScheduleImmediate())); |
| ASSERT(!function.IsNull()); |
| const Object& result = Object::Handle( |
| zone, DartEntry::InvokeFunction(function, Object::empty_array())); |
| ASSERT(result.IsNull() || result.IsError()); |
| return result.raw(); |
| } |
| |
| ObjectPtr DartLibraryCalls::MapSetAt(const Instance& map, |
| const Instance& key, |
| const Instance& value) { |
| const int kTypeArgsLen = 0; |
| const int kNumArguments = 3; |
| ArgumentsDescriptor args_desc(Array::Handle( |
| ArgumentsDescriptor::NewBoxed(kTypeArgsLen, kNumArguments))); |
| const Function& function = Function::Handle( |
| Resolver::ResolveDynamic(map, Symbols::AssignIndexToken(), args_desc)); |
| ASSERT(!function.IsNull()); |
| const Array& args = Array::Handle(Array::New(kNumArguments)); |
| args.SetAt(0, map); |
| args.SetAt(1, key); |
| args.SetAt(2, value); |
| const Object& result = |
| Object::Handle(DartEntry::InvokeFunction(function, args)); |
| return result.raw(); |
| } |
| |
| } // namespace dart |