dart-sdk/runtime/vm/dart_entry.cc
Siva Annamalai 9e19d236ca - Add an OSThread structure which is the generic TLS structure for all C++
fields in a thread (i.e fields that are not Dart VM related)
- Split the Thread structure to be a pure Dart per thread structure and add
  a pointer to os_thread which points to the OSThread structure
- Change Schedule/UnSchedule to set the Dart Thread structure as the TLS of
  the thread when it is inside the Dart world and reset the TLS back to the
  OSThread strcuture when is exits the Dart World.
- Moved the stack_base and few stack size related functions to OSThread from Isolate

R=johnmccutchan@google.com, zra@google.com

Review URL: https://codereview.chromium.org/1439483003 .
2015-11-19 13:45:10 -08:00

602 lines
23 KiB
C++

// 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 "vm/class_finalizer.h"
#include "vm/code_generator.h"
#include "vm/compiler.h"
#include "vm/debugger.h"
#include "vm/object_store.h"
#include "vm/resolver.h"
#include "vm/simulator.h"
#include "vm/stub_code.h"
#include "vm/symbols.h"
namespace dart {
DECLARE_FLAG(bool, lazy_dispatchers);
// A cache of VM heap allocated arguments descriptors.
RawArray* ArgumentsDescriptor::cached_args_descriptors_[kCachedDescriptorCount];
RawObject* DartEntry::InvokeFunction(const Function& function,
const Array& arguments) {
ASSERT(Thread::Current()->IsMutatorThread());
const Array& arguments_descriptor =
Array::Handle(ArgumentsDescriptor::New(arguments.Length()));
return InvokeFunction(function, arguments, arguments_descriptor);
}
class ScopedIsolateStackLimits : public ValueObject {
public:
explicit ScopedIsolateStackLimits(Thread* thread)
: thread_(thread), saved_stack_limit_(0) {
ASSERT(thread != NULL);
// Set the thread's stack_base based on the current
// stack pointer, we keep refining this value as we
// see higher stack pointers (Note: we assume the stack
// grows from high to low addresses).
OSThread* os_thread = thread->os_thread();
ASSERT(os_thread != NULL);
uword current_sp = Isolate::GetCurrentStackPointer();
if (current_sp > os_thread->stack_base()) {
os_thread->set_stack_base(current_sp);
}
// Save the Isolate's current stack limit and adjust the stack
// limit based on the thread's stack_base.
Isolate* isolate = thread->isolate();
ASSERT(isolate == Isolate::Current());
saved_stack_limit_ = isolate->saved_stack_limit();
isolate->SetStackLimitFromStackBase(os_thread->stack_base());
}
~ScopedIsolateStackLimits() {
Isolate* isolate = thread_->isolate();
ASSERT(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.
isolate->SetStackLimit(saved_stack_limit_);
}
private:
Thread* thread_;
uword saved_stack_limit_;
};
// Clears/restores Thread::long_jump_base on construction/destruction.
// Ensures that we do not attempt to long jump across Dart frames.
class SuspendLongJumpScope : public StackResource {
public:
explicit SuspendLongJumpScope(Thread* thread)
: StackResource(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_;
};
RawObject* DartEntry::InvokeFunction(const Function& function,
const Array& arguments,
const Array& arguments_descriptor) {
// 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());
if (!function.HasCode()) {
const Error& error = Error::Handle(
zone, Compiler::CompileFunction(thread, function));
if (!error.IsNull()) {
return error.raw();
}
}
// Now Call the invoke stub which will invoke the dart function.
invokestub entrypoint = reinterpret_cast<invokestub>(
StubCode::InvokeDartCode_entry()->EntryPoint());
const Code& code = Code::Handle(zone, function.CurrentCode());
ASSERT(!code.IsNull());
ASSERT(thread->no_callback_scope_depth() == 0);
ScopedIsolateStackLimits stack_limit(thread);
SuspendLongJumpScope suspend_long_jump_scope(thread);
#if defined(USING_SIMULATOR)
return bit_copy<RawObject*, 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 entrypoint(code,
arguments_descriptor,
arguments,
thread);
#endif
}
RawObject* DartEntry::InvokeClosure(const Array& arguments) {
const Array& arguments_descriptor =
Array::Handle(ArgumentsDescriptor::New(arguments.Length()));
return InvokeClosure(arguments, arguments_descriptor);
}
RawObject* DartEntry::InvokeClosure(const Array& arguments,
const Array& arguments_descriptor) {
Instance& instance = Instance::Handle();
instance ^= arguments.At(0);
// Get the entrypoint corresponding to the closure function or to the call
// method of the instance. This will result in a compilation of the function
// if it is not already compiled.
Function& function = Function::Handle();
if (instance.IsCallable(&function)) {
// Only invoke the function if its arguments are compatible.
const ArgumentsDescriptor args_desc(arguments_descriptor);
if (function.AreValidArgumentCounts(args_desc.Count(),
args_desc.NamedCount(),
NULL)) {
// The closure or non-closure object (receiver) is passed as implicit
// first argument. It is already included in the arguments array.
return InvokeFunction(function, arguments, arguments_descriptor);
}
}
// There is no compatible 'call' method, see if there's a getter.
if (instance.IsClosure()) {
// Special case: closures are implemented with a call getter instead of a
// call method. If the arguments didn't match, go to noSuchMethod instead
// of infinitely recursing on the getter.
} else {
const String& getter_name = String::Handle(Symbols::New("get:call"));
Class& cls = Class::Handle(instance.clazz());
while (!cls.IsNull()) {
function ^= cls.LookupDynamicFunction(getter_name);
if (!function.IsNull()) {
// Getters don't have a stack overflow check, so do one in C++.
Isolate* isolate = Isolate::Current();
#if defined(USING_SIMULATOR)
uword stack_pos = Simulator::Current()->get_register(SPREG);
#else
uword stack_pos = Isolate::GetCurrentStackPointer();
#endif
if (stack_pos < isolate->saved_stack_limit()) {
const Instance& exception =
Instance::Handle(isolate->object_store()->stack_overflow());
return UnhandledException::New(exception, Stacktrace::Handle());
}
const Array& getter_arguments = Array::Handle(Array::New(1));
getter_arguments.SetAt(0, instance);
const Object& getter_result =
Object::Handle(DartEntry::InvokeFunction(function,
getter_arguments));
if (getter_result.IsError()) {
return getter_result.raw();
}
ASSERT(getter_result.IsNull() || getter_result.IsInstance());
arguments.SetAt(0, getter_result);
return InvokeClosure(arguments, arguments_descriptor);
}
cls = cls.SuperClass();
}
}
// No compatible method or getter so invoke noSuchMethod.
return InvokeNoSuchMethod(instance,
Symbols::Call(),
arguments,
arguments_descriptor);
}
RawObject* DartEntry::InvokeNoSuchMethod(const Instance& receiver,
const String& target_name,
const Array& arguments,
const Array& arguments_descriptor) {
ASSERT(receiver.raw() == arguments.At(0));
// 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());
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));
// Now use the invocation mirror object and invoke NoSuchMethod.
const int kNumArguments = 2;
ArgumentsDescriptor args_desc(
Array::Handle(ArgumentsDescriptor::New(kNumArguments)));
Function& function = Function::Handle(
Resolver::ResolveDynamic(receiver,
Symbols::NoSuchMethod(),
args_desc));
if (function.IsNull()) {
ASSERT(!FLAG_lazy_dispatchers);
// If noSuchMethod(invocation) is not found, call Object::noSuchMethod.
Thread* thread = Thread::Current();
function ^= Resolver::ResolveDynamicForReceiverClass(
Class::Handle(thread->zone(),
thread->isolate()->object_store()->object_class()),
Symbols::NoSuchMethod(),
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::Count() const {
return Smi::Cast(Object::Handle(array_.At(kCountIndex))).Value();
}
intptr_t ArgumentsDescriptor::PositionalCount() const {
return Smi::Cast(Object::Handle(array_.At(kPositionalCountIndex))).Value();
}
RawString* 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();
}
bool ArgumentsDescriptor::MatchesNameAt(intptr_t index,
const String& other) const {
return NameAt(index) == other.raw();
}
intptr_t ArgumentsDescriptor::count_offset() {
return Array::element_offset(kCountIndex);
}
intptr_t ArgumentsDescriptor::positional_count_offset() {
return Array::element_offset(kPositionalCountIndex);
}
intptr_t ArgumentsDescriptor::first_named_entry_offset() {
return Array::element_offset(kFirstNamedEntryIndex);
}
RawArray* ArgumentsDescriptor::New(intptr_t num_arguments,
const Array& optional_arguments_names) {
const intptr_t num_named_args =
optional_arguments_names.IsNull() ? 0 : optional_arguments_names.Length();
if (num_named_args == 0) {
return ArgumentsDescriptor::New(num_arguments);
}
const intptr_t num_pos_args = num_arguments - num_named_args;
// Build the arguments descriptor array, which consists of the 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.
const intptr_t descriptor_len = LengthFor(num_named_args);
Array& descriptor = Array::Handle(Array::New(descriptor_len, Heap::kOld));
// Set total number of passed arguments.
descriptor.SetAt(kCountIndex, Smi::Handle(Smi::New(num_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();
Smi& pos = Smi::Handle();
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.
String& previous_name = String::Handle();
Smi& previous_pos = Smi::Handle();
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();
descriptor ^= descriptor.CheckAndCanonicalize(NULL);
ASSERT(!descriptor.IsNull());
return descriptor.raw();
}
RawArray* ArgumentsDescriptor::New(intptr_t num_arguments) {
ASSERT(num_arguments >= 0);
if (num_arguments < kCachedDescriptorCount) {
return cached_args_descriptors_[num_arguments];
}
return NewNonCached(num_arguments);
}
RawArray* ArgumentsDescriptor::NewNonCached(intptr_t num_arguments,
bool canonicalize) {
// Build the arguments descriptor array, which consists of the total
// argument count; the positional argument count; and
// a terminating null to simplify iterating in generated code.
const intptr_t descriptor_len = LengthFor(0);
Array& descriptor = Array::Handle(Array::New(descriptor_len, Heap::kOld));
const Smi& arg_count = Smi::Handle(Smi::New(num_arguments));
// Set total number of passed arguments.
descriptor.SetAt(kCountIndex, arg_count);
// 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) {
descriptor ^= descriptor.CheckAndCanonicalize(NULL);
}
ASSERT(!descriptor.IsNull());
return descriptor.raw();
}
void ArgumentsDescriptor::InitOnce() {
for (int i = 0; i < kCachedDescriptorCount; i++) {
cached_args_descriptors_[i] = ArgumentsDescriptor::NewNonCached(i, false);
}
}
RawObject* 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 = 2; // implicit rcvr and construction phase args.
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);
constructor_arguments.SetAt(
1, Smi::Handle(Smi::New(Function::kCtorPhaseAll)));
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();
}
RawObject* DartLibraryCalls::ToString(const Instance& receiver) {
const int kNumArguments = 1; // Receiver.
ArgumentsDescriptor args_desc(
Array::Handle(ArgumentsDescriptor::New(kNumArguments)));
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();
}
RawObject* DartLibraryCalls::HashCode(const Instance& receiver) {
const int kNumArguments = 1; // Receiver.
ArgumentsDescriptor args_desc(
Array::Handle(ArgumentsDescriptor::New(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();
}
RawObject* DartLibraryCalls::Equals(const Instance& left,
const Instance& right) {
const int kNumArguments = 2;
ArgumentsDescriptor args_desc(
Array::Handle(ArgumentsDescriptor::New(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();
}
RawObject* DartLibraryCalls::LookupHandler(Dart_Port port_id) {
Thread* thread = Thread::Current();
Function& function = Function::Handle(thread->zone(),
thread->isolate()->object_store()->lookup_port_handler());
const int kNumArguments = 1;
if (function.IsNull()) {
Library& isolate_lib = Library::Handle(Library::IsolateLibrary());
ASSERT(!isolate_lib.IsNull());
const String& class_name =
String::Handle(isolate_lib.PrivateName(Symbols::_RawReceivePortImpl()));
const String& function_name =
String::Handle(isolate_lib.PrivateName(Symbols::_lookupHandler()));
function = Resolver::ResolveStatic(isolate_lib,
class_name,
function_name,
kNumArguments,
Object::empty_array());
ASSERT(!function.IsNull());
thread->isolate()->object_store()->set_lookup_port_handler(function);
}
const Array& args = Array::Handle(Array::New(kNumArguments));
args.SetAt(0, Integer::Handle(Integer::New(port_id)));
const Object& result =
Object::Handle(DartEntry::InvokeFunction(function, args));
return result.raw();
}
RawObject* 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 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,
kNumArguments,
Object::empty_array());
ASSERT(!function.IsNull());
isolate->object_store()->set_handle_message_function(function);
}
const Array& args = Array::Handle(zone, Array::New(kNumArguments));
args.SetAt(0, handler);
args.SetAt(1, message);
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()->SetSingleStep();
}
const Object& result = Object::Handle(zone,
DartEntry::InvokeFunction(function, args));
ASSERT(result.IsNull() || result.IsError());
return result.raw();
}
RawObject* 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();
}
RawObject* DartLibraryCalls::MapSetAt(const Instance& map,
const Instance& key,
const Instance& value) {
const int kNumArguments = 3;
ArgumentsDescriptor args_desc(
Array::Handle(ArgumentsDescriptor::New(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