| // 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. |
| |
| // Generate a snapshot file after loading all the scripts specified on the |
| // command line. |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| |
| #include "include/dart_api.h" |
| |
| #include "bin/builtin.h" |
| #include "bin/dartutils.h" |
| #include "bin/file.h" |
| #include "bin/log.h" |
| |
| #include "platform/globals.h" |
| |
| |
| namespace dart { |
| namespace bin { |
| |
| #define CHECK_RESULT(result) \ |
| if (Dart_IsError(result)) { \ |
| free(snapshot_buffer); \ |
| Log::PrintErr("Error: %s", Dart_GetError(result)); \ |
| Dart_ExitScope(); \ |
| Dart_ShutdownIsolate(); \ |
| exit(255); \ |
| } \ |
| |
| |
| // Global state that indicates whether a snapshot is to be created and |
| // if so which file to write the snapshot into. |
| static const char* snapshot_filename = NULL; |
| static const char* package_root = NULL; |
| static uint8_t* snapshot_buffer = NULL; |
| |
| |
| // Global state which contains a pointer to the script name for which |
| // a snapshot needs to be created (NULL would result in the creation |
| // of a generic snapshot that contains only the corelibs). |
| static char* app_script_name = NULL; |
| |
| |
| // Global state that captures the URL mappings specified on the command line. |
| static CommandLineOptions* url_mapping = 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 const char* ProcessOption(const char* option, const char* name) { |
| const intptr_t length = strlen(name); |
| if (strncmp(option, name, length) == 0) { |
| return (option + length); |
| } |
| return NULL; |
| } |
| |
| |
| static bool ProcessSnapshotOption(const char* option) { |
| const char* name = ProcessOption(option, "--snapshot="); |
| if (name != NULL) { |
| snapshot_filename = name; |
| return true; |
| } |
| return false; |
| } |
| |
| |
| static bool ProcessPackageRootOption(const char* option) { |
| const char* name = ProcessOption(option, "--package_root="); |
| if (name != NULL) { |
| package_root = name; |
| return true; |
| } |
| return false; |
| } |
| |
| |
| static bool ProcessURLmappingOption(const char* option) { |
| const char* mapping = ProcessOption(option, "--url_mapping="); |
| if (mapping == NULL) { |
| mapping = ProcessOption(option, "--url-mapping="); |
| } |
| if (mapping != NULL) { |
| url_mapping->AddArgument(mapping); |
| return true; |
| } |
| 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) { |
| const char* kPrefix = "--"; |
| const intptr_t kPrefixLen = strlen(kPrefix); |
| |
| // Skip the binary name. |
| int i = 1; |
| |
| // Parse out the vm options. |
| while ((i < argc) && IsValidFlag(argv[i], kPrefix, kPrefixLen)) { |
| if (ProcessSnapshotOption(argv[i]) || |
| ProcessURLmappingOption(argv[i]) || |
| ProcessPackageRootOption(argv[i])) { |
| i += 1; |
| continue; |
| } |
| vm_options->AddArgument(argv[i]); |
| i += 1; |
| } |
| |
| // Get the script name. |
| if (i < argc) { |
| *script_name = argv[i]; |
| i += 1; |
| } else { |
| *script_name = NULL; |
| } |
| |
| if (snapshot_filename == NULL) { |
| Log::PrintErr("No snapshot output file specified.\n\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| |
| static void WriteSnapshotFile(const uint8_t* buffer, const intptr_t size) { |
| File* file = File::Open(snapshot_filename, File::kWriteTruncate); |
| ASSERT(file != NULL); |
| for (intptr_t i = 0; i < size; i++) { |
| file->WriteByte(buffer[i]); |
| } |
| delete file; |
| } |
| |
| |
| class UriResolverIsolateScope { |
| public: |
| UriResolverIsolateScope() { |
| ASSERT(isolate != NULL); |
| snapshotted_isolate_ = Dart_CurrentIsolate(); |
| Dart_ExitIsolate(); |
| Dart_EnterIsolate(isolate); |
| Dart_EnterScope(); |
| } |
| |
| ~UriResolverIsolateScope() { |
| ASSERT(snapshotted_isolate_ != NULL); |
| Dart_ExitScope(); |
| Dart_ExitIsolate(); |
| Dart_EnterIsolate(snapshotted_isolate_); |
| } |
| |
| static Dart_Isolate isolate; |
| |
| private: |
| Dart_Isolate snapshotted_isolate_; |
| |
| DISALLOW_COPY_AND_ASSIGN(UriResolverIsolateScope); |
| }; |
| |
| Dart_Isolate UriResolverIsolateScope::isolate = NULL; |
| |
| |
| static Dart_Handle ResolveScriptUri(const char* script_uri) { |
| bool failed = false; |
| char* result_string = NULL; |
| |
| { |
| UriResolverIsolateScope scope; |
| |
| // Run DartUtils::ResolveScriptUri in context of uri resolver isolate. |
| Dart_Handle builtin_lib = |
| Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary); |
| CHECK_RESULT(builtin_lib); |
| |
| Dart_Handle result = DartUtils::ResolveScriptUri( |
| DartUtils::NewString(script_uri), builtin_lib); |
| if (Dart_IsError(result)) { |
| failed = true; |
| result_string = strdup(Dart_GetError(result)); |
| } else { |
| result_string = strdup(DartUtils::GetStringValue(result)); |
| } |
| } |
| |
| Dart_Handle result = failed ? Dart_NewApiError(result_string) : |
| DartUtils::NewString(result_string); |
| free(result_string); |
| return result; |
| } |
| |
| |
| static Dart_Handle FilePathFromUri(const char* script_uri) { |
| bool failed = false; |
| char* result_string = NULL; |
| |
| { |
| UriResolverIsolateScope scope; |
| |
| // Run DartUtils::FilePathFromUri in context of uri resolver isolate. |
| Dart_Handle builtin_lib = |
| Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary); |
| CHECK_RESULT(builtin_lib); |
| |
| Dart_Handle result = DartUtils::FilePathFromUri( |
| DartUtils::NewString(script_uri), builtin_lib); |
| if (Dart_IsError(result)) { |
| failed = true; |
| result_string = strdup(Dart_GetError(result)); |
| } else { |
| result_string = strdup(DartUtils::GetStringValue(result)); |
| } |
| } |
| |
| Dart_Handle result = failed ? Dart_NewApiError(result_string) : |
| DartUtils::NewString(result_string); |
| free(result_string); |
| return result; |
| } |
| |
| |
| static Dart_Handle ResolveUri(const char* library_uri, const char* uri) { |
| bool failed = false; |
| char* result_string = NULL; |
| |
| { |
| UriResolverIsolateScope scope; |
| |
| // Run DartUtils::ResolveUri in context of uri resolver isolate. |
| Dart_Handle builtin_lib = |
| Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary); |
| CHECK_RESULT(builtin_lib); |
| |
| Dart_Handle result = DartUtils::ResolveUri( |
| DartUtils::NewString(library_uri), |
| DartUtils::NewString(uri), |
| builtin_lib); |
| if (Dart_IsError(result)) { |
| failed = true; |
| result_string = strdup(Dart_GetError(result)); |
| } else { |
| result_string = strdup(DartUtils::GetStringValue(result)); |
| } |
| } |
| |
| Dart_Handle result = failed ? Dart_NewApiError(result_string) : |
| DartUtils::NewString(result_string); |
| free(result_string); |
| return result; |
| } |
| |
| |
| static Dart_Handle CreateSnapshotLibraryTagHandler(Dart_LibraryTag tag, |
| Dart_Handle library, |
| Dart_Handle url) { |
| if (!Dart_IsLibrary(library)) { |
| return Dart_Error("not a library"); |
| } |
| Dart_Handle library_url = Dart_LibraryUrl(library); |
| if (Dart_IsError(library_url)) { |
| return Dart_Error("accessing library url failed"); |
| } |
| const char* library_url_string = DartUtils::GetStringValue(library_url); |
| const char* mapped_library_url_string = DartUtils::MapLibraryUrl( |
| url_mapping, library_url_string); |
| if (mapped_library_url_string != NULL) { |
| library_url = ResolveScriptUri(mapped_library_url_string); |
| library_url_string = DartUtils::GetStringValue(library_url); |
| } |
| |
| if (!Dart_IsString(url)) { |
| return Dart_Error("url is not a string"); |
| } |
| const char* url_string = DartUtils::GetStringValue(url); |
| const char* mapped_url_string = DartUtils::MapLibraryUrl(url_mapping, |
| url_string); |
| |
| if (tag == kCanonicalizeUrl) { |
| // Keep original names for libraries that have a mapping (e.g. dart:io). |
| if (mapped_url_string) { |
| return url; |
| } |
| return ResolveUri(library_url_string, url_string); |
| } |
| |
| if (DartUtils::IsDartIOLibURL(url_string) && mapped_url_string == NULL) { |
| // No url mapping for dart:io. Load original version. |
| return Builtin::LoadAndCheckLibrary(Builtin::kIOLibrary); |
| } |
| |
| Dart_Handle resolved_url = url; |
| if (mapped_url_string != NULL) { |
| // Mapped urls are relative to working directory. |
| resolved_url = ResolveScriptUri(mapped_url_string); |
| if (Dart_IsError(resolved_url)) { |
| return resolved_url; |
| } |
| } |
| |
| // Get the file path out of the url. |
| Dart_Handle file_path = FilePathFromUri( |
| DartUtils::GetStringValue(resolved_url)); |
| if (Dart_IsError(file_path)) { |
| return file_path; |
| } |
| |
| return DartUtils::LoadSource(NULL, |
| library, |
| url, |
| tag, |
| DartUtils::GetStringValue(file_path)); |
| } |
| |
| |
| static Dart_Handle LoadSnapshotCreationScript(const char* script_name) { |
| Dart_Handle resolved_script_uri = ResolveScriptUri(script_name); |
| if (Dart_IsError(resolved_script_uri)) { |
| return resolved_script_uri; |
| } |
| Dart_Handle script_path = FilePathFromUri( |
| DartUtils::GetStringValue(resolved_script_uri)); |
| if (Dart_IsError(script_path)) { |
| return script_path; |
| } |
| Dart_Handle source = DartUtils::ReadStringFromFile( |
| DartUtils::GetStringValue(script_path)); |
| if (Dart_IsError(source)) { |
| return source; |
| } |
| return Dart_LoadScript(resolved_script_uri, source, 0, 0); |
| } |
| |
| |
| static Dart_Handle LoadGenericSnapshotCreationScript( |
| Builtin::BuiltinLibraryId id) { |
| Dart_Handle source = Builtin::Source(id); |
| if (Dart_IsError(source)) { |
| return source; // source contains the error string. |
| } |
| Dart_Handle lib; |
| // Load the builtin library to make it available in the snapshot |
| // for importing. |
| lib = Builtin::LoadAndCheckLibrary(id); |
| ASSERT(!Dart_IsError(lib)); |
| return lib; |
| } |
| |
| |
| static void PrintUsage() { |
| Log::PrintErr( |
| "Usage:\n" |
| "\n" |
| " gen_snapshot [<vm-flags>] [<options>] \\\n" |
| " --snapshot=<out-file> [<dart-script-file>]\n" |
| "\n" |
| " Writes a snapshot of <dart-script-file> to <out-file>. If no\n" |
| " <dart-script-file> is passed, a generic snapshot of all the corelibs is\n" |
| " created. It is required to specify an output file name:\n" |
| "\n" |
| " --snapshot=<file> Generates a complete snapshot. Uses the url\n" |
| " mapping specified on the command line to load\n" |
| " the libraries.\n" |
| "Supported options:\n" |
| "\n" |
| "--package_root=<path>\n" |
| " Where to find packages, that is, \"package:...\" imports.\n" |
| "\n" |
| "--url_mapping=<mapping>\n" |
| " Uses the URL mapping(s) specified on the command line to load the\n" |
| " libraries. For use only with --snapshot=.\n"); |
| } |
| |
| |
| static void VerifyLoaded(Dart_Handle library) { |
| if (Dart_IsError(library)) { |
| const char* err_msg = Dart_GetError(library); |
| Log::PrintErr("Errors encountered while loading: %s\n", err_msg); |
| Dart_ExitScope(); |
| Dart_ShutdownIsolate(); |
| exit(255); |
| } |
| ASSERT(Dart_IsLibrary(library)); |
| } |
| |
| |
| static void CreateAndWriteSnapshot() { |
| Dart_Handle result; |
| uint8_t* buffer = NULL; |
| intptr_t size = 0; |
| |
| // First create a snapshot. |
| result = Dart_CreateSnapshot(&buffer, &size); |
| CHECK_RESULT(result); |
| |
| // Now write the snapshot out to specified file and exit. |
| WriteSnapshotFile(buffer, size); |
| Dart_ExitScope(); |
| |
| // Shutdown the isolate. |
| Dart_ShutdownIsolate(); |
| } |
| |
| |
| static void SetupForUriResolution() { |
| // Set up the library tag handler for this isolate. |
| Dart_Handle result = Dart_SetLibraryTagHandler(DartUtils::LibraryTagHandler); |
| if (Dart_IsError(result)) { |
| Log::PrintErr("%s", Dart_GetError(result)); |
| Dart_ExitScope(); |
| Dart_ShutdownIsolate(); |
| exit(255); |
| } |
| // This is a generic dart snapshot which needs builtin library setup. |
| Dart_Handle library = |
| LoadGenericSnapshotCreationScript(Builtin::kBuiltinLibrary); |
| VerifyLoaded(library); |
| } |
| |
| |
| static void SetupForGenericSnapshotCreation() { |
| SetupForUriResolution(); |
| |
| Dart_Handle library = LoadGenericSnapshotCreationScript(Builtin::kIOLibrary); |
| VerifyLoaded(library); |
| } |
| |
| |
| int main(int argc, char** argv) { |
| CommandLineOptions vm_options(argc); |
| |
| // Initialize the URL mapping array. |
| CommandLineOptions url_mapping_array(argc); |
| url_mapping = &url_mapping_array; |
| |
| // Parse command line arguments. |
| if (ParseArguments(argc, |
| argv, |
| &vm_options, |
| &app_script_name) < 0) { |
| PrintUsage(); |
| return 255; |
| } |
| |
| DartUtils::SetOriginalWorkingDirectory(); |
| |
| Dart_SetVMFlags(vm_options.count(), vm_options.arguments()); |
| |
| // Initialize the Dart VM. |
| // Note: We don't expect isolates to be created from dart code during |
| // snapshot generation. |
| if (!Dart_Initialize(NULL, NULL, NULL, NULL, |
| NULL, NULL, NULL)) { |
| Log::PrintErr("VM initialization failed\n"); |
| return 255; |
| } |
| |
| char* error; |
| Dart_Isolate isolate = Dart_CreateIsolate(NULL, NULL, NULL, NULL, &error); |
| if (isolate == NULL) { |
| Log::PrintErr("Error: %s", error); |
| free(error); |
| exit(255); |
| } |
| |
| Dart_Handle result; |
| Dart_Handle library; |
| Dart_EnterScope(); |
| |
| ASSERT(snapshot_filename != NULL); |
| // Load up the script before a snapshot is created. |
| if (app_script_name != NULL) { |
| // This is the case of a custom embedder (e.g: dartium) trying to |
| // create a full snapshot. The current isolate is set up so that we can |
| // invoke the dart uri resolution code like _resolveURI. App script is |
| // loaded into a separate isolate. |
| |
| SetupForUriResolution(); |
| |
| // Get handle to builtin library. |
| Dart_Handle builtin_lib = |
| Builtin::LoadAndCheckLibrary(Builtin::kBuiltinLibrary); |
| CHECK_RESULT(builtin_lib); |
| |
| // Prepare for script loading by setting up the 'print' and 'timer' |
| // closures and setting up 'package root' for URI resolution. |
| result = DartUtils::PrepareForScriptLoading(package_root, builtin_lib); |
| CHECK_RESULT(result); |
| Dart_ExitScope(); |
| |
| UriResolverIsolateScope::isolate = isolate; |
| |
| // Now we create an isolate into which we load all the code that needs to |
| // be in the snapshot. |
| if (Dart_CreateIsolate(NULL, NULL, NULL, NULL, &error) == NULL) { |
| fprintf(stderr, "%s", error); |
| free(error); |
| exit(255); |
| } |
| Dart_EnterScope(); |
| |
| // Set up the library tag handler in such a manner that it will use the |
| // URL mapping specified on the command line to load the libraries. |
| result = Dart_SetLibraryTagHandler(CreateSnapshotLibraryTagHandler); |
| CHECK_RESULT(result); |
| // Load the specified script. |
| library = LoadSnapshotCreationScript(app_script_name); |
| VerifyLoaded(library); |
| CreateAndWriteSnapshot(); |
| |
| Dart_EnterIsolate(UriResolverIsolateScope::isolate); |
| Dart_ShutdownIsolate(); |
| } else { |
| SetupForGenericSnapshotCreation(); |
| CreateAndWriteSnapshot(); |
| } |
| return 0; |
| } |
| |
| } // namespace bin |
| } // namespace dart |
| |
| int main(int argc, char** argv) { |
| return dart::bin::main(argc, argv); |
| } |