68d178f223
When these were originally added, we thought we would be able to use them in place of ifdefs and rely on the compiler optimizations and linker GC to remove things. This turned out not to reliably remove what we wanted removed, so we ended up with the ifdefs anyway. Change-Id: I62e74d60d92b18a688b9dffaf77b1440c10a07ff Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134402 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
453 lines
13 KiB
C++
453 lines
13 KiB
C++
// Copyright (c) 2015, 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/bootstrap_natives.h"
|
|
#include "vm/dart_api_impl.h"
|
|
#include "vm/datastream.h"
|
|
#include "vm/exceptions.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/kernel_isolate.h"
|
|
#include "vm/message.h"
|
|
#include "vm/message_handler.h"
|
|
#include "vm/native_entry.h"
|
|
#include "vm/object.h"
|
|
#include "vm/port.h"
|
|
#include "vm/service_event.h"
|
|
#include "vm/service_isolate.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
DECLARE_FLAG(bool, trace_service);
|
|
|
|
#ifndef PRODUCT
|
|
class RegisterRunningIsolatesVisitor : public IsolateVisitor {
|
|
public:
|
|
explicit RegisterRunningIsolatesVisitor(Thread* thread)
|
|
: IsolateVisitor(),
|
|
register_function_(Function::Handle(thread->zone())),
|
|
service_isolate_(thread->isolate()) {
|
|
ASSERT(ServiceIsolate::IsServiceIsolate(Isolate::Current()));
|
|
// Get library.
|
|
const String& library_url = Symbols::DartVMService();
|
|
ASSERT(!library_url.IsNull());
|
|
const Library& library =
|
|
Library::Handle(Library::LookupLibrary(thread, library_url));
|
|
ASSERT(!library.IsNull());
|
|
// Get function.
|
|
const String& function_name =
|
|
String::Handle(String::New("_registerIsolate"));
|
|
ASSERT(!function_name.IsNull());
|
|
register_function_ = library.LookupFunctionAllowPrivate(function_name);
|
|
ASSERT(!register_function_.IsNull());
|
|
}
|
|
|
|
virtual void VisitIsolate(Isolate* isolate) {
|
|
ASSERT(ServiceIsolate::IsServiceIsolate(Isolate::Current()));
|
|
if (!FLAG_show_invisible_isolates && IsVMInternalIsolate(isolate)) {
|
|
// We do not register the service (and descendants), the vm-isolate, or
|
|
// the kernel isolate.
|
|
return;
|
|
}
|
|
// Setup arguments for call.
|
|
Dart_Port port_id = isolate->main_port();
|
|
const Integer& port_int = Integer::Handle(Integer::New(port_id));
|
|
ASSERT(!port_int.IsNull());
|
|
const SendPort& send_port = SendPort::Handle(SendPort::New(port_id));
|
|
const String& name = String::Handle(String::New(isolate->name()));
|
|
ASSERT(!name.IsNull());
|
|
const Array& args = Array::Handle(Array::New(3));
|
|
ASSERT(!args.IsNull());
|
|
args.SetAt(0, port_int);
|
|
args.SetAt(1, send_port);
|
|
args.SetAt(2, name);
|
|
const Object& r =
|
|
Object::Handle(DartEntry::InvokeFunction(register_function_, args));
|
|
if (FLAG_trace_service) {
|
|
OS::PrintErr("vm-service: Isolate %s %" Pd64 " registered.\n",
|
|
name.ToCString(), port_id);
|
|
}
|
|
ASSERT(!r.IsError());
|
|
}
|
|
|
|
private:
|
|
Function& register_function_;
|
|
Isolate* service_isolate_;
|
|
};
|
|
#endif // !PRODUCT
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_SendIsolateServiceMessage, 0, 2) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(SendPort, sp, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(1));
|
|
|
|
// Set the type of the OOB message.
|
|
message.SetAt(0,
|
|
Smi::Handle(thread->zone(), Smi::New(Message::kServiceOOBMsg)));
|
|
|
|
// Serialize message.
|
|
MessageWriter writer(false);
|
|
// TODO(turnidge): Throw an exception when the return value is false?
|
|
bool result = PortMap::PostMessage(
|
|
writer.WriteMessage(message, sp.Id(), Message::kOOBPriority));
|
|
return Bool::Get(result).raw();
|
|
#else
|
|
return Object::null();
|
|
#endif
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_SendRootServiceMessage, 0, 1) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0));
|
|
return Service::HandleRootMessage(message);
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_SendObjectRootServiceMessage, 0, 1) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0));
|
|
return Service::HandleObjectRootMessage(message);
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_OnStart, 0, 0) {
|
|
#ifndef PRODUCT
|
|
if (FLAG_trace_service) {
|
|
OS::PrintErr("vm-service: Booting dart:vmservice library.\n");
|
|
}
|
|
// Boot the dart:vmservice library.
|
|
ServiceIsolate::BootVmServiceLibrary();
|
|
// Register running isolates with service.
|
|
RegisterRunningIsolatesVisitor register_isolates(thread);
|
|
if (FLAG_trace_service) {
|
|
OS::PrintErr("vm-service: Registering running isolates.\n");
|
|
}
|
|
Isolate::VisitIsolates(®ister_isolates);
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_OnExit, 0, 0) {
|
|
#ifndef PRODUCT
|
|
if (FLAG_trace_service) {
|
|
OS::PrintErr("vm-service: processed exit message.\n");
|
|
MessageHandler* message_handler = isolate->message_handler();
|
|
OS::PrintErr("vm-service: live ports = %" Pd "\n",
|
|
message_handler->live_ports());
|
|
}
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_OnServerAddressChange, 0, 1) {
|
|
#ifndef PRODUCT
|
|
GET_NATIVE_ARGUMENT(String, address, arguments->NativeArgAt(0));
|
|
if (address.IsNull()) {
|
|
ServiceIsolate::SetServerAddress(NULL);
|
|
} else {
|
|
ServiceIsolate::SetServerAddress(address.ToCString());
|
|
}
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_ListenStream, 0, 1) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, stream_id, arguments->NativeArgAt(0));
|
|
bool result = Service::ListenStream(stream_id.ToCString());
|
|
return Bool::Get(result).raw();
|
|
#else
|
|
return Object::null();
|
|
#endif
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_CancelStream, 0, 1) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, stream_id, arguments->NativeArgAt(0));
|
|
Service::CancelStream(stream_id.ToCString());
|
|
#endif
|
|
return Object::null();
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_RequestAssets, 0, 0) {
|
|
#ifndef PRODUCT
|
|
return Service::RequestAssets();
|
|
#else
|
|
return Object::null();
|
|
#endif
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
// TODO(25041): When reading, this class copies out the filenames and contents
|
|
// into new buffers. It does this because the lifetime of |bytes| is uncertain.
|
|
// If |bytes| is pinned in memory, then we could instead load up
|
|
// |filenames_| and |contents_| with pointers into |bytes| without making
|
|
// copies.
|
|
class TarArchive {
|
|
public:
|
|
TarArchive(uint8_t* bytes, intptr_t bytes_length)
|
|
: rs_(bytes, bytes_length) {}
|
|
|
|
void Read() {
|
|
while (HasNext()) {
|
|
char* filename;
|
|
uint8_t* data;
|
|
intptr_t data_length;
|
|
if (Next(&filename, &data, &data_length)) {
|
|
filenames_.Add(filename);
|
|
contents_.Add(data);
|
|
content_lengths_.Add(data_length);
|
|
}
|
|
}
|
|
}
|
|
|
|
char* NextFilename() { return filenames_.RemoveLast(); }
|
|
|
|
uint8_t* NextContent() { return contents_.RemoveLast(); }
|
|
|
|
intptr_t NextContentLength() { return content_lengths_.RemoveLast(); }
|
|
|
|
bool HasMore() const { return filenames_.length() > 0; }
|
|
|
|
intptr_t Length() const { return filenames_.length(); }
|
|
|
|
private:
|
|
enum TarHeaderFields {
|
|
kTarHeaderFilenameOffset = 0,
|
|
kTarHeaderFilenameSize = 100,
|
|
kTarHeaderSizeOffset = 124,
|
|
kTarHeaderSizeSize = 12,
|
|
kTarHeaderTypeOffset = 156,
|
|
kTarHeaderTypeSize = 1,
|
|
kTarHeaderSize = 512,
|
|
};
|
|
|
|
enum TarType {
|
|
kTarAregType = '\0',
|
|
kTarRegType = '0',
|
|
kTarLnkType = '1',
|
|
kTarSymType = '2',
|
|
kTarChrType = '3',
|
|
kTarBlkType = '4',
|
|
kTarDirType = '5',
|
|
kTarFifoType = '6',
|
|
kTarContType = '7',
|
|
kTarXhdType = 'x',
|
|
kTarXglType = 'g',
|
|
};
|
|
|
|
bool HasNext() const { return !EndOfArchive(); }
|
|
|
|
bool Next(char** filename, uint8_t** data, intptr_t* data_length) {
|
|
intptr_t startOfBlock = rs_.Position();
|
|
*filename = ReadFilename();
|
|
rs_.SetPosition(startOfBlock + kTarHeaderSizeOffset);
|
|
intptr_t size = ReadSize();
|
|
rs_.SetPosition(startOfBlock + kTarHeaderTypeOffset);
|
|
TarType type = ReadType();
|
|
SeekToNextBlock(kTarHeaderSize);
|
|
if ((type != kTarRegType) && (type != kTarAregType)) {
|
|
SkipContents(size);
|
|
return false;
|
|
}
|
|
ReadContents(data, size);
|
|
*data_length = size;
|
|
return true;
|
|
}
|
|
|
|
void SeekToNextBlock(intptr_t blockSize) {
|
|
intptr_t remainder = blockSize - (rs_.Position() % blockSize);
|
|
rs_.Advance(remainder);
|
|
}
|
|
|
|
uint8_t PeekByte(intptr_t i) const {
|
|
return *(rs_.AddressOfCurrentPosition() + i);
|
|
}
|
|
|
|
bool EndOfArchive() const {
|
|
if (rs_.PendingBytes() < (kTarHeaderSize * 2)) {
|
|
return true;
|
|
}
|
|
for (intptr_t i = 0; i < (kTarHeaderSize * 2); i++) {
|
|
if (PeekByte(i) != 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TarType ReadType() {
|
|
return static_cast<TarType>(ReadStream::Raw<1, uint8_t>::Read(&rs_));
|
|
}
|
|
|
|
void SkipContents(intptr_t size) {
|
|
rs_.Advance(size);
|
|
SeekToNextBlock(kTarHeaderSize);
|
|
}
|
|
|
|
intptr_t ReadCString(char** s, intptr_t length) {
|
|
intptr_t to_read = Utils::Minimum(length, rs_.PendingBytes());
|
|
char* result = new char[to_read + 1];
|
|
strncpy(result,
|
|
reinterpret_cast<const char*>(rs_.AddressOfCurrentPosition()),
|
|
to_read);
|
|
result[to_read] = '\0';
|
|
rs_.SetPosition(rs_.Position() + to_read);
|
|
*s = result;
|
|
return to_read;
|
|
}
|
|
|
|
intptr_t ReadSize() {
|
|
char* octalSize;
|
|
unsigned int size;
|
|
|
|
ReadCString(&octalSize, kTarHeaderSizeSize);
|
|
int result = sscanf(octalSize, "%o", &size);
|
|
delete[] octalSize;
|
|
|
|
if (result != 1) {
|
|
return 0;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
char* ReadFilename() {
|
|
char* result;
|
|
intptr_t result_length = ReadCString(&result, kTarHeaderFilenameSize);
|
|
if (result[0] == '/') {
|
|
return result;
|
|
}
|
|
char* fixed_result = new char[result_length + 2]; // '/' + '\0'.
|
|
fixed_result[0] = '/';
|
|
strncpy(&fixed_result[1], result, result_length);
|
|
fixed_result[result_length + 1] = '\0';
|
|
delete[] result;
|
|
return fixed_result;
|
|
}
|
|
|
|
void ReadContents(uint8_t** data, intptr_t size) {
|
|
uint8_t* result = new uint8_t[size];
|
|
rs_.ReadBytes(result, size);
|
|
SeekToNextBlock(kTarHeaderSize);
|
|
*data = result;
|
|
}
|
|
|
|
ReadStream rs_;
|
|
GrowableArray<char*> filenames_;
|
|
GrowableArray<uint8_t*> contents_;
|
|
GrowableArray<intptr_t> content_lengths_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(TarArchive);
|
|
};
|
|
|
|
static void ContentsFinalizer(void* isolate_callback_data,
|
|
Dart_WeakPersistentHandle handle,
|
|
void* peer) {
|
|
uint8_t* data = reinterpret_cast<uint8_t*>(peer);
|
|
delete[] data;
|
|
}
|
|
|
|
static void FilenameFinalizer(void* isolate_callback_data,
|
|
Dart_WeakPersistentHandle handle,
|
|
void* peer) {
|
|
char* filename = reinterpret_cast<char*>(peer);
|
|
delete[] filename;
|
|
}
|
|
|
|
#endif
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_DecodeAssets, 0, 1) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(TypedData, data, arguments->NativeArgAt(0));
|
|
Api::Scope scope(thread);
|
|
Dart_Handle data_handle = Api::NewHandle(thread, data.raw());
|
|
Dart_Handle result_list;
|
|
{
|
|
TransitionVMToNative transition(thread);
|
|
|
|
Dart_TypedData_Type typ;
|
|
void* bytes;
|
|
intptr_t length;
|
|
Dart_Handle err =
|
|
Dart_TypedDataAcquireData(data_handle, &typ, &bytes, &length);
|
|
ASSERT(!Dart_IsError(err));
|
|
|
|
TarArchive archive(reinterpret_cast<uint8_t*>(bytes), length);
|
|
archive.Read();
|
|
|
|
err = Dart_TypedDataReleaseData(data_handle);
|
|
ASSERT(!Dart_IsError(err));
|
|
|
|
intptr_t archive_size = archive.Length();
|
|
|
|
result_list = Dart_NewList(2 * archive_size);
|
|
ASSERT(!Dart_IsError(result_list));
|
|
|
|
intptr_t idx = 0;
|
|
while (archive.HasMore()) {
|
|
char* filename = archive.NextFilename();
|
|
intptr_t filename_length = strlen(filename);
|
|
uint8_t* contents = archive.NextContent();
|
|
intptr_t contents_length = archive.NextContentLength();
|
|
|
|
Dart_Handle dart_filename = Dart_NewExternalLatin1String(
|
|
reinterpret_cast<uint8_t*>(filename), filename_length, filename,
|
|
filename_length, FilenameFinalizer);
|
|
ASSERT(!Dart_IsError(dart_filename));
|
|
|
|
Dart_Handle dart_contents = Dart_NewExternalTypedDataWithFinalizer(
|
|
Dart_TypedData_kUint8, contents, contents_length, contents,
|
|
contents_length, ContentsFinalizer);
|
|
ASSERT(!Dart_IsError(dart_contents));
|
|
|
|
Dart_ListSetAt(result_list, idx, dart_filename);
|
|
Dart_ListSetAt(result_list, (idx + 1), dart_contents);
|
|
idx += 2;
|
|
}
|
|
}
|
|
return Api::UnwrapArrayHandle(thread->zone(), result_list).raw();
|
|
#else
|
|
return Object::null();
|
|
#endif
|
|
}
|
|
|
|
DEFINE_NATIVE_ENTRY(VMService_spawnUriNotify, 0, 2) {
|
|
#ifndef PRODUCT
|
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, result, arguments->NativeArgAt(0));
|
|
GET_NON_NULL_NATIVE_ARGUMENT(String, token, arguments->NativeArgAt(1));
|
|
|
|
if (result.IsSendPort()) {
|
|
Dart_Port id = SendPort::Cast(result).Id();
|
|
Isolate* isolate = PortMap::GetIsolate(id);
|
|
if (isolate != NULL) {
|
|
ServiceEvent spawn_event(isolate, ServiceEvent::kIsolateSpawn);
|
|
spawn_event.set_spawn_token(&token);
|
|
Service::HandleEvent(&spawn_event);
|
|
} else {
|
|
// There is no isolate at the control port anymore. Must have
|
|
// died already.
|
|
ServiceEvent spawn_event(NULL, ServiceEvent::kIsolateSpawn);
|
|
const String& error = String::Handle(
|
|
String::New("spawned isolate exited before notification completed"));
|
|
spawn_event.set_spawn_token(&token);
|
|
spawn_event.set_spawn_error(&error);
|
|
Service::HandleEvent(&spawn_event);
|
|
}
|
|
} else {
|
|
// The isolate failed to spawn.
|
|
ASSERT(result.IsString());
|
|
ServiceEvent spawn_event(NULL, ServiceEvent::kIsolateSpawn);
|
|
spawn_event.set_spawn_token(&token);
|
|
spawn_event.set_spawn_error(&String::Cast(result));
|
|
Service::HandleEvent(&spawn_event);
|
|
}
|
|
#endif // PRODUCT
|
|
return Object::null();
|
|
}
|
|
|
|
} // namespace dart
|