[kernel/vm] Fix native extensions.

Tested with imports through current directory, VM binary directory,
and LD_LIBRARY_PATH. This also restores the Dart 1 behavior of not supporting
relative extension paths.

Change-Id: I090bf8592fef74d4ccde40e6f550baa84c98e3bc
Reviewed-on: https://dart-review.googlesource.com/69162
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
Commit-Queue: Samir Jindel <sjindel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/import.dart b/pkg/front_end/lib/src/fasta/import.dart
index 91165d3..b484601 100644
--- a/pkg/front_end/lib/src/fasta/import.dart
+++ b/pkg/front_end/lib/src/fasta/import.dart
@@ -39,7 +39,7 @@
 
   // The LibraryBuilder for the imported library ('imported') may be null when
   // this field is set.
-  final Uri nativeImportUri;
+  final String nativeImportPath;
 
   Import(
       this.importer,
@@ -51,14 +51,14 @@
       this.charOffset,
       this.prefixCharOffset,
       int importIndex,
-      {this.nativeImportUri})
+      {this.nativeImportPath})
       : prefixBuilder = createPrefixBuilder(prefix, importer, imported,
             combinators, deferred, charOffset, prefixCharOffset, importIndex);
 
   Uri get fileUri => importer.fileUri;
 
   void finalizeImports(LibraryBuilder importer) {
-    if (nativeImportUri != null) return;
+    if (nativeImportPath != null) return;
     void Function(String, Declaration) add;
     if (prefixBuilder == null) {
       add = (String name, Declaration member) {
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
index adcad5b..b52d03d 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_library_builder.dart
@@ -828,10 +828,10 @@
     }
   }
 
-  void addNativeDependency(Uri nativeImportUri) {
+  void addNativeDependency(String nativeImportPath) {
     Declaration constructor = loader.getNativeAnnotation();
     Arguments arguments =
-        new Arguments(<Expression>[new StringLiteral("$nativeImportUri")]);
+        new Arguments(<Expression>[new StringLiteral(nativeImportPath)]);
     Expression annotation;
     if (constructor.isConstructor) {
       annotation = new ConstructorInvocation(constructor.target, arguments)
@@ -862,8 +862,8 @@
         Import import = imports[importIndex++];
 
         // Rather than add a LibraryDependency, we attach an annotation.
-        if (import.nativeImportUri != null) {
-          addNativeDependency(import.nativeImportUri);
+        if (import.nativeImportPath != null) {
+          addNativeDependency(import.nativeImportPath);
           continue;
         }
 
diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
index 1529ed0..9f15391 100644
--- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart
@@ -290,27 +290,30 @@
       }
     }
 
-    const String nativeExtensionScheme = "dart-ext:";
-    bool isExternal = uri.startsWith(nativeExtensionScheme);
-    if (isExternal) {
-      uri = uri.substring(nativeExtensionScheme.length);
-      uriOffset += nativeExtensionScheme.length;
-    }
-
-    Uri resolvedUri = resolve(this.uri, uri, uriOffset);
-
     LibraryBuilder builder = null;
-    if (isExternal) {
-      if (resolvedUri.scheme == "package") {
+
+    Uri resolvedUri;
+    String nativePath;
+    const String nativeExtensionScheme = "dart-ext:";
+    if (uri.startsWith(nativeExtensionScheme)) {
+      String strippedUri = uri.substring(nativeExtensionScheme.length);
+      if (strippedUri.startsWith("package")) {
+        resolvedUri = resolve(
+            this.uri, strippedUri, uriOffset + nativeExtensionScheme.length);
         resolvedUri = loader.target.translateUri(resolvedUri);
+        nativePath = resolvedUri.toString();
+      } else {
+        resolvedUri = new Uri(scheme: "dart-ext", pathSegments: [uri]);
+        nativePath = uri;
       }
     } else {
+      resolvedUri = resolve(this.uri, uri, uriOffset);
       builder = loader.read(resolvedUri, charOffset, accessor: this);
     }
 
     imports.add(new Import(this, builder, deferred, prefix, combinators,
         configurations, charOffset, prefixCharOffset, importIndex,
-        nativeImportUri: builder == null ? resolvedUri : null));
+        nativeImportPath: nativePath));
   }
 
   void addPart(List<MetadataBuilder> metadata, String uri, int charOffset) {
diff --git a/pkg/front_end/testcases/external_import.dart.direct.expect b/pkg/front_end/testcases/external_import.dart.direct.expect
index b262abf..2776224 100644
--- a/pkg/front_end/testcases/external_import.dart.direct.expect
+++ b/pkg/front_end/testcases/external_import.dart.direct.expect
@@ -1,6 +1,6 @@
-@dart._internal::ExternalName::•("org-dartlang-testcase:///here")
-@dart._internal::ExternalName::•("org-dartlang-testcase:///there")
-@dart._internal::ExternalName::•("file:///usr/local/somewhere")
+@dart._internal::ExternalName::•("dart-ext:here")
+@dart._internal::ExternalName::•("dart-ext:foo/../there")
+@dart._internal::ExternalName::•("dart-ext:/usr/local/somewhere")
 library;
 import self as self;
 import "dart:_internal" as _in;
diff --git a/pkg/front_end/testcases/external_import.dart.direct.transformed.expect b/pkg/front_end/testcases/external_import.dart.direct.transformed.expect
index b262abf..2776224 100644
--- a/pkg/front_end/testcases/external_import.dart.direct.transformed.expect
+++ b/pkg/front_end/testcases/external_import.dart.direct.transformed.expect
@@ -1,6 +1,6 @@
-@dart._internal::ExternalName::•("org-dartlang-testcase:///here")
-@dart._internal::ExternalName::•("org-dartlang-testcase:///there")
-@dart._internal::ExternalName::•("file:///usr/local/somewhere")
+@dart._internal::ExternalName::•("dart-ext:here")
+@dart._internal::ExternalName::•("dart-ext:foo/../there")
+@dart._internal::ExternalName::•("dart-ext:/usr/local/somewhere")
 library;
 import self as self;
 import "dart:_internal" as _in;
diff --git a/pkg/front_end/testcases/external_import.dart.outline.expect b/pkg/front_end/testcases/external_import.dart.outline.expect
index b446720..b0af5d4 100644
--- a/pkg/front_end/testcases/external_import.dart.outline.expect
+++ b/pkg/front_end/testcases/external_import.dart.outline.expect
@@ -1,6 +1,6 @@
-@dart._internal::ExternalName::•("org-dartlang-testcase:///here")
-@dart._internal::ExternalName::•("org-dartlang-testcase:///there")
-@dart._internal::ExternalName::•("file:///usr/local/somewhere")
+@dart._internal::ExternalName::•("dart-ext:here")
+@dart._internal::ExternalName::•("dart-ext:foo/../there")
+@dart._internal::ExternalName::•("dart-ext:/usr/local/somewhere")
 library;
 import self as self;
 import "dart:_internal" as _in;
diff --git a/pkg/front_end/testcases/external_import.dart.strong.expect b/pkg/front_end/testcases/external_import.dart.strong.expect
index b262abf..2776224 100644
--- a/pkg/front_end/testcases/external_import.dart.strong.expect
+++ b/pkg/front_end/testcases/external_import.dart.strong.expect
@@ -1,6 +1,6 @@
-@dart._internal::ExternalName::•("org-dartlang-testcase:///here")
-@dart._internal::ExternalName::•("org-dartlang-testcase:///there")
-@dart._internal::ExternalName::•("file:///usr/local/somewhere")
+@dart._internal::ExternalName::•("dart-ext:here")
+@dart._internal::ExternalName::•("dart-ext:foo/../there")
+@dart._internal::ExternalName::•("dart-ext:/usr/local/somewhere")
 library;
 import self as self;
 import "dart:_internal" as _in;
diff --git a/pkg/front_end/testcases/external_import.dart.strong.transformed.expect b/pkg/front_end/testcases/external_import.dart.strong.transformed.expect
index b262abf..2776224 100644
--- a/pkg/front_end/testcases/external_import.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/external_import.dart.strong.transformed.expect
@@ -1,6 +1,6 @@
-@dart._internal::ExternalName::•("org-dartlang-testcase:///here")
-@dart._internal::ExternalName::•("org-dartlang-testcase:///there")
-@dart._internal::ExternalName::•("file:///usr/local/somewhere")
+@dart._internal::ExternalName::•("dart-ext:here")
+@dart._internal::ExternalName::•("dart-ext:foo/../there")
+@dart._internal::ExternalName::•("dart-ext:/usr/local/somewhere")
 library;
 import self as self;
 import "dart:_internal" as _in;
diff --git a/runtime/bin/loader.cc b/runtime/bin/loader.cc
index 0096fa6..4e4bf65 100644
--- a/runtime/bin/loader.cc
+++ b/runtime/bin/loader.cc
@@ -674,18 +674,32 @@
                                  MallocFinalizer);
     return result;
   }
-  if (tag == Dart_kImportResolvedExtensionTag) {
-    if (strncmp(url_string, "file://", 7)) {
+  if (tag == Dart_kImportExtensionTag) {
+    if (strncmp(url_string, "dart-ext:", 9)) {
       return DartUtils::NewError(
-          "Resolved native extensions must use the file:// scheme.");
+          "Native extensions must use the dart-ext: scheme.");
     }
-    const char* absolute_path = DartUtils::RemoveScheme(url_string);
+    const char* path = DartUtils::RemoveScheme(url_string);
 
-    if (!File::IsAbsolutePath(absolute_path)) {
-      return DartUtils::NewError("Native extension path must be absolute.");
+    const char* lib_uri = NULL;
+    result = Dart_StringToCString(Dart_LibraryUrl(library), &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);
     }
 
-    return Extensions::LoadExtension("/", absolute_path, library);
+    if (!File::IsAbsolutePath(path) && PathContainsSeparator(path)) {
+      return DartUtils::NewError(
+          "Native extension path must be absolute, or simply the file name: "
+          "%s: ",
+          path);
+    }
+
+    return Extensions::LoadExtension(lib_path, path, library);
   }
   if (tag != Dart_kScriptTag) {
     // Special case for handling dart: imports and parts.
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index abde8b1..1954394 100644
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -2846,7 +2846,7 @@
   Dart_kSourceTag,
   Dart_kImportTag,
   Dart_kKernelTag,
-  Dart_kImportResolvedExtensionTag,
+  Dart_kImportExtensionTag,
 } Dart_LibraryTag;
 
 /**
@@ -2896,12 +2896,10 @@
  * files into one intermediate file hence we don't use the source/import or
  * script tags.
  *
- * Dart_kImportResolvedExtensionTag
+ * Dart_kImportExtensionTag
  *
- * This tag is used to load an external import (shared object file) without
- * performing path resolution first. The 'url' provided should be an absolute
- * path with the 'file://' schema. It doesn't require the service isolate to be
- * available and will not initialize a Loader for the isolate.
+ * This tag is used to load an external import (shared object file). The
+ * extension path must have the scheme 'dart-ext:'.
  */
 typedef Dart_Handle (*Dart_LibraryTagHandler)(
     Dart_LibraryTag tag,
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index df6ceaf..61947f5 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -547,7 +547,7 @@
       {
         TransitionVMToNative transition(thread_);
         Api::Scope api_scope(thread_);
-        Dart_Handle retval = handler(Dart_kImportResolvedExtensionTag,
+        Dart_Handle retval = handler(Dart_kImportExtensionTag,
                                      Api::NewHandle(thread_, library.raw()),
                                      Api::NewHandle(thread_, uri_path.raw()));
         result = Api::UnwrapHandle(retval);
diff --git a/tests/standalone_2/io/test_extension_fail_test.dart b/tests/standalone_2/io/test_extension_fail_test.dart
index 262d1db..dae4411 100644
--- a/tests/standalone_2/io/test_extension_fail_test.dart
+++ b/tests/standalone_2/io/test_extension_fail_test.dart
@@ -36,7 +36,7 @@
 }
 
 bool checkExitCode(int code) {
-  return ((code == 255) || (code == 253));
+  return ((code == 255) || (code == 254) || (code == 253));
 }
 
 bool checkStdError(String err) {