| // 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()) { | 
 |   } | 
 |  | 
 |   virtual void VisitIsolate(Isolate* isolate) { | 
 |     ServiceIsolate::RegisterRunningIsolate(isolate); | 
 |   } | 
 |  | 
 |  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 | 
 | } | 
 |  | 
 | }  // namespace dart |