Files
sdk/runtime/bin/main.cc
T
Ryan Macnak 4607e38d28 Reapply "Save and restore feedback from JIT."
Ensure ICData is attached to the callee flow graph in the inliner when built from kernel IR, not just from source.

R=fschneider@google.com

Review-Url: https://codereview.chromium.org/2572423004 .
2016-12-16 16:00:17 -08:00

2058 lines
66 KiB
C++

// Copyright (c) 2012, 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "include/dart_api.h"
#include "include/dart_tools_api.h"
#include "bin/builtin.h"
#include "bin/dartutils.h"
#include "bin/directory.h"
#include "bin/embedded_dart_io.h"
#include "bin/eventhandler.h"
#include "bin/extensions.h"
#include "bin/file.h"
#include "bin/isolate_data.h"
#include "bin/loader.h"
#include "bin/log.h"
#include "bin/platform.h"
#include "bin/process.h"
#include "bin/thread.h"
#include "bin/utils.h"
#include "bin/vmservice_impl.h"
#include "platform/globals.h"
#include "platform/hashmap.h"
#include "platform/text_buffer.h"
#if !defined(DART_PRECOMPILER)
#include "zlib/zlib.h"
#endif
namespace dart {
namespace bin {
// vm_isolate_snapshot_buffer points to a snapshot for the vm isolate if we
// link in a snapshot otherwise it is initialized to NULL.
extern const uint8_t* vm_isolate_snapshot_buffer;
// isolate_snapshot_buffer points to a snapshot for an isolate if we link in a
// snapshot otherwise it is initialized to NULL.
extern const uint8_t* isolate_snapshot_buffer;
/**
* Global state used to control and store generation of application snapshots
* An application snapshot can be generated and run using the following
* command
* dart --snapshot-kind=app-jit --snapshot=<app_snapshot_filename>
* <script_uri> [<script_options>]
* To Run the application snapshot generated above, use :
* dart <app_snapshot_filename> [<script_options>]
*/
static bool run_app_snapshot = false;
static const char* snapshot_filename = NULL;
enum SnapshotKind {
kNone,
kScript,
kAppAOT,
kAppJIT,
};
static SnapshotKind gen_snapshot_kind = kNone;
static bool use_dart_frontend = false;
static const char* frontend_filename = NULL;
// Value of the --save-feedback flag.
// (This pointer points into an argv buffer and does not need to be
// free'd.)
static const char* save_feedback_filename = NULL;
// Value of the --load-feedback flag.
// (This pointer points into an argv buffer and does not need to be
// free'd.)
static const char* load_feedback_filename = NULL;
// Value of the --package-root flag.
// (This pointer points into an argv buffer and does not need to be
// free'd.)
static const char* commandline_package_root = NULL;
// Value of the --packages flag.
// (This pointer points into an argv buffer and does not need to be
// free'd.)
static const char* commandline_packages_file = NULL;
// Global flag that is used to indicate that we want to compile all the
// dart functions and not run anything.
static bool compile_all = false;
static bool parse_all = false;
// Global flag that is used to indicate that we want to use blobs/mmap instead
// of assembly/shared libraries for precompilation.
static bool use_blobs = false;
// Global flag that is used to indicate that we want to compile everything in
// the same way as precompilation before main, then continue running in the
// same process.
// Always set this with dart_noopt.
#if defined(DART_PRECOMPILER) && !defined(DART_NO_SNAPSHOT)
static const bool is_noopt = true;
#else
static const bool is_noopt = false;
#endif
extern const char* kPrecompiledVMIsolateSymbolName;
extern const char* kPrecompiledIsolateSymbolName;
extern const char* kPrecompiledInstructionsSymbolName;
extern const char* kPrecompiledDataSymbolName;
// Global flag that is used to indicate that we want to trace resolution of
// URIs and the loading of libraries, parts and scripts.
static bool trace_loading = false;
static Dart_Isolate main_isolate = NULL;
static const char* DEFAULT_VM_SERVICE_SERVER_IP = "localhost";
static const int DEFAULT_VM_SERVICE_SERVER_PORT = 8181;
// VM Service options.
static const char* vm_service_server_ip = DEFAULT_VM_SERVICE_SERVER_IP;
// The 0 port is a magic value which results in the first available port
// being allocated.
static int vm_service_server_port = -1;
// True when we are running in development mode and cross origin security
// checks are disabled.
static bool vm_service_dev_mode = false;
// Exit code indicating an API error.
static const int kApiErrorExitCode = 253;
// Exit code indicating a compilation error.
static const int kCompilationErrorExitCode = 254;
// Exit code indicating an unhandled error that is not a compilation error.
static const int kErrorExitCode = 255;
// Exit code indicating a vm restart request. Never returned to the user.
static const int kRestartRequestExitCode = 1000;
static void ErrorExit(int exit_code, const char* format, ...) {
va_list arguments;
va_start(arguments, format);
Log::VPrintErr(format, arguments);
va_end(arguments);
Dart_ExitScope();
Dart_ShutdownIsolate();
// Terminate process exit-code handler.
Process::TerminateExitCodeHandler();
char* error = Dart_Cleanup();
if (error != NULL) {
Log::PrintErr("VM cleanup failed: %s\n", error);
free(error);
}
EventHandler::Stop();
Platform::Exit(exit_code);
}
// The environment provided through the command line using -D options.
static dart::HashMap* environment = NULL;
static bool IsValidFlag(const char* name,
const char* prefix,
intptr_t prefix_length) {
intptr_t name_length = strlen(name);
return ((name_length > prefix_length) &&
(strncmp(name, prefix, prefix_length) == 0));
}
static bool version_option = false;
static bool ProcessVersionOption(const char* arg,
CommandLineOptions* vm_options) {
if (*arg != '\0') {
return false;
}
version_option = true;
return true;
}
static bool help_option = false;
static bool ProcessHelpOption(const char* arg, CommandLineOptions* vm_options) {
if (*arg != '\0') {
return false;
}
help_option = true;
return true;
}
static bool verbose_option = false;
static bool ProcessVerboseOption(const char* arg,
CommandLineOptions* vm_options) {
if (*arg != '\0') {
return false;
}
verbose_option = true;
return true;
}
static bool ProcessPackageRootOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '-') {
return false;
}
commandline_package_root = arg;
return true;
}
static bool ProcessPackagesOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '-') {
return false;
}
commandline_packages_file = arg;
return true;
}
static bool ProcessSaveFeedbackOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '-') {
return false;
}
save_feedback_filename = arg;
return true;
}
static bool ProcessLoadFeedbackOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '-') {
return false;
}
load_feedback_filename = arg;
return true;
}
static void* GetHashmapKeyFromString(char* key) {
return reinterpret_cast<void*>(key);
}
static bool ExtractPortAndAddress(const char* option_value,
int* out_port,
const char** out_ip,
int default_port,
const char* default_ip) {
// [option_value] has to be one of the following formats:
// - ""
// - ":8181"
// - "=8181"
// - ":8181/192.168.0.1"
// - "=8181/192.168.0.1"
// - "=8181/::1"
if (*option_value == '\0') {
*out_ip = default_ip;
*out_port = default_port;
return true;
}
if ((*option_value != '=') && (*option_value != ':')) {
return false;
}
int port = atoi(option_value + 1);
const char* slash = strstr(option_value, "/");
if (slash == NULL) {
*out_ip = default_ip;
*out_port = port;
return true;
}
*out_ip = slash + 1;
*out_port = port;
return true;
}
static bool ProcessEnvironmentOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '\0') {
// Ignore empty -D option.
Log::PrintErr("No arguments given to -D option, ignoring it\n");
return true;
}
// Split the name=value part of the -Dname=value argument.
const char* equals_pos = strchr(arg, '=');
if (equals_pos == NULL) {
// No equal sign (name without value) currently not supported.
Log::PrintErr("No value given in -D%s option, ignoring it\n", arg);
return true;
}
char* name;
char* value = NULL;
int name_len = equals_pos - arg;
if (name_len == 0) {
Log::PrintErr("No name given in -D%s option, ignoring it\n", arg);
return true;
}
// Split name=value into name and value.
name = reinterpret_cast<char*>(malloc(name_len + 1));
strncpy(name, arg, name_len);
name[name_len] = '\0';
value = strdup(equals_pos + 1);
if (environment == NULL) {
environment = new HashMap(&HashMap::SameStringValue, 4);
}
HashMap::Entry* entry = environment->Lookup(GetHashmapKeyFromString(name),
HashMap::StringHash(name), true);
ASSERT(entry != NULL); // Lookup adds an entry if key not found.
if (entry->value != NULL) {
free(name);
free(entry->value);
}
entry->value = value;
return true;
}
static bool ProcessCompileAllOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg != '\0') {
return false;
}
compile_all = true;
return true;
}
static bool ProcessParseAllOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg != '\0') {
return false;
}
parse_all = true;
return true;
}
static bool ProcessFrontendOption(const char* filename,
CommandLineOptions* vm_options) {
ASSERT(filename != NULL);
if (filename[0] == '\0') {
return false;
}
use_dart_frontend = true;
frontend_filename = filename;
vm_options->AddArgument("--use-dart-frontend");
return true;
}
static bool ProcessUseBlobsOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg != '\0') {
return false;
}
use_blobs = true;
return true;
}
static bool ProcessSnapshotFilenameOption(const char* filename,
CommandLineOptions* vm_options) {
snapshot_filename = filename;
if (gen_snapshot_kind == kNone) {
gen_snapshot_kind = kScript; // Default behavior.
}
return true;
}
static bool ProcessSnapshotKindOption(const char* kind,
CommandLineOptions* vm_options) {
if (strcmp(kind, "script") == 0) {
gen_snapshot_kind = kScript;
return true;
} else if (strcmp(kind, "app-aot") == 0) {
gen_snapshot_kind = kAppAOT;
return true;
} else if (strcmp(kind, "app-jit") == 0) {
gen_snapshot_kind = kAppJIT;
return true;
}
Log::PrintErr(
"Unrecognized snapshot kind: '%s'\nValid kinds are: "
"script, app-aot, app-jit\n",
kind);
return false;
}
static bool ProcessEnableVmServiceOption(const char* option_value,
CommandLineOptions* vm_options) {
ASSERT(option_value != NULL);
if (!ExtractPortAndAddress(
option_value, &vm_service_server_port, &vm_service_server_ip,
DEFAULT_VM_SERVICE_SERVER_PORT, DEFAULT_VM_SERVICE_SERVER_IP)) {
Log::PrintErr(
"unrecognized --enable-vm-service option syntax. "
"Use --enable-vm-service[=<port number>[/<bind address>]]\n");
return false;
}
return true;
}
static bool ProcessDisableServiceOriginCheckOption(
const char* option_value,
CommandLineOptions* vm_options) {
ASSERT(option_value != NULL);
Log::PrintErr(
"WARNING: You are running with the service protocol in an "
"insecure mode.\n");
vm_service_dev_mode = true;
return true;
}
static bool ProcessObserveOption(const char* option_value,
CommandLineOptions* vm_options) {
ASSERT(option_value != NULL);
if (!ExtractPortAndAddress(
option_value, &vm_service_server_port, &vm_service_server_ip,
DEFAULT_VM_SERVICE_SERVER_PORT, DEFAULT_VM_SERVICE_SERVER_IP)) {
Log::PrintErr(
"unrecognized --observe option syntax. "
"Use --observe[=<port number>[/<bind address>]]\n");
return false;
}
// These options should also be documented in the help message.
vm_options->AddArgument("--pause-isolates-on-exit");
vm_options->AddArgument("--pause-isolates-on-unhandled-exceptions");
vm_options->AddArgument("--warn-on-pause-with-no-debugger");
return true;
}
static bool ProcessTraceLoadingOption(const char* arg,
CommandLineOptions* vm_options) {
if (*arg != '\0') {
return false;
}
trace_loading = true;
return true;
}
static bool ProcessHotReloadTestModeOption(const char* arg,
CommandLineOptions* vm_options) {
if (*arg != '\0') {
return false;
}
// Identity reload.
vm_options->AddArgument("--identity_reload");
// Start reloading quickly.
vm_options->AddArgument("--reload_every=4");
// Reload from optimized and unoptimized code.
vm_options->AddArgument("--reload_every_optimized=false");
// Reload less frequently as time goes on.
vm_options->AddArgument("--reload_every_back_off");
// Ensure that every isolate has reloaded once before exiting.
vm_options->AddArgument("--check_reloaded");
return true;
}
static bool ProcessHotReloadRollbackTestModeOption(
const char* arg,
CommandLineOptions* vm_options) {
// Identity reload.
vm_options->AddArgument("--identity_reload");
// Start reloading quickly.
vm_options->AddArgument("--reload_every=4");
// Reload from optimized and unoptimized code.
vm_options->AddArgument("--reload_every_optimized=false");
// Reload less frequently as time goes on.
vm_options->AddArgument("--reload_every_back_off");
// Ensure that every isolate has reloaded once before exiting.
vm_options->AddArgument("--check_reloaded");
// Force all reloads to fail and execute the rollback code.
vm_options->AddArgument("--reload_force_rollback");
return true;
}
extern bool short_socket_read;
extern bool short_socket_write;
static bool ProcessShortSocketReadOption(const char* arg,
CommandLineOptions* vm_options) {
short_socket_read = true;
return true;
}
static bool ProcessShortSocketWriteOption(const char* arg,
CommandLineOptions* vm_options) {
short_socket_write = true;
return true;
}
#if !defined(TARGET_OS_MACOS)
extern const char* commandline_root_certs_file;
extern const char* commandline_root_certs_cache;
static bool ProcessRootCertsFileOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '-') {
return false;
}
if (commandline_root_certs_cache != NULL) {
Log::PrintErr(
"Only one of --root-certs-file and --root-certs-cache "
"may be specified");
return false;
}
commandline_root_certs_file = arg;
return true;
}
static bool ProcessRootCertsCacheOption(const char* arg,
CommandLineOptions* vm_options) {
ASSERT(arg != NULL);
if (*arg == '-') {
return false;
}
if (commandline_root_certs_file != NULL) {
Log::PrintErr(
"Only one of --root-certs-file and --root-certs-cache "
"may be specified");
return false;
}
commandline_root_certs_cache = arg;
return true;
}
#endif // !defined(TARGET_OS_MACOS)
static struct {
const char* option_name;
bool (*process)(const char* option, CommandLineOptions* vm_options);
} main_options[] = {
// Standard options shared with dart2js.
{"-D", ProcessEnvironmentOption},
{"-h", ProcessHelpOption},
{"--help", ProcessHelpOption},
{"--packages=", ProcessPackagesOption},
{"--package-root=", ProcessPackageRootOption},
{"-v", ProcessVerboseOption},
{"--verbose", ProcessVerboseOption},
{"--version", ProcessVersionOption},
// VM specific options to the standalone dart program.
{"--compile_all", ProcessCompileAllOption},
{"--parse_all", ProcessParseAllOption},
{"--dfe=", ProcessFrontendOption},
{"--enable-vm-service", ProcessEnableVmServiceOption},
{"--disable-service-origin-check", ProcessDisableServiceOriginCheckOption},
{"--observe", ProcessObserveOption},
{"--snapshot=", ProcessSnapshotFilenameOption},
{"--snapshot-kind=", ProcessSnapshotKindOption},
{"--use-blobs", ProcessUseBlobsOption},
{"--save-feedback=", ProcessSaveFeedbackOption},
{"--load-feedback=", ProcessLoadFeedbackOption},
{"--trace-loading", ProcessTraceLoadingOption},
{"--hot-reload-test-mode", ProcessHotReloadTestModeOption},
{"--hot-reload-rollback-test-mode", ProcessHotReloadRollbackTestModeOption},
{"--short_socket_read", ProcessShortSocketReadOption},
{"--short_socket_write", ProcessShortSocketWriteOption},
#if !defined(TARGET_OS_MACOS)
{"--root-certs-file=", ProcessRootCertsFileOption},
{"--root-certs-cache=", ProcessRootCertsCacheOption},
#endif // !defined(TARGET_OS_MACOS)
{NULL, NULL}};
static bool ProcessMainOptions(const char* option,
CommandLineOptions* vm_options) {
int i = 0;
const char* name = main_options[0].option_name;
int option_length = strlen(option);
while (name != NULL) {
int length = strlen(name);
if ((option_length >= length) && (strncmp(option, name, length) == 0)) {
if (main_options[i].process(option + length, vm_options)) {
return true;
}
}
i += 1;
name = main_options[i].option_name;
}
return false;
}
// Parse out the command line arguments. Returns -1 if the arguments
// are incorrect, 0 otherwise.
static int ParseArguments(int argc,
char** argv,
CommandLineOptions* vm_options,
char** script_name,
CommandLineOptions* dart_options,
bool* print_flags_seen,
bool* verbose_debug_seen) {
const char* kPrefix = "--";
const intptr_t kPrefixLen = strlen(kPrefix);
// Store the executable name.
Platform::SetExecutableName(argv[0]);
// Start the rest after the executable name.
int i = 1;
// Parse out the vm options.
while (i < argc) {
if (ProcessMainOptions(argv[i], vm_options)) {
i++;
} else {
// Check if this flag is a potentially valid VM flag.
const char* kChecked = "-c";
const char* kPackageRoot = "-p";
if (strncmp(argv[i], kPackageRoot, strlen(kPackageRoot)) == 0) {
if (!ProcessPackageRootOption(argv[i] + strlen(kPackageRoot),
vm_options)) {
i++;
if ((argv[i] == NULL) ||
!ProcessPackageRootOption(argv[i], vm_options)) {
Log::PrintErr("Invalid option specification : '%s'\n", argv[i - 1]);
i++;
break;
}
}
i++;
continue; // '-p' is not a VM flag so don't add to vm options.
} else if (strncmp(argv[i], kChecked, strlen(kChecked)) == 0) {
vm_options->AddArgument("--checked");
i++;
continue; // '-c' is not a VM flag so don't add to vm options.
} else if (!IsValidFlag(argv[i], kPrefix, kPrefixLen)) {
break;
}
// The following two flags are processed by both the embedder and
// the VM.
const char* kPrintFlags1 = "--print-flags";
const char* kPrintFlags2 = "--print_flags";
const char* kVerboseDebug1 = "--verbose_debug";
const char* kVerboseDebug2 = "--verbose-debug";
if ((strncmp(argv[i], kPrintFlags1, strlen(kPrintFlags1)) == 0) ||
(strncmp(argv[i], kPrintFlags2, strlen(kPrintFlags2)) == 0)) {
*print_flags_seen = true;
} else if ((strncmp(argv[i], kVerboseDebug1, strlen(kVerboseDebug1)) ==
0) ||
(strncmp(argv[i], kVerboseDebug2, strlen(kVerboseDebug2)) ==
0)) {
*verbose_debug_seen = true;
}
vm_options->AddArgument(argv[i]);
i++;
}
}
// The arguments to the VM are at positions 1 through i-1 in argv.
Platform::SetExecutableArguments(i, argv);
// Get the script name.
if (i < argc) {
*script_name = argv[i];
i++;
} else {
return -1;
}
// Parse out options to be passed to dart main.
while (i < argc) {
dart_options->AddArgument(argv[i]);
i++;
}
// Verify consistency of arguments.
if ((commandline_package_root != NULL) &&
(commandline_packages_file != NULL)) {
Log::PrintErr(
"Specifying both a packages directory and a packages "
"file is invalid.\n");
return -1;
}
if ((commandline_package_root != NULL) &&
(strlen(commandline_package_root) == 0)) {
Log::PrintErr("Empty package root specified.\n");
return -1;
}
if ((commandline_packages_file != NULL) &&
(strlen(commandline_packages_file) == 0)) {
Log::PrintErr("Empty package file name specified.\n");
return -1;
}
if (is_noopt && gen_snapshot_kind != kNone) {
Log::PrintErr("Generating a snapshot with dart_noopt is invalid.\n");
return -1;
}
if ((gen_snapshot_kind != kNone) && (snapshot_filename == NULL)) {
Log::PrintErr("Generating a snapshot requires a filename (--snapshot).\n");
return -1;
}
if ((gen_snapshot_kind != kNone) && run_app_snapshot) {
Log::PrintErr(
"Specifying an option to generate a snapshot and"
" run using a snapshot is invalid.\n");
return -1;
}
return 0;
}
static Dart_Handle CreateRuntimeOptions(CommandLineOptions* options) {
int options_count = options->count();
Dart_Handle dart_arguments = Dart_NewList(options_count);
if (Dart_IsError(dart_arguments)) {
return dart_arguments;
}
for (int i = 0; i < options_count; i++) {
Dart_Handle argument_value = DartUtils::NewString(options->GetArgument(i));
if (Dart_IsError(argument_value)) {
return argument_value;
}
Dart_Handle result = Dart_ListSetAt(dart_arguments, i, argument_value);
if (Dart_IsError(result)) {
return result;
}
}
return dart_arguments;
}
static Dart_Handle EnvironmentCallback(Dart_Handle name) {
uint8_t* utf8_array;
intptr_t utf8_len;
Dart_Handle result = Dart_Null();
Dart_Handle handle = Dart_StringToUTF8(name, &utf8_array, &utf8_len);
if (Dart_IsError(handle)) {
handle = Dart_ThrowException(
DartUtils::NewDartArgumentError(Dart_GetError(handle)));
} else {
char* name_chars = reinterpret_cast<char*>(malloc(utf8_len + 1));
memmove(name_chars, utf8_array, utf8_len);
name_chars[utf8_len] = '\0';
const char* value = NULL;
if (environment != NULL) {
HashMap::Entry* entry =
environment->Lookup(GetHashmapKeyFromString(name_chars),
HashMap::StringHash(name_chars), false);
if (entry != NULL) {
value = reinterpret_cast<char*>(entry->value);
}
}
if (value != NULL) {
result = Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(value),
strlen(value));
}
free(name_chars);
}
return result;
}
#define CHECK_RESULT(result) \
if (Dart_IsError(result)) { \
*error = strdup(Dart_GetError(result)); \
if (Dart_IsCompilationError(result)) { \
*exit_code = kCompilationErrorExitCode; \
} else if (Dart_IsApiError(result)) { \
*exit_code = kApiErrorExitCode; \
} else if (Dart_IsVMRestartRequest(result)) { \
*exit_code = kRestartRequestExitCode; \
} else { \
*exit_code = kErrorExitCode; \
} \
Dart_ExitScope(); \
Dart_ShutdownIsolate(); \
return NULL; \
}
static void SnapshotOnExitHook(int64_t exit_code);
// Returns true on success, false on failure.
static Dart_Isolate CreateIsolateAndSetupHelper(const char* script_uri,
const char* main,
const char* package_root,
const char* packages_config,
Dart_IsolateFlags* flags,
char** error,
int* exit_code) {
ASSERT(script_uri != NULL);
const bool needs_load_port = true;
#if defined(PRODUCT)
const bool run_service_isolate = needs_load_port;
#else
// Always create the service isolate in DEBUG and RELEASE modes for profiling,
// even if we don't need it for loading.
const bool run_service_isolate = true;
#endif // PRODUCT
if (!run_service_isolate &&
(strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0)) {
return NULL;
}
if (strcmp(script_uri, DART_KERNEL_ISOLATE_NAME) == 0) {
if (!use_dart_frontend) {
*error = strdup("Kernel isolate not supported.");
return NULL;
} else {
if (packages_config == NULL) {
packages_config = commandline_packages_file;
}
}
}
// If the script is a Kernel binary, then we will try to bootstrap from the
// script.
const uint8_t* kernel_file = NULL;
intptr_t kernel_length = -1;
const bool is_kernel =
!run_app_snapshot &&
TryReadKernel(script_uri, &kernel_file, &kernel_length);
void* kernel_program = NULL;
if (is_kernel) {
kernel_program = Dart_ReadKernelBinary(kernel_file, kernel_length);
free(const_cast<uint8_t*>(kernel_file));
}
IsolateData* isolate_data =
new IsolateData(script_uri, package_root, packages_config);
Dart_Isolate isolate =
is_kernel ? Dart_CreateIsolateFromKernel(script_uri, main, kernel_program,
flags, isolate_data, error)
: Dart_CreateIsolate(script_uri, main, isolate_snapshot_buffer,
flags, isolate_data, error);
if (isolate == NULL) {
delete isolate_data;
return NULL;
}
Dart_EnterScope();
// Set up the library tag handler for this isolate.
Dart_Handle result = Dart_SetLibraryTagHandler(Loader::LibraryTagHandler);
CHECK_RESULT(result);
if (is_kernel) {
Dart_Handle result = Dart_LoadKernel(kernel_program);
CHECK_RESULT(result);
}
if (is_kernel || (isolate_snapshot_buffer != NULL)) {
// Setup the native resolver as the snapshot does not carry it.
Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
Builtin::SetNativeResolver(Builtin::kIOLibrary);
}
if (run_app_snapshot) {
Dart_Handle result = Loader::ReloadNativeExtensions();
CHECK_RESULT(result);
}
if (Dart_IsServiceIsolate(isolate)) {
// If this is the service isolate, load embedder specific bits and return.
bool skip_library_load = run_app_snapshot;
if (!VmService::Setup(vm_service_server_ip, vm_service_server_port,
skip_library_load, vm_service_dev_mode)) {
*error = strdup(VmService::GetErrorMessage());
return NULL;
}
if (compile_all) {
result = Dart_CompileAll();
CHECK_RESULT(result);
}
result = Dart_SetEnvironmentCallback(EnvironmentCallback);
CHECK_RESULT(result);
Dart_ExitScope();
Dart_ExitIsolate();
return isolate;
}
// Prepare builtin and other core libraries for use to resolve URIs.
// Set up various closures, e.g: printing, timers etc.
// Set up 'package root' for URI resolution.
result = DartUtils::PrepareForScriptLoading(false, trace_loading);
CHECK_RESULT(result);
if (needs_load_port) {
// Set up the load port provided by the service isolate so that we can
// load scripts.
result = DartUtils::SetupServiceLoadPort();
CHECK_RESULT(result);
}
if (Dart_IsKernelIsolate(isolate)) {
script_uri = frontend_filename;
}
// Setup package root if specified.
result = DartUtils::SetupPackageRoot(package_root, packages_config);
CHECK_RESULT(result);
result = Dart_SetEnvironmentCallback(EnvironmentCallback);
CHECK_RESULT(result);
if (!Dart_IsKernelIsolate(isolate) && use_dart_frontend) {
// This must be the main script to be loaded. Wait for Kernel isolate
// to finish initialization.
Dart_Port port = Dart_ServiceWaitForKernelPort();
if (port == ILLEGAL_PORT) {
*error = strdup("Error while initializing Kernel isolate");
return NULL;
}
}
if (run_app_snapshot) {
result = DartUtils::SetupIOLibrary(script_uri);
CHECK_RESULT(result);
Loader::InitForSnapshot(script_uri);
} else {
// Load the specified application script into the newly created isolate.
Dart_Handle uri =
DartUtils::ResolveScript(Dart_NewStringFromCString(script_uri));
CHECK_RESULT(uri);
if (!is_kernel) {
result = Loader::LibraryTagHandler(Dart_kScriptTag, Dart_Null(), uri);
CHECK_RESULT(result);
}
Dart_TimelineEvent("LoadScript", Dart_TimelineGetMicros(),
Dart_GetMainPortId(), Dart_Timeline_Event_Async_End, 0,
NULL, NULL);
result = DartUtils::SetupIOLibrary(script_uri);
CHECK_RESULT(result);
}
// Make the isolate runnable so that it is ready to handle messages.
Dart_ExitScope();
Dart_ExitIsolate();
bool retval = Dart_IsolateMakeRunnable(isolate);
if (!retval) {
*error = strdup("Invalid isolate state - Unable to make it runnable");
Dart_EnterIsolate(isolate);
Dart_ShutdownIsolate();
return NULL;
}
return isolate;
}
#undef CHECK_RESULT
static Dart_Isolate CreateIsolateAndSetup(const char* script_uri,
const char* main,
const char* package_root,
const char* package_config,
Dart_IsolateFlags* flags,
void* data,
char** error) {
// The VM should never call the isolate helper with a NULL flags.
ASSERT(flags != NULL);
ASSERT(flags->version == DART_FLAGS_CURRENT_VERSION);
if ((package_root != NULL) && (package_config != NULL)) {
*error = strdup(
"Invalid arguments - Cannot simultaneously specify "
"package root and package map.");
return NULL;
}
int exit_code = 0;
return CreateIsolateAndSetupHelper(script_uri, main, package_root,
package_config, flags, error, &exit_code);
}
static void PrintVersion() {
Log::PrintErr("Dart VM version: %s\n", Dart_VersionString());
}
// clang-format off
static void PrintUsage() {
Log::PrintErr(
"Usage: dart [<vm-flags>] <dart-script-file> [<dart-options>]\n"
"\n"
"Executes the Dart script passed as <dart-script-file>.\n"
"\n");
if (!verbose_option) {
Log::PrintErr(
"Common options:\n"
"--checked or -c\n"
" Insert runtime type checks and enable assertions (checked mode).\n"
"--help or -h\n"
" Display this message (add -v or --verbose for information about\n"
" all VM options).\n"
"--package-root=<path> or -p<path>\n"
" Where to find packages, that is, \"package:...\" imports.\n"
"--packages=<path>\n"
" Where to find a package spec file.\n"
"--observe[=<port>[/<bind-address>]]\n"
" The observe flag is a convenience flag used to run a program with a\n"
" set of options which are often useful for debugging under Observatory.\n"
" These options are currently:\n"
" --enable-vm-service[=<port>[/<bind-address>]]\n"
" --pause-isolates-on-exit\n"
" --pause-isolates-on-unhandled-exceptions\n"
" --warn-on-pause-with-no-debugger\n"
" This set is subject to change.\n"
" Please see these options (--help --verbose) for further documentation.\n"
"--snapshot-kind=<snapsot_kind>\n"
"--snapshot=<file_name>\n"
" These snapshot options are used to generate a snapshot of the loaded\n"
" Dart script:\n"
" <snapshot-kind> controls the kind of snapshot, it could be\n"
" script(default), app-aot or app-jit\n"
" <file_name> specifies the file into which the snapshot is written\n"
"--version\n"
" Print the VM version.\n");
} else {
Log::PrintErr(
"Supported options:\n"
"--checked or -c\n"
" Insert runtime type checks and enable assertions (checked mode).\n"
"--help or -h\n"
" Display this message (add -v or --verbose for information about\n"
" all VM options).\n"
"--package-root=<path> or -p<path>\n"
" Where to find packages, that is, \"package:...\" imports.\n"
"--packages=<path>\n"
" Where to find a package spec file.\n"
"--observe[=<port>[/<bind-address>]]\n"
" The observe flag is a convenience flag used to run a program with a\n"
" set of options which are often useful for debugging under Observatory.\n"
" These options are currently:\n"
" --enable-vm-service[=<port>[/<bind-address>]]\n"
" --pause-isolates-on-exit\n"
" --pause-isolates-on-unhandled-exceptions\n"
" --warn-on-pause-with-no-debugger\n"
" This set is subject to change.\n"
" Please see these options for further documentation.\n"
"--snapshot-kind=<snapsot_kind>\n"
"--snapshot=<file_name>\n"
" These snapshot options are used to generate a snapshot of the loaded\n"
" Dart script:\n"
" <snapshot-kind> controls the kind of snapshot, it could be\n"
" script(default), app-aot or app-jit\n"
" <file_name> specifies the file into which the snapshot is written\n"
"--version\n"
" Print the VM version.\n"
"\n"
"--trace-loading\n"
" enables tracing of library and script loading\n"
"\n"
"--enable-vm-service[=<port>[/<bind-address>]]\n"
" enables the VM service and listens on specified port for connections\n"
" (default port number is 8181, default bind address is localhost).\n"
#if !defined(TARGET_OS_MACOS)
"\n"
"--root-certs-file=<path>\n"
" The path to a file containing the trusted root certificates to use for\n"
" secure socket connections.\n"
"--root-certs-cache=<path>\n"
" The path to a cache directory containing the trusted root certificates to\n"
" use for secure socket connections.\n"
#endif // !defined(TARGET_OS_MACOS)
"\n"
"The following options are only used for VM development and may\n"
"be changed in any future version:\n");
const char* print_flags = "--print_flags";
Dart_SetVMFlags(1, &print_flags);
}
}
// clang-format on
char* BuildIsolateName(const char* script_name, const char* func_name) {
// Skip past any slashes in the script name.
const char* last_slash = strrchr(script_name, '/');
if (last_slash != NULL) {
script_name = last_slash + 1;
}
const char* kFormat = "%s/%s";
intptr_t len = strlen(script_name) + strlen(func_name) + 2;
char* buffer = new char[len];
ASSERT(buffer != NULL);
snprintf(buffer, len, kFormat, script_name, func_name);
return buffer;
}
static void ShutdownIsolate(void* callback_data) {
IsolateData* isolate_data = reinterpret_cast<IsolateData*>(callback_data);
delete isolate_data;
}
static const char* InternalJsonRpcError(Dart_Handle error) {
TextBuffer buffer(128);
buffer.Printf(
"{\"code\":-32603,"
"\"message\":\"Internal error\","
"\"details\": \"%s\"}",
Dart_GetError(error));
return buffer.Steal();
}
class DartScope {
public:
DartScope() { Dart_EnterScope(); }
~DartScope() { Dart_ExitScope(); }
};
static bool ServiceGetIOHandler(const char* method,
const char** param_keys,
const char** param_values,
intptr_t num_params,
void* user_data,
const char** response) {
DartScope scope;
// TODO(ajohnsen): Store the library/function in isolate data or user_data.
Dart_Handle dart_io_str = Dart_NewStringFromCString("dart:io");
if (Dart_IsError(dart_io_str)) {
*response = InternalJsonRpcError(dart_io_str);
return false;
}
Dart_Handle io_lib = Dart_LookupLibrary(dart_io_str);
if (Dart_IsError(io_lib)) {
*response = InternalJsonRpcError(io_lib);
return false;
}
Dart_Handle handler_function_name =
Dart_NewStringFromCString("_serviceObjectHandler");
if (Dart_IsError(handler_function_name)) {
*response = InternalJsonRpcError(handler_function_name);
return false;
}
// TODO(johnmccutchan): paths is no longer used. Update the io
// _serviceObjectHandler function to use json rpc.
Dart_Handle paths = Dart_NewList(0);
Dart_Handle keys = Dart_NewList(num_params);
Dart_Handle values = Dart_NewList(num_params);
for (int i = 0; i < num_params; i++) {
Dart_ListSetAt(keys, i, Dart_NewStringFromCString(param_keys[i]));
Dart_ListSetAt(values, i, Dart_NewStringFromCString(param_values[i]));
}
Dart_Handle args[] = {paths, keys, values};
Dart_Handle result = Dart_Invoke(io_lib, handler_function_name, 3, args);
if (Dart_IsError(result)) {
*response = InternalJsonRpcError(result);
return false;
}
const char* json;
result = Dart_StringToCString(result, &json);
if (Dart_IsError(result)) {
*response = InternalJsonRpcError(result);
return false;
}
*response = strdup(json);
return true;
}
static const char* kStdoutStreamId = "Stdout";
static const char* kStderrStreamId = "Stderr";
static bool ServiceStreamListenCallback(const char* stream_id) {
if (strcmp(stream_id, kStdoutStreamId) == 0) {
SetCaptureStdout(true);
return true;
} else if (strcmp(stream_id, kStderrStreamId) == 0) {
SetCaptureStderr(true);
return true;
}
return false;
}
static void ServiceStreamCancelCallback(const char* stream_id) {
if (strcmp(stream_id, kStdoutStreamId) == 0) {
SetCaptureStdout(false);
} else if (strcmp(stream_id, kStderrStreamId) == 0) {
SetCaptureStderr(false);
}
}
static bool FileModifiedCallback(const char* url, int64_t since) {
if (strncmp(url, "file:///", 8) == 0) {
// If it isn't a file on local disk, we don't know if it has been
// modified.
return true;
}
int64_t data[File::kStatSize];
File::Stat(url + 7, data);
if (data[File::kType] == File::kDoesNotExist) {
return true;
}
bool modified = data[File::kModifiedTime] > since;
return modified;
}
static void WriteSnapshotFile(const char* filename,
bool write_magic_number,
const uint8_t* buffer,
const intptr_t size) {
char* concat = NULL;
File* file = File::Open(filename, File::kWriteTruncate);
if (file == NULL) {
ErrorExit(kErrorExitCode, "Unable to open file %s for writing snapshot\n",
filename);
}
if (write_magic_number) {
// Write the magic number to indicate file is a script snapshot.
DartUtils::WriteMagicNumber(file);
}
if (!file->WriteFully(buffer, size)) {
ErrorExit(kErrorExitCode, "Unable to write file %s for writing snapshot\n",
filename);
}
file->Release();
if (concat != NULL) {
delete concat;
}
}
static const int64_t kAppSnapshotHeaderSize = 5 * sizeof(int64_t); // NOLINT
static const int64_t kAppSnapshotMagicNumber = 0xf6f6dcdc;
static const int64_t kAppSnapshotPageSize = 4 * KB;
static bool ReadAppSnapshotBlobs(const char* script_name,
const uint8_t** vmisolate_buffer,
const uint8_t** isolate_buffer,
const uint8_t** instructions_buffer,
const uint8_t** rodata_buffer) {
File* file = File::Open(script_name, File::kRead);
if (file == NULL) {
return false;
}
if (file->Length() < kAppSnapshotHeaderSize) {
file->Release();
return false;
}
int64_t header[5];
ASSERT(sizeof(header) == kAppSnapshotHeaderSize);
if (!file->ReadFully(&header, kAppSnapshotHeaderSize)) {
file->Release();
return false;
}
if (header[0] != kAppSnapshotMagicNumber) {
file->Release();
return false;
}
int64_t vmisolate_size = header[1];
int64_t vmisolate_position =
Utils::RoundUp(file->Position(), kAppSnapshotPageSize);
int64_t isolate_size = header[2];
int64_t isolate_position =
Utils::RoundUp(vmisolate_position + vmisolate_size, kAppSnapshotPageSize);
int64_t rodata_size = header[3];
int64_t rodata_position = isolate_position + isolate_size;
if (rodata_size != 0) {
rodata_position = Utils::RoundUp(rodata_position, kAppSnapshotPageSize);
}
int64_t instructions_size = header[4];
int64_t instructions_position = rodata_position + rodata_size;
if (instructions_size != 0) {
instructions_position =
Utils::RoundUp(instructions_position, kAppSnapshotPageSize);
}
void* read_only_buffer =
file->Map(File::kReadOnly, vmisolate_position,
instructions_position - vmisolate_position);
if (read_only_buffer == NULL) {
Log::PrintErr("Failed to memory map snapshot\n");
Platform::Exit(kErrorExitCode);
}
if (vmisolate_size != 0) {
*vmisolate_buffer = reinterpret_cast<const uint8_t*>(read_only_buffer) +
(vmisolate_position - vmisolate_position);
}
*isolate_buffer = reinterpret_cast<const uint8_t*>(read_only_buffer) +
(isolate_position - vmisolate_position);
if (rodata_size == 0) {
*rodata_buffer = NULL;
} else {
*rodata_buffer = reinterpret_cast<const uint8_t*>(read_only_buffer) +
(rodata_position - vmisolate_position);
}
if (instructions_size == 0) {
*instructions_buffer = NULL;
} else {
*instructions_buffer = reinterpret_cast<const uint8_t*>(
file->Map(File::kReadExecute, instructions_position, header[4]));
if (*instructions_buffer == NULL) {
Log::PrintErr("Failed to memory map snapshot\n");
Platform::Exit(kErrorExitCode);
}
}
file->Release();
return true;
}
static bool ReadAppSnapshotDynamicLibrary(const char* script_name,
const uint8_t** vmisolate_buffer,
const uint8_t** isolate_buffer,
const uint8_t** instructions_buffer,
const uint8_t** rodata_buffer) {
void* library = Extensions::LoadExtensionLibrary(script_name);
if (library == NULL) {
return false;
}
*vmisolate_buffer = reinterpret_cast<const uint8_t*>(
Extensions::ResolveSymbol(library, kPrecompiledVMIsolateSymbolName));
if (*vmisolate_buffer == NULL) {
Log::PrintErr("Failed to resolve symbol '%s'\n",
kPrecompiledVMIsolateSymbolName);
Platform::Exit(kErrorExitCode);
}
*isolate_buffer = reinterpret_cast<const uint8_t*>(
Extensions::ResolveSymbol(library, kPrecompiledIsolateSymbolName));
if (*isolate_buffer == NULL) {
Log::PrintErr("Failed to resolve symbol '%s'\n",
kPrecompiledIsolateSymbolName);
Platform::Exit(kErrorExitCode);
}
*instructions_buffer = reinterpret_cast<const uint8_t*>(
Extensions::ResolveSymbol(library, kPrecompiledInstructionsSymbolName));
if (*instructions_buffer == NULL) {
Log::PrintErr("Failed to resolve symbol '%s'\n",
kPrecompiledInstructionsSymbolName);
Platform::Exit(kErrorExitCode);
}
*rodata_buffer = reinterpret_cast<const uint8_t*>(
Extensions::ResolveSymbol(library, kPrecompiledDataSymbolName));
if (*rodata_buffer == NULL) {
Log::PrintErr("Failed to resolve symbol '%s'\n",
kPrecompiledDataSymbolName);
Platform::Exit(kErrorExitCode);
}
return true;
}
static bool ReadAppSnapshot(const char* script_name,
const uint8_t** vmisolate_buffer,
const uint8_t** isolate_buffer,
const uint8_t** instructions_buffer,
const uint8_t** rodata_buffer) {
if (File::GetType(script_name, true) != File::kIsFile) {
// If 'script_name' refers to a pipe, don't read to check for an app
// snapshot since we cannot rewind if it isn't (and couldn't mmap it in
// anyway if it was).
return false;
}
if (ReadAppSnapshotBlobs(script_name, vmisolate_buffer, isolate_buffer,
instructions_buffer, rodata_buffer)) {
return true;
}
return ReadAppSnapshotDynamicLibrary(script_name, vmisolate_buffer,
isolate_buffer, instructions_buffer,
rodata_buffer);
}
static bool WriteInt64(File* file, int64_t size) {
return file->WriteFully(&size, sizeof(size));
}
static void WriteAppSnapshot(const char* filename,
uint8_t* vmisolate_buffer,
intptr_t vmisolate_size,
uint8_t* isolate_buffer,
intptr_t isolate_size,
uint8_t* instructions_buffer,
intptr_t instructions_size,
uint8_t* rodata_buffer,
intptr_t rodata_size) {
File* file = File::Open(filename, File::kWriteTruncate);
if (file == NULL) {
ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename);
}
file->WriteFully(&kAppSnapshotMagicNumber, sizeof(kAppSnapshotMagicNumber));
WriteInt64(file, vmisolate_size);
WriteInt64(file, isolate_size);
WriteInt64(file, rodata_size);
WriteInt64(file, instructions_size);
ASSERT(file->Position() == kAppSnapshotHeaderSize);
file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
if (!file->WriteFully(vmisolate_buffer, vmisolate_size)) {
ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename);
}
file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
if (!file->WriteFully(isolate_buffer, isolate_size)) {
ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n", filename);
}
if (rodata_size != 0) {
file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
if (!file->WriteFully(rodata_buffer, rodata_size)) {
ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n",
filename);
}
}
if (instructions_size != 0) {
file->SetPosition(Utils::RoundUp(file->Position(), kAppSnapshotPageSize));
if (!file->WriteFully(instructions_buffer, instructions_size)) {
ErrorExit(kErrorExitCode, "Unable to write snapshot file '%s'\n",
filename);
}
}
file->Flush();
file->Release();
}
static void GenerateScriptSnapshot() {
// First create a snapshot.
uint8_t* buffer = NULL;
intptr_t size = 0;
Dart_Handle result = Dart_CreateScriptSnapshot(&buffer, &size);
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}
WriteSnapshotFile(snapshot_filename, true, buffer, size);
}
static void GeneratePrecompiledSnapshot() {
uint8_t* vm_isolate_buffer = NULL;
intptr_t vm_isolate_size = 0;
uint8_t* isolate_buffer = NULL;
intptr_t isolate_size = 0;
uint8_t* assembly_buffer = NULL;
intptr_t assembly_size = 0;
uint8_t* instructions_blob_buffer = NULL;
intptr_t instructions_blob_size = 0;
uint8_t* rodata_blob_buffer = NULL;
intptr_t rodata_blob_size = 0;
Dart_Handle result;
if (use_blobs) {
result = Dart_CreatePrecompiledSnapshotBlob(
&vm_isolate_buffer, &vm_isolate_size, &isolate_buffer, &isolate_size,
&instructions_blob_buffer, &instructions_blob_size, &rodata_blob_buffer,
&rodata_blob_size);
} else {
result = Dart_CreatePrecompiledSnapshotAssembly(&assembly_buffer,
&assembly_size);
}
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}
if (use_blobs) {
WriteAppSnapshot(snapshot_filename, vm_isolate_buffer, vm_isolate_size,
isolate_buffer, isolate_size, instructions_blob_buffer,
instructions_blob_size, rodata_blob_buffer,
rodata_blob_size);
} else {
WriteSnapshotFile(snapshot_filename, false, assembly_buffer, assembly_size);
}
}
#if defined(TARGET_ARCH_X64)
static void GenerateAppJITSnapshot() {
uint8_t* vm_isolate_buffer = NULL;
intptr_t vm_isolate_size = 0;
uint8_t* isolate_buffer = NULL;
intptr_t isolate_size = 0;
uint8_t* instructions_blob_buffer = NULL;
intptr_t instructions_blob_size = 0;
uint8_t* rodata_blob_buffer = NULL;
intptr_t rodata_blob_size = 0;
Dart_Handle result = Dart_CreateAppJITSnapshot(
&isolate_buffer, &isolate_size, &instructions_blob_buffer,
&instructions_blob_size, &rodata_blob_buffer, &rodata_blob_size);
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}
WriteAppSnapshot(snapshot_filename, vm_isolate_buffer, vm_isolate_size,
isolate_buffer, isolate_size, instructions_blob_buffer,
instructions_blob_size, rodata_blob_buffer,
rodata_blob_size);
}
#endif // defined(TARGET_ARCH_X64)
static void GenerateAppSnapshot() {
Dart_Handle result;
#if defined(TARGET_ARCH_X64)
result = Dart_PrecompileJIT();
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}
GenerateAppJITSnapshot();
#else
// Create an application snapshot of the script.
uint8_t* vm_isolate_buffer = NULL;
intptr_t vm_isolate_size = 0;
uint8_t* isolate_buffer = NULL;
intptr_t isolate_size = 0;
result = Dart_CreateSnapshot(&vm_isolate_buffer, &vm_isolate_size,
&isolate_buffer, &isolate_size);
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}
WriteAppSnapshot(snapshot_filename, vm_isolate_buffer, vm_isolate_size,
isolate_buffer, isolate_size, NULL, 0, NULL, 0);
#endif // defined(TARGET_ARCH_X64)
}
#define CHECK_RESULT(result) \
if (Dart_IsError(result)) { \
if (Dart_IsVMRestartRequest(result)) { \
Dart_ExitScope(); \
Dart_ShutdownIsolate(); \
return true; \
} \
const int exit_code = Dart_IsCompilationError(result) \
? kCompilationErrorExitCode \
: kErrorExitCode; \
ErrorExit(exit_code, "%s\n", Dart_GetError(result)); \
}
static void SnapshotOnExitHook(int64_t exit_code) {
if (Dart_CurrentIsolate() != main_isolate) {
Log::PrintErr(
"A snapshot was requested, but a secondary isolate "
"performed a hard exit (%" Pd64 ").\n",
exit_code);
Platform::Exit(kErrorExitCode);
}
if (exit_code == 0) {
GenerateAppSnapshot();
}
}
bool RunMainIsolate(const char* script_name, CommandLineOptions* dart_options) {
// Call CreateIsolateAndSetup which creates an isolate and loads up
// the specified application script.
char* error = NULL;
int exit_code = 0;
char* isolate_name = BuildIsolateName(script_name, "main");
Dart_Isolate isolate = CreateIsolateAndSetupHelper(
script_name, "main", commandline_package_root, commandline_packages_file,
NULL, &error, &exit_code);
if (isolate == NULL) {
delete[] isolate_name;
if (exit_code == kRestartRequestExitCode) {
free(error);
return true;
}
Log::PrintErr("%s\n", error);
free(error);
error = NULL;
Process::TerminateExitCodeHandler();
error = Dart_Cleanup();
if (error != NULL) {
Log::PrintErr("VM cleanup failed: %s\n", error);
free(error);
}
EventHandler::Stop();
Platform::Exit((exit_code != 0) ? exit_code : kErrorExitCode);
}
main_isolate = isolate;
delete[] isolate_name;
Dart_EnterIsolate(isolate);
ASSERT(isolate == Dart_CurrentIsolate());
ASSERT(isolate != NULL);
Dart_Handle result;
Dart_EnterScope();
if (gen_snapshot_kind == kScript) {
GenerateScriptSnapshot();
} else {
// Lookup the library of the root script.
Dart_Handle root_lib = Dart_RootLibrary();
// Import the root library into the builtin library so that we can easily
// lookup the main entry point exported from the root library.
IsolateData* isolate_data =
reinterpret_cast<IsolateData*>(Dart_IsolateData(isolate));
result = Dart_LibraryImportLibrary(isolate_data->builtin_lib(), root_lib,
Dart_Null());
if (is_noopt || (gen_snapshot_kind == kAppAOT) ||
(gen_snapshot_kind == kAppJIT)) {
// Load the embedder's portion of the VM service's Dart code so it will
// be included in the app snapshot.
if (!VmService::LoadForGenPrecompiled()) {
Log::PrintErr("VM service loading failed: %s\n",
VmService::GetErrorMessage());
exit(kErrorExitCode);
}
}
if (compile_all) {
result = Dart_CompileAll();
CHECK_RESULT(result);
}
if (parse_all) {
result = Dart_ParseAll();
CHECK_RESULT(result);
Dart_ExitScope();
// Shutdown the isolate.
Dart_ShutdownIsolate();
return false;
}
if (is_noopt || (gen_snapshot_kind == kAppAOT)) {
Dart_QualifiedFunctionName standalone_entry_points[] = {
{"dart:_builtin", "::", "_getMainClosure"},
{"dart:_builtin", "::", "_getPrintClosure"},
{"dart:_builtin", "::", "_getUriBaseClosure"},
{"dart:_builtin", "::", "_resolveInWorkingDirectory"},
{"dart:_builtin", "::", "_setWorkingDirectory"},
{"dart:_builtin", "::", "_setPackageRoot"},
{"dart:_builtin", "::", "_setPackagesMap"},
{"dart:_builtin", "::", "_libraryFilePath"},
{"dart:io", "::", "_makeUint8ListView"},
{"dart:io", "::", "_makeDatagram"},
{"dart:io", "::", "_setupHooks"},
{"dart:io", "::", "_getWatchSignalInternal"},
{"dart:io", "CertificateException", "CertificateException."},
{"dart:io", "Directory", "Directory."},
{"dart:io", "File", "File."},
{"dart:io", "FileSystemException", "FileSystemException."},
{"dart:io", "HandshakeException", "HandshakeException."},
{"dart:io", "Link", "Link."},
{"dart:io", "OSError", "OSError."},
{"dart:io", "TlsException", "TlsException."},
{"dart:io", "X509Certificate", "X509Certificate._"},
{"dart:io", "_ExternalBuffer", "set:data"},
{"dart:io", "_ExternalBuffer", "get:start"},
{"dart:io", "_ExternalBuffer", "set:start"},
{"dart:io", "_ExternalBuffer", "get:end"},
{"dart:io", "_ExternalBuffer", "set:end"},
{"dart:io", "_Platform", "set:_nativeScript"},
{"dart:io", "_ProcessStartStatus", "set:_errorCode"},
{"dart:io", "_ProcessStartStatus", "set:_errorMessage"},
{"dart:io", "_SecureFilterImpl", "get:buffers"},
{"dart:io", "_SecureFilterImpl", "get:ENCRYPTED_SIZE"},
{"dart:io", "_SecureFilterImpl", "get:SIZE"},
{"dart:vmservice_io", "::", "main"},
{NULL, NULL, NULL} // Must be terminated with NULL entries.
};
uint8_t* feedback_buffer = NULL;
intptr_t feedback_length = 0;
if (load_feedback_filename != NULL) {
File* file = File::Open(load_feedback_filename, File::kRead);
if (file == NULL) {
ErrorExit(kErrorExitCode, "Failed to read JIT feedback.\n");
}
feedback_length = file->Length();
feedback_buffer = reinterpret_cast<uint8_t*>(malloc(feedback_length));
if (!file->ReadFully(feedback_buffer, feedback_length)) {
ErrorExit(kErrorExitCode, "Failed to read JIT feedback.\n");
}
file->Release();
}
const bool reset_fields = gen_snapshot_kind == kAppAOT;
result = Dart_Precompile(standalone_entry_points, reset_fields,
feedback_buffer, feedback_length);
if (feedback_buffer != NULL) {
free(feedback_buffer);
}
CHECK_RESULT(result);
}
if (gen_snapshot_kind == kAppAOT) {
GeneratePrecompiledSnapshot();
} else {
if (Dart_IsNull(root_lib)) {
ErrorExit(kErrorExitCode, "Unable to find root library for '%s'\n",
script_name);
}
// The helper function _getMainClosure creates a closure for the main
// entry point which is either explicitly or implictly exported from the
// root library.
Dart_Handle main_closure =
Dart_Invoke(isolate_data->builtin_lib(),
Dart_NewStringFromCString("_getMainClosure"), 0, NULL);
CHECK_RESULT(main_closure);
// Call _startIsolate in the isolate library to enable dispatching the
// initial startup message.
const intptr_t kNumIsolateArgs = 2;
Dart_Handle isolate_args[kNumIsolateArgs];
isolate_args[0] = main_closure; // entryPoint
isolate_args[1] = CreateRuntimeOptions(dart_options); // args
Dart_Handle isolate_lib =
Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate"));
result = Dart_Invoke(isolate_lib,
Dart_NewStringFromCString("_startMainIsolate"),
kNumIsolateArgs, isolate_args);
CHECK_RESULT(result);
// Keep handling messages until the last active receive port is closed.
result = Dart_RunLoop();
// Generate an app snapshot after execution if specified.
if (gen_snapshot_kind == kAppJIT) {
if (!Dart_IsCompilationError(result) &&
!Dart_IsVMRestartRequest(result)) {
GenerateAppSnapshot();
}
}
CHECK_RESULT(result);
if (save_feedback_filename != NULL) {
uint8_t* buffer = NULL;
intptr_t size = 0;
result = Dart_SaveJITFeedback(&buffer, &size);
if (Dart_IsError(result)) {
ErrorExit(kErrorExitCode, "%s\n", Dart_GetError(result));
}
WriteSnapshotFile(save_feedback_filename, false, buffer, size);
}
}
}
Dart_ExitScope();
// Shutdown the isolate.
Dart_ShutdownIsolate();
// No restart.
return false;
}
#undef CHECK_RESULT
// Observatory assets are only needed in the regular dart binary.
#if !defined(DART_PRECOMPILER) && !defined(NO_OBSERVATORY)
extern unsigned int observatory_assets_archive_len;
extern const uint8_t* observatory_assets_archive;
// |input| is assumed to be a gzipped stream.
// This function allocates the output buffer in the C heap and the caller
// is responsible for freeing it.
void Decompress(const uint8_t* input,
unsigned int input_len,
uint8_t** output,
unsigned int* output_length) {
ASSERT(input != NULL);
ASSERT(input_len > 0);
ASSERT(output != NULL);
ASSERT(output_length != NULL);
// Initialize output.
*output = NULL;
*output_length = 0;
const unsigned int kChunkSize = 256 * 1024;
uint8_t chunk_out[kChunkSize];
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = 0;
int ret = inflateInit2(&strm, 32 + MAX_WBITS);
ASSERT(ret == Z_OK);
unsigned int input_cursor = 0;
unsigned int output_cursor = 0;
do {
// Setup input.
unsigned int size_in = input_len - input_cursor;
if (size_in > kChunkSize) {
size_in = kChunkSize;
}
strm.avail_in = size_in;
strm.next_in = const_cast<uint8_t*>(&input[input_cursor]);
// Inflate until we've exhausted the current input chunk.
do {
// Setup output.
strm.avail_out = kChunkSize;
strm.next_out = &chunk_out[0];
// Inflate.
ret = inflate(&strm, Z_SYNC_FLUSH);
// We either hit the end of the stream or made forward progress.
ASSERT((ret == Z_STREAM_END) || (ret == Z_OK));
// Grow output buffer size.
unsigned int size_out = kChunkSize - strm.avail_out;
*output_length += size_out;
*output = reinterpret_cast<uint8_t*>(realloc(*output, *output_length));
// Copy output.
memmove(&((*output)[output_cursor]), &chunk_out[0], size_out);
output_cursor += size_out;
} while (strm.avail_out == 0);
// We've processed size_in bytes.
input_cursor += size_in;
// We're finished decompressing when zlib tells us.
} while (ret != Z_STREAM_END);
inflateEnd(&strm);
}
Dart_Handle GetVMServiceAssetsArchiveCallback() {
uint8_t* decompressed = NULL;
unsigned int decompressed_len = 0;
Decompress(observatory_assets_archive, observatory_assets_archive_len,
&decompressed, &decompressed_len);
Dart_Handle tar_file =
DartUtils::MakeUint8Array(decompressed, decompressed_len);
// Free decompressed memory as it has been copied into a Dart array.
free(decompressed);
return tar_file;
}
#else // !defined(DART_PRECOMPILER)
static Dart_GetVMServiceAssetsArchive GetVMServiceAssetsArchiveCallback = NULL;
#endif // !defined(DART_PRECOMPILER)
void main(int argc, char** argv) {
char* script_name;
const int EXTRA_VM_ARGUMENTS = 8;
CommandLineOptions vm_options(argc + EXTRA_VM_ARGUMENTS);
CommandLineOptions dart_options(argc);
bool print_flags_seen = false;
bool verbose_debug_seen = false;
// Perform platform specific initialization.
if (!Platform::Initialize()) {
Log::PrintErr("Initialization failed\n");
}
// On Windows, the argv strings are code page encoded and not
// utf8. We need to convert them to utf8.
bool argv_converted = ShellUtils::GetUtf8Argv(argc, argv);
// Parse command line arguments.
if (ParseArguments(argc, argv, &vm_options, &script_name, &dart_options,
&print_flags_seen, &verbose_debug_seen) < 0) {
if (help_option) {
PrintUsage();
Platform::Exit(0);
} else if (version_option) {
PrintVersion();
Platform::Exit(0);
} else if (print_flags_seen) {
// Will set the VM flags, print them out and then we exit as no
// script was specified on the command line.
Dart_SetVMFlags(vm_options.count(), vm_options.arguments());
Platform::Exit(0);
} else {
PrintUsage();
Platform::Exit(kErrorExitCode);
}
}
Thread::InitOnce();
Loader::InitOnce();
if (!DartUtils::SetOriginalWorkingDirectory()) {
OSError err;
Log::PrintErr("Error determining current directory: %s\n", err.message());
Platform::Exit(kErrorExitCode);
}
const uint8_t* instructions_snapshot = NULL;
const uint8_t* data_snapshot = NULL;
if (ReadAppSnapshot(script_name, &vm_isolate_snapshot_buffer,
&isolate_snapshot_buffer, &instructions_snapshot,
&data_snapshot)) {
run_app_snapshot = true;
}
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
// Constant true if PRODUCT or DART_PRECOMPILED_RUNTIME.
if ((gen_snapshot_kind != kNone) || run_app_snapshot) {
vm_options.AddArgument("--load_deferred_eagerly");
}
#endif
if (gen_snapshot_kind == kAppJIT) {
vm_options.AddArgument("--fields_may_be_reset");
#if !defined(PRODUCT)
vm_options.AddArgument("--collect_code=false");
#endif
}
if ((gen_snapshot_kind == kAppAOT) || is_noopt) {
vm_options.AddArgument("--precompilation");
}
#if defined(DART_PRECOMPILED_RUNTIME)
vm_options.AddArgument("--precompilation");
#endif
if (gen_snapshot_kind == kAppJIT) {
Process::SetExitHook(SnapshotOnExitHook);
}
Dart_SetVMFlags(vm_options.count(), vm_options.arguments());
// Start event handler.
TimerUtils::InitOnce();
EventHandler::Start();
// Initialize the Dart VM.
Dart_InitializeParams init_params;
memset(&init_params, 0, sizeof(init_params));
init_params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
init_params.vm_isolate_snapshot = vm_isolate_snapshot_buffer;
init_params.instructions_snapshot = instructions_snapshot;
init_params.data_snapshot = data_snapshot;
init_params.create = CreateIsolateAndSetup;
init_params.shutdown = ShutdownIsolate;
init_params.file_open = DartUtils::OpenFile;
init_params.file_read = DartUtils::ReadFile;
init_params.file_write = DartUtils::WriteFile;
init_params.file_close = DartUtils::CloseFile;
init_params.entropy_source = DartUtils::EntropySource;
init_params.get_service_assets = GetVMServiceAssetsArchiveCallback;
char* error = Dart_Initialize(&init_params);
if (error != NULL) {
EventHandler::Stop();
Log::PrintErr("VM initialization failed: %s\n", error);
free(error);
Platform::Exit(kErrorExitCode);
}
Dart_RegisterIsolateServiceRequestCallback("getIO", &ServiceGetIOHandler,
NULL);
Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback,
&ServiceStreamCancelCallback);
Dart_SetFileModifiedCallback(&FileModifiedCallback);
// Run the main isolate until we aren't told to restart.
while (RunMainIsolate(script_name, &dart_options)) {
Log::PrintErr("Restarting VM\n");
}
// Terminate process exit-code handler.
Process::TerminateExitCodeHandler();
error = Dart_Cleanup();
if (error != NULL) {
Log::PrintErr("VM cleanup failed: %s\n", error);
free(error);
}
EventHandler::Stop();
// Free copied argument strings if converted.
if (argv_converted) {
for (int i = 0; i < argc; i++) {
free(argv[i]);
}
}
// Free environment if any.
if (environment != NULL) {
for (HashMap::Entry* p = environment->Start(); p != NULL;
p = environment->Next(p)) {
free(p->key);
free(p->value);
}
delete environment;
}
Platform::Exit(Process::GlobalExitCode());
}
} // namespace bin
} // namespace dart
int main(int argc, char** argv) {
dart::bin::main(argc, argv);
UNREACHABLE();
}