Service isolate requests Observatory assets from embedder

- Provide a new callback to Dart_Initialize that will be called when the service isolate needs the Observatory assets
- Move asset management code out of embedder and into common dart:_vmservice
- Use the tar format as the archive format
- Add build script to generate .cc file containing the archive
- Add a simple tar parser to service isolate

This makes it possible for embedders to not bake Observatory into their executable but instead load it off disk or over the network. The standalone embedder still links Observatory into the executable.

R=rmacnak@google.com

Review URL: https://codereview.chromium.org/1411853003 .
This commit is contained in:
John McCutchan 2015-10-26 07:21:23 -07:00
parent db98c37dac
commit 2844eb86ca
24 changed files with 545 additions and 88 deletions

View file

@ -18,6 +18,8 @@
'bootstrap_resources_cc_file':
'<(gen_source_dir)/bootstrap_resources_gen.cc',
'snapshot_cc_file': '<(gen_source_dir)/snapshot_gen.cc',
'observatory_assets_cc_file': '<(gen_source_dir)/observatory_assets.cc',
'observatory_assets_tar_file': '<(gen_source_dir)/observatory_assets.tar',
},
'targets': [
{
@ -202,6 +204,7 @@
'generate_io_patch_cc_file#host',
'generate_snapshot_file#host',
'generate_resources_cc_file#host',
'generate_observatory_assets_cc_file#host',
],
'sources': [
'builtin_common.cc',
@ -374,6 +377,7 @@
'toolsets':['host'],
'dependencies': [
'generate_resources_cc_file#host',
'generate_observatory_assets_cc_file#host',
'libdart_nosnapshot',
'libdart_builtin',
'libdart_io',
@ -477,12 +481,40 @@
]
},
{
'target_name': 'generate_resources_cc_file',
'target_name': 'generate_observatory_assets_cc_file',
'type': 'none',
'toolsets':['host'],
'dependencies': [
'build_observatory#host',
],
'actions': [
{
'action_name': 'generate_observatory_assets_cc_file',
'inputs': [
'../tools/create_archive.py',
'<(PRODUCT_DIR)/observatory/deployed/web/index.html'
],
'outputs': [
'<(observatory_assets_cc_file)',
],
'action': [
'python',
'tools/create_archive.py',
'--output', '<(observatory_assets_cc_file)',
'--tar_output', '<(observatory_assets_tar_file)',
'--outer_namespace', 'dart',
'--inner_namespace', 'bin',
'--name', 'observatory_assets_archive',
'--client_root', '<(PRODUCT_DIR)/observatory/deployed/web/',
],
'message': 'Generating ''<(observatory_assets_cc_file)'' file.'
},
]
},
{
'target_name': 'generate_resources_cc_file',
'type': 'none',
'toolsets':['host'],
'includes': [
'resources_sources.gypi',
],
@ -491,8 +523,6 @@
'action_name': 'generate_resources_cc',
'inputs': [
'../tools/create_resources.py',
# The following two files are used to trigger a rebuild.
'<(PRODUCT_DIR)/observatory/deployed/web/index.html',
'<@(_sources)',
],
'outputs': [
@ -501,13 +531,11 @@
'action': [
'python',
'tools/create_resources.py',
'--compress',
'--output', '<(resources_cc_file)',
'--outer_namespace', 'dart',
'--inner_namespace', 'bin',
'--table_name', 'service_bin',
'--root_prefix', 'bin/',
'--client_root', '<(PRODUCT_DIR)/observatory/deployed/web/',
'<@(_sources)'
],
'message': 'Generating ''<(resources_cc_file)'' file.'
@ -557,6 +585,7 @@
'build_observatory#host',
'generate_snapshot_file#host',
'generate_resources_cc_file#host',
'generate_observatory_assets_cc_file#host',
],
'include_dirs': [
'..',
@ -572,6 +601,7 @@
'vmservice_impl.h',
'<(snapshot_cc_file)',
'<(resources_cc_file)',
'<(observatory_assets_cc_file)',
],
'conditions': [
['OS=="win"', {
@ -626,6 +656,7 @@
'<(io_cc_file)',
'<(io_patch_cc_file)',
'<(bootstrap_resources_cc_file)',
'observatory_assets_empty.cc',
'snapshot_empty.cc',
],
'conditions': [
@ -661,6 +692,7 @@
'libdart_builtin',
'libdart_io',
'generate_resources_cc_file#host',
'generate_observatory_assets_cc_file#host',
],
'include_dirs': [
'..',
@ -679,6 +711,7 @@
'<(io_cc_file)',
'<(io_patch_cc_file)',
'<(resources_cc_file)',
'<(observatory_assets_cc_file)',
'snapshot_empty.cc',
],
'conditions': [

View file

@ -284,6 +284,27 @@ Dart_Handle DartUtils::ReadStringFromFile(const char* filename) {
}
Dart_Handle DartUtils::MakeUint8Array(const uint8_t* buffer, intptr_t len) {
Dart_Handle array = Dart_NewTypedData(Dart_TypedData_kUint8, len);
RETURN_IF_ERROR(array);
{
Dart_TypedData_Type td_type;
void* td_data;
intptr_t td_len;
Dart_Handle result =
Dart_TypedDataAcquireData(array, &td_type, &td_data, &td_len);
RETURN_IF_ERROR(result);
ASSERT(td_type == Dart_TypedData_kUint8);
ASSERT(td_len == len);
ASSERT(td_data != NULL);
memmove(td_data, buffer, td_len);
result = Dart_TypedDataReleaseData(array);
RETURN_IF_ERROR(result);
}
return array;
}
Dart_Handle DartUtils::SetWorkingDirectory(Dart_Handle builtin_lib) {
Dart_Handle directory = NewString(original_working_directory);
return SingleArgDart_Invoke(builtin_lib, "_setWorkingDirectory", directory);

View file

@ -119,6 +119,7 @@ class DartUtils {
static void CloseFile(void* stream);
static bool EntropySource(uint8_t* buffer, intptr_t length);
static Dart_Handle ReadStringFromFile(const char* filename);
static Dart_Handle MakeUint8Array(const uint8_t* buffer, intptr_t length);
static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag,
Dart_Handle library,
Dart_Handle url);

View file

@ -724,7 +724,7 @@ static bool ParseEntryPointsManifestSingleLine(
// These allocations are collected in |CleanupEntryPointsCollection|.
char* entry_item =
reinterpret_cast<char*>(calloc(chars_read + 1, sizeof(char)));
memcpy(entry_item, line + offset, chars_read);
memmove(entry_item, line + offset, chars_read);
switch (i) {
case 0: // library
@ -1047,7 +1047,8 @@ int main(int argc, char** argv) {
DartUtils::ReadFile,
DartUtils::WriteFile,
DartUtils::CloseFile,
DartUtils::EntropySource);
DartUtils::EntropySource,
NULL);
if (error != NULL) {
Log::PrintErr("VM initialization failed: %s\n", error);
free(error);

View file

@ -1346,6 +1346,15 @@ bool RunMainIsolate(const char* script_name,
#undef CHECK_RESULT
extern unsigned int observatory_assets_archive_len;
extern const char* observatory_assets_archive;
Dart_Handle GetVMServiceAssetsArchiveCallback() {
return DartUtils::MakeUint8Array(
reinterpret_cast<const uint8_t*>(&observatory_assets_archive[0]),
observatory_assets_archive_len);
}
void main(int argc, char** argv) {
char* script_name;
@ -1436,7 +1445,8 @@ void main(int argc, char** argv) {
DartUtils::ReadFile,
DartUtils::WriteFile,
DartUtils::CloseFile,
DartUtils::EntropySource);
DartUtils::EntropySource,
GetVMServiceAssetsArchiveCallback);
if (error != NULL) {
if (do_vm_shutdown) {
DebuggerConnectionHandler::StopHandler();

View file

@ -0,0 +1,24 @@
// 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.
// This file is linked into the dart executable when it does not have
// Observatory baked in.
#if defined(_WIN32)
typedef unsigned __int8 uint8_t;
#else
#include <inttypes.h>
#include <stdint.h>
#endif
#include <stddef.h>
namespace dart {
namespace bin {
static const char observatory_assets_archive_[] = { '\0' };
unsigned int observatory_assets_archive_len = 0;
const char* observatory_assets_archive = observatory_assets_archive_;
} // namespace bin
} // namespace dart

View file

@ -7,11 +7,8 @@
'sources': [
# Standalone VM service sources.
'vmservice/loader.dart',
'vmservice/resources.dart',
'vmservice/server.dart',
'vmservice/vmservice_io.dart',
# We no longer list client sources explicitly and instead include all deployed
# files.
],
}

View file

@ -108,6 +108,7 @@ static int Main(int argc, const char** argv) {
dart::bin::DartUtils::ReadFile,
dart::bin::DartUtils::WriteFile,
dart::bin::DartUtils::CloseFile,
NULL,
NULL);
ASSERT(err_msg == NULL);
// Apply the filter to all registered tests.

View file

@ -1,62 +0,0 @@
// Copyright (c) 2013, 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.
part of vmservice_io;
String detectMimeType(String name) {
var extensionStart = name.lastIndexOf('.');
var extension = name.substring(extensionStart+1);
switch (extension) {
case 'html':
return 'text/html; charset=UTF-8';
case 'dart':
return 'application/dart; charset=UTF-8';
case 'js':
return 'application/javascript; charset=UTF-8';
case 'css':
return 'text/css; charset=UTF-8';
case 'gif':
return 'image/gif';
case 'png':
return 'image/png';
case 'jpg':
return 'image/jpeg';
case 'jpeg':
return 'image/jpeg';
case 'svg':
return 'image/svg+xml';
default:
return 'text/plain';
}
}
class Resource {
final String name;
final String mimeType;
final List<int> data;
Resource(this.name, this.mimeType, this.data);
static final Map<String, Resource> resources = new Map<String, Resource>();
}
ZLibCodec _zlib;
void _addResource(String name, List<int> data, bool compressed) {
var mimeType = detectMimeType(name);
if (compressed) {
if (_zlib == null) {
_zlib = new ZLibCodec();
}
try {
data = _zlib.decode(data);
} catch(e) {
print('error decompressing service isolate resource: $name');
return;
}
}
Resource resource = new Resource(name, mimeType, data);
Resource.resources[name] = resource;
}
void _triggerResourceLoad() native "VMServiceIO_TriggerResourceLoad";

View file

@ -136,12 +136,12 @@ class Server {
return;
}
var resource = Resource.resources[path];
if (resource != null) {
// Serving up a static resource (e.g. .css, .html, .png).
Asset asset = assets[path];
if (asset != null) {
// Serving up a static asset (e.g. .css, .html, .png).
request.response.headers.contentType =
ContentType.parse(resource.mimeType);
request.response.add(resource.data);
ContentType.parse(asset.mimeType);
request.response.add(asset.data);
request.response.close();
return;
}

View file

@ -11,7 +11,6 @@ import 'dart:isolate';
import 'dart:_vmservice';
part 'loader.dart';
part 'resources.dart';
part 'server.dart';
// The TCP ip/port that the HTTP server listens on.
@ -25,9 +24,10 @@ bool _isWindows = false;
var _signalWatch;
var _signalSubscription;
// HTTP servr.
// HTTP server.
Server server;
Future<Server> serverFuture;
Map<String, Asset> assets;
_onShutdown() {
if (server != null) {
@ -42,8 +42,11 @@ _onShutdown() {
}
_bootServer() {
// Load resources.
_triggerResourceLoad();
try {
assets = Asset.request();
} catch (e) {
print('Could not load Observatory assets: $e');
}
// Lazily create service.
var service = new VMService();
service.onShutdown = _onShutdown;

View file

@ -867,6 +867,18 @@ typedef void (*Dart_FileCloseCallback)(void* stream);
typedef bool (*Dart_EntropySource)(uint8_t* buffer, intptr_t length);
/**
* Callback provided by the embedder that is used by the vmservice isolate
* to request the asset archive. The asset archive must be an uncompressed tar
* archive that is stored in a Uint8List.
*
* If the embedder has no vmservice isolate assets, the callback can be NULL.
*
* \return The embedder must return a handle to a Uint8List containing an
* uncompressed tar archive or null.
*/
typedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)();
/**
* Initializes the VM.
*
@ -883,6 +895,10 @@ typedef bool (*Dart_EntropySource)(uint8_t* buffer, intptr_t length);
* \param shutdown A function to be called when an isolate is shutdown.
* See Dart_IsolateShutdownCallback.
*
* \param get_service_assets A function to be called by the service isolate when
* it requires the vmservice assets archive.
* See Dart_GetVMServiceAssetsArchive.
*
* \return NULL if initialization is successful. Returns an error message
* otherwise. The caller is responsible for freeing the error message.
*/
@ -897,7 +913,8 @@ DART_EXPORT char* Dart_Initialize(
Dart_FileReadCallback file_read,
Dart_FileWriteCallback file_write,
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source);
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets);
/**
* Cleanup state in the VM before process termination.

View file

@ -146,4 +146,9 @@ DEFINE_NATIVE_ENTRY(VMService_CancelStream, 1) {
return Object::null();
}
DEFINE_NATIVE_ENTRY(VMService_RequestAssets, 0) {
return Service::RequestAssets();
}
} // namespace dart

View file

@ -10,3 +10,4 @@ patch void _onStart() native "VMService_OnStart";
patch void _onExit() native "VMService_OnExit";
patch bool _vmListenStream(String streamId) native "VMService_ListenStream";
patch void _vmCancelStream(String streamId) native "VMService_CancelStream";
patch Uint8List _requestAssets() native "VMService_RequestAssets";

View file

@ -0,0 +1,140 @@
# 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.
#
# This python script creates a tar archive and a C++ source file which contains
# the tar archive as an array of bytes.
import os
import sys
from os.path import join, splitext
import time
from optparse import OptionParser
from datetime import date
import tarfile
import tempfile
def makeArchive(tar_path, client_root, files):
tar = tarfile.open(tar_path, mode='w')
for input_file_name in files:
# Chop off client_root.
archive_file_name = input_file_name[ len(client_root) : ]
# Open input file and add it to the archive.
with open(input_file_name, 'rb') as input_file:
tarInfo = tarfile.TarInfo(name=archive_file_name)
input_file.seek(0,2)
tarInfo.size = input_file.tell()
input_file.seek(0)
tar.addfile(tarInfo, fileobj=input_file)
tar.close()
def writeCCFile(output_file,
outer_namespace,
inner_namespace,
name,
tar_archive,
):
cc_text = '''
// Copyright (c) %d, 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.
''' % date.today().year
cc_text += 'namespace %s {\n' % outer_namespace
if inner_namespace != None:
cc_text += 'namespace %s {\n' % inner_namespace
cc_text += '\n\n'
# Write the archive.
cc_text += 'static const char %s_[] = {\n ' % name
lineCounter = 0
for byte in tar_archive:
cc_text += r" '\x%02x'," % ord(byte)
lineCounter += 1
if lineCounter == 10:
cc_text += '\n '
lineCounter = 0
if lineCounter != 0:
cc_text += '\n '
cc_text += '};\n'
cc_text += '\nunsigned int %s_len = %d;\n' % (name, len(tar_archive))
cc_text += '\nconst char* %s = %s_;\n' % (name, name)
if inner_namespace != None:
cc_text += '} // namespace %s\n' % inner_namespace
cc_text += '} // namespace %s\n' % outer_namespace
open(output_file, 'w').write(cc_text)
def main(args):
try:
# Parse input.
parser = OptionParser()
parser.add_option("--output",
action="store", type="string",
help="output file name")
parser.add_option("--tar_output",
action="store", type="string",
help="tar output file name")
parser.add_option("--outer_namespace",
action="store", type="string",
help="outer C++ namespace",
default="dart")
parser.add_option("--inner_namespace",
action="store", type="string",
help="inner C++ namespace",
default="bin")
parser.add_option("--name",
action="store", type="string",
help="name of tar archive symbol")
parser.add_option("--client_root",
action="store", type="string",
help="root directory client resources")
(options, args) = parser.parse_args()
if not options.output:
sys.stderr.write('--output not specified\n')
return -1
if not options.tar_output:
sys.stderr.write('--tar_output not specified\n')
return -1
if not options.name:
sys.stderr.write('--name not specified\n')
return -1
if not options.client_root:
sys.stderr.write('--client_root not specified')
return -1
files = [ ]
for dirname, dirnames, filenames in os.walk(options.client_root):
# strip out all dot files.
filenames = [f for f in filenames if not f[0] == '.']
dirnames[:] = [d for d in dirnames if not d[0] == '.']
for f in filenames:
src_path = os.path.join(dirname, f)
if (os.path.isdir(src_path)):
continue
files.append(src_path)
# Write out archive.
makeArchive(options.tar_output, options.client_root, files)
# Read it back in.
with open(options.tar_output, 'rb') as tar_file:
tar_archive = tar_file.read()
# Write CC file.
writeCCFile(options.output,
options.outer_namespace,
options.inner_namespace,
options.name,
tar_archive)
return 0
except Exception, inst:
sys.stderr.write('create_resources.py exception\n')
sys.stderr.write(str(inst))
sys.stderr.write('\n')
return -1
if __name__ == '__main__':
sys.exit(main(sys.argv))

View file

@ -404,6 +404,7 @@ namespace dart {
V(VMService_OnExit, 0) \
V(VMService_ListenStream, 1) \
V(VMService_CancelStream, 1) \
V(VMService_RequestAssets, 0) \
class BootstrapNatives : public AllStatic {
public:

View file

@ -80,7 +80,8 @@ const char* Dart::InitOnce(const uint8_t* vm_isolate_snapshot,
Dart_FileReadCallback file_read,
Dart_FileWriteCallback file_write,
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source) {
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets) {
// TODO(iposva): Fix race condition here.
if (vm_isolate_ != NULL || !Flags::Initialized()) {
return "VM already initialized or flags not initialized.";
@ -201,6 +202,7 @@ const char* Dart::InitOnce(const uint8_t* vm_isolate_snapshot,
Isolate::SetUnhandledExceptionCallback(unhandled);
Isolate::SetShutdownCallback(shutdown);
Service::SetGetServiceAssetsCallback(get_service_assets);
ServiceIsolate::Run();
return NULL;

View file

@ -31,7 +31,8 @@ class Dart : public AllStatic {
Dart_FileReadCallback file_read,
Dart_FileWriteCallback file_write,
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source);
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets);
static const char* Cleanup();
static Isolate* CreateIsolate(const char* name_prefix,

View file

@ -1280,7 +1280,8 @@ DART_EXPORT char* Dart_Initialize(
Dart_FileReadCallback file_read,
Dart_FileWriteCallback file_write,
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source) {
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets) {
if ((instructions_snapshot != NULL) && !FLAG_precompilation) {
return strdup("Flag --precompilation was not specified.");
}
@ -1288,7 +1289,8 @@ DART_EXPORT char* Dart_Initialize(
instructions_snapshot,
create, interrupt, unhandled, shutdown,
file_open, file_read, file_write,
file_close, entropy_source);
file_close, entropy_source,
get_service_assets);
if (err_msg != NULL) {
return strdup(err_msg);
}

View file

@ -103,7 +103,7 @@ ServiceMethodDescriptor* FindMethod(const char* method_name);
// Support for streams defined in embedders.
Dart_ServiceStreamListenCallback Service::stream_listen_callback_ = NULL;
Dart_ServiceStreamCancelCallback Service::stream_cancel_callback_ = NULL;
Dart_GetVMServiceAssetsArchive Service::get_service_assets_callback_ = NULL;
// These are the set of streams known to the core VM.
StreamInfo Service::vm_stream("VM");
@ -144,6 +144,7 @@ bool Service::ListenStream(const char* stream_id) {
return false;
}
void Service::CancelStream(const char* stream_id) {
if (FLAG_trace_service) {
OS::Print("vm-service: stopping stream '%s'\n",
@ -162,11 +163,49 @@ void Service::CancelStream(const char* stream_id) {
}
}
RawObject* Service::RequestAssets() {
Thread* T = Thread::Current();
Api::Scope api_scope(T);
if (get_service_assets_callback_ == NULL) {
return Object::null();
}
Dart_Handle handle = get_service_assets_callback_();
if (Dart_IsError(handle)) {
Dart_PropagateError(handle);
}
const Object& object = Object::Handle(Api::UnwrapHandle(handle));
if (object.IsNull()) {
return Object::null();
}
if (!object.IsTypedData()) {
const String& error_message =
String::Handle(
String::New("An implementation of Dart_GetVMServiceAssetsArchive "
"should return a Uint8Array or null."));
const Error& error = Error::Handle(ApiError::New(error_message));
Exceptions::PropagateError(error);
return Object::null();
}
const TypedData& typed_data = TypedData::Cast(object);
if (typed_data.ElementSizeInBytes() != 1) {
const String& error_message =
String::Handle(
String::New("An implementation of Dart_GetVMServiceAssetsArchive "
"should return a Uint8Array or null."));
const Error& error = Error::Handle(ApiError::New(error_message));
Exceptions::PropagateError(error);
return Object::null();
}
return Api::UnwrapHandle(handle);
}
static uint8_t* allocator(uint8_t* ptr, intptr_t old_size, intptr_t new_size) {
void* new_ptr = realloc(reinterpret_cast<void*>(ptr), new_size);
return reinterpret_cast<uint8_t*>(new_ptr);
}
static void PrintMissingParamError(JSONStream* js,
const char* param) {
js->PrintError(kInvalidParams,
@ -925,6 +964,12 @@ void Service::SetEmbedderStreamCallbacks(
}
void Service::SetGetServiceAssetsCallback(
Dart_GetVMServiceAssetsArchive get_service_assets) {
get_service_assets_callback_ = get_service_assets;
}
EmbedderServiceHandler* Service::FindRootEmbedderHandler(
const char* name) {
EmbedderServiceHandler* current = root_service_handler_head_;

View file

@ -101,6 +101,9 @@ class Service : public AllStatic {
Dart_ServiceStreamListenCallback listen_callback,
Dart_ServiceStreamCancelCallback cancel_callback);
static void SetGetServiceAssetsCallback(
Dart_GetVMServiceAssetsArchive get_service_assets);
static void SendEchoEvent(Isolate* isolate, const char* text);
static void SendGraphEvent(Isolate* isolate);
static void SendInspectEvent(Isolate* isolate, const Object& inspectee);
@ -140,6 +143,8 @@ class Service : public AllStatic {
static bool ListenStream(const char* stream_id);
static void CancelStream(const char* stream_id);
static RawObject* RequestAssets();
static Dart_ServiceStreamListenCallback stream_listen_callback() {
return stream_listen_callback_;
}
@ -182,6 +187,7 @@ class Service : public AllStatic {
static EmbedderServiceHandler* root_service_handler_head_;
static Dart_ServiceStreamListenCallback stream_listen_callback_;
static Dart_ServiceStreamCancelCallback stream_cancel_callback_;
static Dart_GetVMServiceAssetsArchive get_service_assets_callback_;
static bool needs_isolate_events_;
static bool needs_debug_events_;

View file

@ -0,0 +1,205 @@
// 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.
part of dart._vmservice;
class Asset {
final String name;
final Uint8List data;
Asset(this.name, this.data);
String get mimeType {
var extensionStart = name.lastIndexOf('.');
var extension = name.substring(extensionStart+1);
switch (extension) {
case 'html':
return 'text/html; charset=UTF-8';
case 'dart':
return 'application/dart; charset=UTF-8';
case 'js':
return 'application/javascript; charset=UTF-8';
case 'css':
return 'text/css; charset=UTF-8';
case 'gif':
return 'image/gif';
case 'png':
return 'image/png';
case 'jpg':
return 'image/jpeg';
case 'jpeg':
return 'image/jpeg';
case 'svg':
return 'image/svg+xml';
default:
return 'text/plain';
}
}
/// Call to request assets from the embedder.
static Map<String, Asset> request() {
Map<String, Asset> assets = new Map<String, Asset>();
Uint8List tarBytes = _requestAssets();
if (tarBytes == null) {
return assets;
}
_TarArchive archive = new _TarArchive(tarBytes);
while (archive.hasNext()) {
Asset asset = archive.next();
if (asset == null) {
// Skip over special files.
continue;
}
assets[asset.name] = asset;
}
return assets;
}
String toString() => '$name ($mimeType)';
}
class _ByteStream {
final Uint8List bytes;
final int offset;
int get length => bytes.length - offset;
int _cursor = 0;
_ByteStream(this.bytes, [this.offset = 0]);
void reset() {
_cursor = 0;
}
int peekByte([int index = 0]) => bytes[offset + _cursor + index];
int readByte() {
int r = peekByte();
_advance(1);
return r;
}
void skip(int bytes) => _advance(bytes);
void seekToNextBlock(int blockSize) {
int remainder = blockSize - (_cursor % blockSize);
_advance(remainder);
}
void _advance(int bytes) {
_cursor += bytes;
if (_cursor > length) {
_cursor = length;
}
}
int get remaining => length - _cursor;
bool get hasMore => remaining > 0;
int get cursor => _cursor;
void set cursor(int cursor) {
_cursor = cursor;
if (_cursor > length) {
_cursor = length;
}
}
}
class _TarArchive {
static const List<int> tarMagic = const [ 0x75, 0x73, 0x74, 0x61, 0x72, 0 ];
static const List<int> tarVersion = const [ 0x30, 0x30 ];
static const int tarHeaderSize = 512;
static const int tarHeaderFilenameSize = 100;
static const int tarHeaderFilenameOffset = 0;
static const int tarHeaderSizeSize = 12;
static const int tarHeaderSizeOffset = 124;
static const int tarHeaderTypeSize = 1;
static const int tarHeaderTypeOffset = 156;
static const int tarHeaderFileType = 0x30;
static String _readCString(_ByteStream bs, int length) {
StringBuffer sb = new StringBuffer();
int count = 0;
while (bs.hasMore && count < length) {
if (bs.peekByte() == 0) {
// Null character.
break;
}
sb.writeCharCode(bs.readByte());
count++;
}
return sb.toString();
}
static String _readFilename(_ByteStream bs) {
String filename = _readCString(bs, tarHeaderFilenameSize);
if (filename.startsWith('/')) {
return filename;
}
return '/' + filename;
}
static Uint8List _readContents(_ByteStream bs, int size) {
Uint8List result = new Uint8List(size);
int i = 0;
while (bs.hasMore && i < size) {
result[i] = bs.readByte();
i++;
}
bs.seekToNextBlock(tarHeaderSize);
return result;
}
static void _skipContents(_ByteStream bs, int size) {
bs.skip(size);
bs.seekToNextBlock(tarHeaderSize);
}
static int _readSize(_ByteStream bs) {
String octalSize = _readCString(bs, tarHeaderSizeSize);
return int.parse(octalSize,
radix: 8,
onError: (_) => 0);
}
static int _readType(_ByteStream bs) {
return bs.readByte();
}
static bool _endOfArchive(_ByteStream bs) {
if (bs.remaining < (tarHeaderSize * 2)) {
return true;
}
for (int i = 0; i < (tarHeaderSize * 2); i++) {
if (bs.peekByte(i) != 0) {
return false;
}
}
return true;
}
final _ByteStream _bs;
_TarArchive(Uint8List bytes)
: _bs = new _ByteStream(bytes);
bool hasNext() {
return !_endOfArchive(_bs);
}
Asset next() {
int startOfBlock = _bs.cursor;
String filename = _readFilename(_bs);
_bs.cursor = startOfBlock + tarHeaderSizeOffset;
int size = _readSize(_bs);
_bs.cursor = startOfBlock + tarHeaderTypeOffset;
int type = _readType(_bs);
_bs.seekToNextBlock(tarHeaderSize);
if (type != tarHeaderFileType) {
_skipContents(_bs, size);
return null;
}
Uint8List bytes = _readContents(_bs, size);
return new Asset(filename, bytes);
}
}

View file

@ -9,6 +9,7 @@ import 'dart:convert';
import 'dart:isolate';
import 'dart:typed_data';
part 'asset.dart';
part 'client.dart';
part 'constants.dart';
part 'running_isolate.dart';
@ -329,3 +330,4 @@ external bool _vmListenStream(String streamId);
external void _vmCancelStream(String streamId);
external Uint8List _requestAssets();

View file

@ -8,6 +8,7 @@
'sources': [
'vmservice.dart',
# The above file needs to be first as it imports required libraries.
'asset.dart',
'client.dart',
'constants.dart',
'running_isolate.dart',