Reload native extensions when starting from a snapshot.

Fixes #21180
Fixes #27574

R=johnmccutchan@google.com

Review URL: https://codereview.chromium.org/2475523002 .
This commit is contained in:
Ryan Macnak 2016-11-07 16:17:47 -08:00
parent 4b2751a80e
commit 987165f022
13 changed files with 240 additions and 69 deletions

View file

@ -233,6 +233,16 @@ const char* DartUtils::RemoveScheme(const char* url) {
}
char* DartUtils::DirName(const char* url) {
const char* slash = strrchr(url, File::PathSeparator()[0]);
if (slash == NULL) {
return strdup(url);
} else {
return strndup(url, slash - url + 1);
}
}
void* DartUtils::OpenFile(const char* name, bool write) {
File* file = File::Open(name, write ? File::kWriteTruncate : File::kRead);
return reinterpret_cast<void*>(file);

View file

@ -126,6 +126,7 @@ class DartUtils {
static bool IsDartBuiltinLibURL(const char* url_name);
static bool IsHttpSchemeURL(const char* url_name);
static const char* RemoveScheme(const char* url);
static char* DirName(const char* url);
static void* MapExecutable(const char* name, intptr_t* file_len);
static void* OpenFile(const char* name, bool write);
static void ReadFile(const uint8_t** data, intptr_t* file_len, void* stream);

View file

@ -11,6 +11,7 @@
#include "bin/file.h"
#include "bin/lockers.h"
#include "bin/utils.h"
#include "include/dart_tools_api.h"
namespace dart {
namespace bin {
@ -353,6 +354,7 @@ bool Loader::ProcessResultLocked(Loader* loader, Loader::IOResult* result) {
loader->monitor_->Exit();
Dart_Handle dart_result = Dart_Null();
bool reload_extensions = false;
switch (tag) {
case Dart_kImportTag:
@ -367,6 +369,7 @@ bool Loader::ProcessResultLocked(Loader* loader, Loader::IOResult* result) {
case Dart_kScriptTag:
if (payload_type == DartUtils::kSnapshotMagicNumber) {
dart_result = Dart_LoadScriptFromSnapshot(payload, payload_length);
reload_extensions = true;
} else if (payload_type == DartUtils::kKernelMagicNumber) {
dart_result = Dart_LoadKernel(payload, payload_length);
} else {
@ -385,6 +388,15 @@ bool Loader::ProcessResultLocked(Loader* loader, Loader::IOResult* result) {
return false;
}
if (reload_extensions) {
dart_result = ReloadNativeExtensions();
if (Dart_IsError(dart_result)) {
// Remember the error if we encountered one.
loader->error_ = dart_result;
return false;
}
}
return true;
}
@ -437,6 +449,51 @@ void Loader::InitForSnapshot(const char* snapshot_uri) {
}
#define RETURN_ERROR(result) \
if (Dart_IsError(result)) return result;
Dart_Handle Loader::ReloadNativeExtensions() {
Dart_Handle scheme =
Dart_NewStringFromCString(DartUtils::kDartExtensionScheme);
Dart_Handle extension_imports = Dart_GetImportsOfScheme(scheme);
RETURN_ERROR(extension_imports);
intptr_t length = -1;
Dart_Handle result = Dart_ListLength(extension_imports, &length);
RETURN_ERROR(result);
Dart_Handle* import_handles = reinterpret_cast<Dart_Handle*>(
Dart_ScopeAllocate(sizeof(Dart_Handle) * length));
result = Dart_ListGetRange(extension_imports, 0, length, import_handles);
RETURN_ERROR(result);
for (intptr_t i = 0; i < length; i += 2) {
Dart_Handle importer = import_handles[i];
Dart_Handle importee = import_handles[i + 1];
const char* extension_uri = NULL;
result = Dart_StringToCString(Dart_LibraryUrl(importee), &extension_uri);
RETURN_ERROR(result);
const char* extension_path = DartUtils::RemoveScheme(extension_uri);
const char* lib_uri = NULL;
result = Dart_StringToCString(Dart_LibraryUrl(importer), &lib_uri);
RETURN_ERROR(result);
char* lib_path = NULL;
if (strncmp(lib_uri, "file://", 7) == 0) {
lib_path = DartUtils::DirName(DartUtils::RemoveScheme(lib_uri));
} else {
lib_path = strdup(lib_uri);
}
result = Extensions::LoadExtension(lib_path, extension_path, importer);
free(lib_path);
RETURN_ERROR(result);
}
return Dart_True();
}
Dart_Handle Loader::LoadUrlContents(Dart_Handle url,
uint8_t** payload,
intptr_t* payload_length) {

View file

@ -22,6 +22,8 @@ class Loader {
static void InitForSnapshot(const char* snapshot_uri);
static Dart_Handle ReloadNativeExtensions();
// Loads contents of the specified url.
static Dart_Handle LoadUrlContents(Dart_Handle url,
uint8_t** payload,

View file

@ -815,6 +815,10 @@ static Dart_Isolate CreateIsolateAndSetupHelper(const char* script_uri,
Builtin::SetNativeResolver(Builtin::kBuiltinLibrary);
Builtin::SetNativeResolver(Builtin::kIOLibrary);
}
if (run_app_snapshot) {
Dart_Handle result = Loader::ReloadNativeExtensions();
CHECK_RESULT(result);
}
// Set up the library tag handler for this isolate.
Dart_Handle result = Dart_SetLibraryTagHandler(Loader::LibraryTagHandler);

View file

@ -2940,6 +2940,19 @@ DART_EXPORT Dart_Handle Dart_LibraryImportLibrary(Dart_Handle library,
Dart_Handle import,
Dart_Handle prefix);
/**
* Returns a flattened list of pairs. The first element in each pair is the
* importing library and and the second element is the imported library for each
* import in the isolate of a library whose URI's scheme is [scheme].
*
* Requires there to be a current isolate.
*
* \return A handle to a list of flattened pairs of importer-importee.
*/
DART_EXPORT Dart_Handle Dart_GetImportsOfScheme(Dart_Handle scheme);
/**
* Called by the embedder to provide the source for a "part of"
* directive. This function should be called in response to a

View file

@ -156,7 +156,7 @@ class LibraryViewElement extends HtmlElement implements Renderable {
new DivElement()
..classes = ['content-centered-big']
..children = [
new HeadingElement.h2()..text = 'ICData',
new HeadingElement.h2()..text = 'Library',
new HRElement(),
new ObjectCommonElement(_isolate, _library, _retainedSizes,
_reachableSizes, _references, _retainingPaths, _instances,

View file

@ -5718,6 +5718,42 @@ DART_EXPORT Dart_Handle Dart_LibraryImportLibrary(Dart_Handle library,
}
DART_EXPORT Dart_Handle Dart_GetImportsOfScheme(Dart_Handle scheme) {
DARTSCOPE(Thread::Current());
Isolate* I = T->isolate();
const String& scheme_vm = Api::UnwrapStringHandle(Z, scheme);
if (scheme_vm.IsNull()) {
RETURN_TYPE_ERROR(Z, scheme, String);
}
const GrowableObjectArray& libraries =
GrowableObjectArray::Handle(Z, I->object_store()->libraries());
const GrowableObjectArray& result =
GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
Library& importer = Library::Handle(Z);
Array& imports = Array::Handle(Z);
Namespace& ns = Namespace::Handle(Z);
Library& importee = Library::Handle(Z);
String& importee_uri = String::Handle(Z);
for (intptr_t i = 0; i < libraries.Length(); i++) {
importer ^= libraries.At(i);
imports = importer.imports();
for (intptr_t j = 0; j < imports.Length(); j++) {
ns ^= imports.At(j);
if (ns.IsNull()) continue;
importee = ns.library();
importee_uri = importee.url();
if (importee_uri.StartsWith(scheme_vm)) {
result.Add(importer);
result.Add(importee);
}
}
}
return Api::NewHandle(T, Array::MakeArray(result));
}
DART_EXPORT Dart_Handle Dart_LoadSource(Dart_Handle library,
Dart_Handle url,
Dart_Handle resolved_url,

View file

@ -0,0 +1,11 @@
// Copyright (c) 2016, 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.
//
// Dart test program for testing native extensions.
import 'sample_extension_test_helper.dart';
void main() {
testNativeExtensions("app-jit");
}

View file

@ -0,0 +1,11 @@
// Copyright (c) 2016, 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.
//
// Dart test program for testing native extensions.
import 'sample_extension_test_helper.dart';
void main() {
testNativeExtensions("script");
}

View file

@ -1,72 +1,11 @@
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2016, 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.
//
// Dart test program for testing native extensions.
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import "package:expect/expect.dart";
import "package:path/path.dart";
Future copyFileToDirectory(String file, String directory) {
String src = file;
String dst = directory;
switch (Platform.operatingSystem) {
case 'linux':
case 'macos':
return Process.run('cp', [src, dst]);
case 'windows':
return Process.run('cmd.exe', ['/C', 'copy $src $dst']);
default:
Expect.fail('Unknown operating system ${Platform.operatingSystem}');
}
}
String getNativeLibraryPath(String buildDirectory) {
switch (Platform.operatingSystem) {
case 'linux':
return join(buildDirectory, 'lib.target', 'libsample_extension.so');
case 'macos':
return join(buildDirectory, 'libsample_extension.dylib');
case 'windows':
return join(buildDirectory, 'sample_extension.dll');
default:
Expect.fail('Unknown operating system ${Platform.operatingSystem}');
}
}
import 'sample_extension_test_helper.dart';
void main() {
String buildDirectory = dirname(Platform.executable);
Directory tempDirectory =
Directory.systemTemp.createTempSync('sample_extension_');
String testDirectory = tempDirectory.path;
String sourceDirectory = Platform.script.resolve('..').toFilePath();
// Copy sample_extension shared library, sample_extension dart files and
// sample_extension tests to the temporary test directory.
copyFileToDirectory(getNativeLibraryPath(buildDirectory), testDirectory)
.then((_) => Future.forEach(['sample_synchronous_extension.dart',
'sample_asynchronous_extension.dart',
'test_sample_synchronous_extension.dart',
'test_sample_asynchronous_extension.dart'],
(file) => copyFileToDirectory(join(sourceDirectory, file), testDirectory)
))
.then((_) => Future.forEach(['test_sample_synchronous_extension.dart',
'test_sample_asynchronous_extension.dart'],
(test) => Process.run(Platform.executable, [join(testDirectory, test)])
.then((ProcessResult result) {
if (result.exitCode != 0) {
print('Failing test: ${join(sourceDirectory, test)}');
print('Failing process stdout: ${result.stdout}');
print('Failing process stderr: ${result.stderr}');
print('End failing process stderr');
Expect.fail('Test failed with exit code ${result.exitCode}');
}
})
))
.whenComplete(() => tempDirectory.deleteSync(recursive: true));
testNativeExtensions(null /* no snapshot */);
}

View file

@ -0,0 +1,90 @@
// 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.
//
// Dart test program for testing native extensions.
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import "package:expect/expect.dart";
import "package:path/path.dart";
Future copyFileToDirectory(String file, String directory) {
String src = file;
String dst = directory;
switch (Platform.operatingSystem) {
case 'linux':
case 'macos':
return Process.run('cp', [src, dst]);
case 'windows':
return Process.run('cmd.exe', ['/C', 'copy $src $dst']);
default:
Expect.fail('Unknown operating system ${Platform.operatingSystem}');
}
}
String getNativeLibraryPath(String buildDirectory) {
switch (Platform.operatingSystem) {
case 'linux':
return join(buildDirectory, 'lib.target', 'libsample_extension.so');
case 'macos':
return join(buildDirectory, 'libsample_extension.dylib');
case 'windows':
return join(buildDirectory, 'sample_extension.dll');
default:
Expect.fail('Unknown operating system ${Platform.operatingSystem}');
}
}
Future run(String program, List arguments) async {
print("+ $program ${arguments.join(' ')}");
ProcessResult result = await Process.run(program, arguments);
if (result.exitCode != 0) {
print('Failing process stdout: ${result.stdout}');
print('Failing process stderr: ${result.stderr}');
print('End failing process stderr');
Expect.fail('Test failed with exit code ${result.exitCode}');
}
}
Future testNativeExtensions(String snapshotKind) async {
String buildDirectory = dirname(Platform.executable);
Directory tempDirectory =
Directory.systemTemp.createTempSync('sample_extension_');
try {
String testDirectory = tempDirectory.path;
String sourceDirectory = Platform.script.resolve('..').toFilePath();
// Copy sample_extension shared library, sample_extension dart files and
// sample_extension tests to the temporary test directory.
await copyFileToDirectory(getNativeLibraryPath(buildDirectory),
testDirectory);
for (var file in ['sample_synchronous_extension.dart',
'sample_asynchronous_extension.dart',
'test_sample_synchronous_extension.dart',
'test_sample_asynchronous_extension.dart']) {
await copyFileToDirectory(join(sourceDirectory, file), testDirectory);
}
for (var test in ['test_sample_synchronous_extension.dart',
'test_sample_asynchronous_extension.dart']) {
String script = join(testDirectory, test);
String snapshot;
if (snapshotKind == null) {
snapshot = script;
} else {
snapshot = join(testDirectory, "$test.snapshot");
await run(Platform.executable,
['--snapshot=$snapshot',
'--snapshot-kind=$snapshotKind',
script]);
}
await run(Platform.executable, [snapshot]);
}
} finally {
await tempDirectory.deleteSync(recursive: true);
}
}

View file

@ -19,10 +19,7 @@ sample_extension: Crash # Unable to compile UnsupportedError.message.
build_dart: Skip
[ $arch == arm ]
sample_extension/test/sample_extension_test: Skip # Issue 14705
[ $arch == simarm64 ]
*: Skip
sample_extension/test/*: Skip # Issue 14705
[ $noopt || $runtime == dart_precompiled || $runtime == dart_app ]
sample_extension: Skip # Platform.executable