diff --git a/runtime/bin/dartutils.cc b/runtime/bin/dartutils.cc index d29c947deb5..08a8452eba3 100644 --- a/runtime/bin/dartutils.cc +++ b/runtime/bin/dartutils.cc @@ -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(file); diff --git a/runtime/bin/dartutils.h b/runtime/bin/dartutils.h index f8ad050c34c..9cbebf65260 100644 --- a/runtime/bin/dartutils.h +++ b/runtime/bin/dartutils.h @@ -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); diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc index a7d99ccdd46..c94a21f0307 100644 --- a/runtime/bin/loader.cc +++ b/runtime/bin/loader.cc @@ -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_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) { diff --git a/runtime/bin/loader.h b/runtime/bin/loader.h index ac9bc45f7d5..2076be8b313 100644 --- a/runtime/bin/loader.h +++ b/runtime/bin/loader.h @@ -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, diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index bd1e6affac4..b481a67980e 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -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); diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 2608724962e..bc60b3b006b 100755 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -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 diff --git a/runtime/observatory/lib/src/elements/library_view.dart b/runtime/observatory/lib/src/elements/library_view.dart index 2265d1b487c..165ffc85ffb 100644 --- a/runtime/observatory/lib/src/elements/library_view.dart +++ b/runtime/observatory/lib/src/elements/library_view.dart @@ -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, diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 343f5979e5e..e1c2acacbde 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -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, diff --git a/samples/sample_extension/test/sample_extension_app_snapshot_test.dart b/samples/sample_extension/test/sample_extension_app_snapshot_test.dart new file mode 100644 index 00000000000..04193c98ce0 --- /dev/null +++ b/samples/sample_extension/test/sample_extension_app_snapshot_test.dart @@ -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"); +} diff --git a/samples/sample_extension/test/sample_extension_script_snapshot_test.dart b/samples/sample_extension/test/sample_extension_script_snapshot_test.dart new file mode 100644 index 00000000000..31b9e03fc53 --- /dev/null +++ b/samples/sample_extension/test/sample_extension_script_snapshot_test.dart @@ -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"); +} diff --git a/samples/sample_extension/test/sample_extension_test.dart b/samples/sample_extension/test/sample_extension_test.dart index 9c0a42d3046..4311ff544f7 100644 --- a/samples/sample_extension/test/sample_extension_test.dart +++ b/samples/sample_extension/test/sample_extension_test.dart @@ -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 */); } diff --git a/samples/sample_extension/test/sample_extension_test_helper.dart b/samples/sample_extension/test/sample_extension_test_helper.dart new file mode 100644 index 00000000000..e1c1faa0aa5 --- /dev/null +++ b/samples/sample_extension/test/sample_extension_test_helper.dart @@ -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); + } +} diff --git a/samples/samples.status b/samples/samples.status index c01c0d9540f..02b193fc87f 100644 --- a/samples/samples.status +++ b/samples/samples.status @@ -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