Cherry-pick "Refactor Regexp.prototype" am: 780b5e3420 am: fd88a6d57e am: 40be1f0a48 am: 8419031c72

Change-Id: I4a57f9116496bf2b555e79270471c52e20ff3019
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..9172eae
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,36 @@
+cc_library_shared {
+    name: "libpac",
+
+    srcs: [
+        "src/proxy_resolver_v8.cc",
+        "src/proxy_resolver_v8_wrapper.cc",
+        "src/proxy_resolver_js_bindings.cc",
+        "src/net_util.cc",
+    ],
+
+    cflags: [
+        "-Wno-endif-labels",
+        "-Wno-import",
+        "-Wno-format",
+        "-Wno-unused-parameter",
+        "-Werror",
+    ],
+
+    export_include_dirs: ["includes"],
+    local_include_dirs: ["src"],
+
+    static_libs: ["libv8"],
+
+    shared_libs: [
+        "liblog",
+        "libicuuc",
+        "libicui18n",
+    ],
+
+    stubs: {
+        symbol_file: "libpac.map.txt",
+        versions: [
+            "1",
+        ],
+    },
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index ae0e768..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_CPP_EXTENSION := .cc
-
-# Set up the target identity
-LOCAL_MODULE := libpac
-LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-
-LOCAL_SRC_FILES := \
-  src/proxy_resolver_v8.cc \
-  src/proxy_resolver_js_bindings.cc \
-  src/net_util.cc
-
-LOCAL_CFLAGS += \
-  -Wno-endif-labels \
-  -Wno-import \
-  -Wno-format \
-  -Wno-unused-parameter \
-  -Werror
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src $(LOCAL_PATH)/../v8
-
-LOCAL_STATIC_LIBRARIES := libv8
-
-LOCAL_SHARED_LIBRARIES := libutils liblog libicuuc libicui18n
-
-LOCAL_CXX_STL := libc++
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(LOCAL_PATH)/test/Android.mk
diff --git a/LICENSE b/LICENSE
index 8dc3504..fd537da 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,42 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Akhil Arora <akhil.arora@sun.com>
+ *   Tomi Leppikangas <Tomi.Leppikangas@oulu.fi>
+ *   Darin Fisher <darin@meer.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
diff --git a/MODULE_LICENSE_BSD b/MODULE_LICENSE_BSD
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_BSD
+++ /dev/null
diff --git a/NOTICE b/NOTICE
deleted file mode 100644
index 8dc3504..0000000
--- a/NOTICE
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..7a694c9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+LICENSE
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..995d180
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,4 @@
+# Default code reviewers picked from top 3 or more developers.
+# Please update this list if you find better candidates.
+pgrafov@google.com
+rubinxu@google.com
diff --git a/includes/proxy_resolver_v8_wrapper.h b/includes/proxy_resolver_v8_wrapper.h
new file mode 100644
index 0000000..776a45d
--- /dev/null
+++ b/includes/proxy_resolver_v8_wrapper.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NET_PROXY_PROXY_RESOLVER_V8_WRAPPER_H_
+#define NET_PROXY_PROXY_RESOLVER_V8_WRAPPER_H_
+
+// This header should be compatible with C or C++ compiler
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define OK 0
+#define ERR_PAC_SCRIPT_FAILED -1
+#define ERR_FAILED -2
+
+/**
+* Declare an incomplete type ProxyResolverV8Handle. A pointer of this should always be
+* either null or pointing to a C++ ProxyResolverV8 object.
+*/
+typedef void* ProxyResolverV8Handle;
+
+/**
+ * Create a ProxyResolverV8Handle. The caller must call ProxyResolverV8Handle_delete to release
+ * the memory.
+ */
+ProxyResolverV8Handle* ProxyResolverV8Handle_new();
+
+/**
+ * @return the result in char16_t array if the run is successful. The result contains a list of
+ *         proxies according to the PAC specification. Otherwise, return NULL if the run fails.
+ *         The memory is allocated with malloc() and the caller should use free() to release it
+ *         after use.
+ */
+char16_t* ProxyResolverV8Handle_GetProxyForURL(ProxyResolverV8Handle* handle,
+        const char16_t* spec, const char16_t* host);
+/**
+ * @return OK if setting the pac script successfully.
+ */
+int ProxyResolverV8Handle_SetPacScript(ProxyResolverV8Handle* handle,
+        const char16_t* script_data);
+
+void ProxyResolverV8Handle_delete(ProxyResolverV8Handle* handle);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NET_PROXY_PROXY_RESOLVER_V8_WRAPPER_H_
\ No newline at end of file
diff --git a/libpac.map.txt b/libpac.map.txt
new file mode 100644
index 0000000..1936fa9
--- /dev/null
+++ b/libpac.map.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LIBPAC_EXTERNAL_1 {
+  global:
+    ProxyResolverV8Handle_new;
+    ProxyResolverV8Handle_GetProxyForURL;
+    ProxyResolverV8Handle_SetPacScript;
+    ProxyResolverV8Handle_delete;
+  local:
+    *;
+};
\ No newline at end of file
diff --git a/src/proxy_resolver_js_bindings.h b/src/proxy_resolver_js_bindings.h
index 9559492..ebdacfe 100644
--- a/src/proxy_resolver_js_bindings.h
+++ b/src/proxy_resolver_js_bindings.h
@@ -6,13 +6,10 @@
 #define NET_PROXY_PROXY_RESOLVER_JS_BINDINGS_H_
 #pragma once
 
-#include <utils/String16.h>
 #include <string>
 
 namespace net {
 
-class ProxyErrorListener;
-
 // Interface for the javascript bindings.
 class ProxyResolverJSBindings {
  public:
diff --git a/src/proxy_resolver_v8.cc b/src/proxy_resolver_v8.cc
index 5d8b776..5884bd1 100644
--- a/src/proxy_resolver_v8.cc
+++ b/src/proxy_resolver_v8.cc
@@ -2,13 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#ifdef ANDROID
+#define LOG_TAG "libpac"
+#include <utils/Log.h>
+#endif
+
 #include "proxy_resolver_v8.h"
 
 #include <algorithm>
 #include <cstdio>
 #include <iostream>
 #include <string>
-#include <utils/String8.h>
 #include <v8.h>
 #include <libplatform/libplatform.h>
 #include <vector>
@@ -65,50 +69,49 @@
 // isInNetEx()         | N/A         |  IPv4/IPv6        |  IPv4/IPv6
 // -----------------+-------------+-------------------+--------------
 
-static bool DoIsStringASCII(const android::String16& str) {
-  for (size_t i = 0; i < str.size(); i++) {
-    unsigned short c = str.string()[i];
+static bool DoIsStringASCII(const std::u16string& str) {
+  for (size_t i = 0; i < str.length(); i++) {
+    unsigned short c = str.data()[i];
     if (c > 0x7F)
       return false;
   }
   return true;
 }
 
-bool IsStringASCII(const android::String16& str) {
+bool IsStringASCII(const std::u16string& str) {
   return DoIsStringASCII(str);
 }
 
+#ifdef ANDROID
+#define LOG_ALERT(...) ALOGD(__VA_ARGS__)
+#define LOG_ERROR(...) ALOGE(__VA_ARGS__)
+#else
+#define LOG_ALERT(...) vprintf(__VA_ARGS__)
+#define LOG_ERROR(...) vfprintf(stderr, __VA_ARGS__)
+#endif
+
 namespace net {
 
 namespace {
 
+class DefaultProxyErrorListener : public ProxyErrorListener {
+public:
+    ~DefaultProxyErrorListener() {}
+    void AlertMessage(const std::string& message) {
+        LOG_ALERT("Alert: %s", message.data());
+    }
+    void ErrorMessage(const std::string& message) {
+        LOG_ERROR("Error: %s", message.data());
+    }
+};
+
+ProxyErrorListener* const defaultProxyErrorListener = new DefaultProxyErrorListener();
+
 // Pseudo-name for the PAC script.
 const char kPacResourceName[] = "proxy-pac-script.js";
 // Pseudo-name for the PAC utility script.
 const char kPacUtilityResourceName[] = "proxy-pac-utility-script.js";
 
-// External string wrapper so V8 can access the UTF16 string wrapped by
-// ProxyResolverScriptData.
-class V8ExternalStringFromScriptData
-    : public v8::String::ExternalStringResource {
- public:
-  explicit V8ExternalStringFromScriptData(
-      const android::String16& script_data)
-      : script_data_(script_data) {}
-
-  virtual const uint16_t* data() const {
-    return reinterpret_cast<const uint16_t*>(script_data_.string());
-  }
-
-  virtual size_t length() const {
-    return script_data_.size();
-  }
-
- private:
-  const android::String16& script_data_;
-//  DISALLOW_COPY_AND_ASSIGN(V8ExternalStringFromScriptData);
-};
-
 // External string wrapper so V8 can access a string literal.
 class V8ExternalASCIILiteral
     : public v8::String::ExternalOneByteStringResource {
@@ -155,28 +158,23 @@
 }
 
 // Converts a V8 String to a UTF16 string.
-android::String16 V8StringToUTF16(v8::Handle<v8::String> s) {
+std::u16string V8StringToUTF16(v8::Handle<v8::String> s) {
   int len = s->Length();
   char16_t* buf = new char16_t[len + 1];
   s->Write(reinterpret_cast<uint16_t*>(buf), 0, len);
-  android::String16 ret(buf, len);
+  std::u16string ret(buf, len);
   delete[] buf;
   return ret;
 }
 
-std::string UTF16ToASCII(const android::String16& str) {
-  android::String8 rstr(str);
-  return std::string(rstr.string());
-}
-
 // Converts an ASCII std::string to a V8 string.
 v8::Local<v8::String> ASCIIStringToV8String(v8::Isolate* isolate, const std::string& s) {
   return v8::String::NewFromUtf8(isolate, s.data(), v8::String::kNormalString, s.size());
 }
 
-v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const android::String16& s) {
+v8::Local<v8::String> UTF16StringToV8String(v8::Isolate* isolate, const std::u16string& s) {
   return v8::String::NewFromTwoByte(
-      isolate, reinterpret_cast<const uint16_t*>(s.string()),
+      isolate, reinterpret_cast<const uint16_t*>(s.data()),
       v8::String::kNormalString, s.size());
 }
 
@@ -191,8 +189,8 @@
 
 // Stringizes a V8 object by calling its toString() method. Returns true
 // on success. This may fail if the toString() throws an exception.
-bool V8ObjectToUTF16String(v8::Handle<v8::Value> object,
-                           android::String16* utf16_result,
+bool V8ObjectToUTF8String(v8::Handle<v8::Value> object,
+                           std::string* utf8_result,
                            v8::Isolate* isolate) {
   if (object.IsEmpty())
     return false;
@@ -201,7 +199,7 @@
   v8::Local<v8::String> str_object = object->ToString();
   if (str_object.IsEmpty())
     return false;
-  *utf16_result = V8StringToUTF16(str_object);
+  *utf8_result = V8StringToUTF8(str_object);
   return true;
 }
 
@@ -212,11 +210,11 @@
   if (args.Length() == 0 || args[0].IsEmpty() || !args[0]->IsString())
     return false;
 
-  const android::String16 hostname_utf16 = V8StringToUTF16(args[0]->ToString());
+  const std::u16string hostname_utf16 = V8StringToUTF16(args[0]->ToString());
 
   // If the hostname is already in ASCII, simply return it as is.
   if (IsStringASCII(hostname_utf16)) {
-    *hostname = UTF16ToASCII(hostname_utf16);
+    *hostname = V8StringToUTF8(args[0]->ToString());
     return true;
   }
   return false;
@@ -371,8 +369,8 @@
     v8_context_.Reset();
   }
 
-  int ResolveProxy(const android::String16 url, const android::String16 host,
-        android::String16* results) {
+  int ResolveProxy(const std::u16string url, const std::u16string host,
+        std::u16string* results) {
     v8::Locker locked(isolate_);
     v8::Isolate::Scope isolate_scope(isolate_);
     v8::HandleScope scope(isolate_);
@@ -383,8 +381,7 @@
 
     v8::Local<v8::Value> function;
     if (!GetFindProxyForURL(&function)) {
-      error_listener_->ErrorMessage(
-          android::String16("FindProxyForURL() is undefined"));
+      error_listener_->ErrorMessage("FindProxyForURL() is undefined");
       return ERR_PAC_SCRIPT_FAILED;
     }
 
@@ -392,19 +389,17 @@
         UTF16StringToV8String(isolate_, url),
         UTF16StringToV8String(isolate_, host) };
 
-    v8::TryCatch try_catch;
+    v8::TryCatch try_catch(isolate_);
     v8::Local<v8::Value> ret = v8::Function::Cast(*function)->Call(
         context->Global(), 2, argv);
 
     if (try_catch.HasCaught()) {
-      error_listener_->ErrorMessage(
-          V8StringToUTF16(try_catch.Message()->Get()));
+      error_listener_->ErrorMessage(V8StringToUTF8(try_catch.Message()->Get()));
       return ERR_PAC_SCRIPT_FAILED;
     }
 
     if (!ret->IsString()) {
-      error_listener_->ErrorMessage(
-          android::String16("FindProxyForURL() did not return a string."));
+      error_listener_->ErrorMessage("FindProxyForURL() did not return a string.");
       return ERR_PAC_SCRIPT_FAILED;
     }
 
@@ -415,15 +410,14 @@
       //               could extend the parsing to handle IDNA hostnames by
       //               converting them to ASCII punycode.
       //               crbug.com/47234
-      error_listener_->ErrorMessage(
-          android::String16("FindProxyForURL() returned a non-ASCII string"));
+      error_listener_->ErrorMessage("FindProxyForURL() returned a non-ASCII string");
       return ERR_PAC_SCRIPT_FAILED;
     }
 
     return OK;
   }
 
-  int InitV8(const android::String16& pac_script) {
+  int InitV8(const std::u16string& pac_script) {
     v8::Locker locked(isolate_);
     v8::Isolate::Scope isolate_scope(isolate_);
     v8::HandleScope scope(isolate_);
@@ -431,7 +425,7 @@
     v8_this_.Reset(isolate_, v8::External::New(isolate_, this));
     v8::Local<v8::External> v8_this =
         v8::Local<v8::External>::New(isolate_, v8_this_);
-    v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
+    v8::Local<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate_);
 
     // Attach the javascript bindings.
     v8::Local<v8::FunctionTemplate> alert_template =
@@ -523,22 +517,24 @@
   void HandleError(v8::Handle<v8::Message> message) {
     if (message.IsEmpty())
       return;
-    error_listener_->ErrorMessage(V8StringToUTF16(message->Get()));
+    error_listener_->ErrorMessage(V8StringToUTF8(message->Get()));
   }
 
   // Compiles and runs |script| in the current V8 context.
   // Returns OK on success, otherwise an error code.
   int RunScript(v8::Handle<v8::String> script, const char* script_name) {
-    v8::TryCatch try_catch;
+    v8::Local<v8::Context> context =
+        v8::Local<v8::Context>::New(isolate_, v8_context_);
+    v8::TryCatch try_catch(isolate_);
 
     // Compile the script.
     v8::ScriptOrigin origin =
         v8::ScriptOrigin(ASCIILiteralToV8String(isolate_, script_name));
-    v8::Local<v8::Script> code = v8::Script::Compile(script, &origin);
+    v8::MaybeLocal<v8::Script> code = v8::Script::Compile(context, script, &origin);
 
     // Execute.
     if (!code.IsEmpty())
-      code->Run();
+      code.ToLocalChecked()->Run(context);
 
     // Check for errors.
     if (try_catch.HasCaught()) {
@@ -556,14 +552,11 @@
 
     // Like firefox we assume "undefined" if no argument was specified, and
     // disregard any arguments beyond the first.
-    android::String16 message;
+    std::string message;
     if (args.Length() == 0) {
-      std::string undef = "undefined";
-      android::String8 undef8(undef.c_str());
-      android::String16 wundef(undef8);
-      message = wundef;
+      message = "undefined";
     } else {
-      if (!V8ObjectToUTF16String(args[0], &message, args.GetIsolate()))
+      if (!V8ObjectToUTF8String(args[0], &message, args.GetIsolate()))
         return;  // toString() threw an exception.
     }
 
@@ -714,6 +707,10 @@
 
 bool ProxyResolverV8::initialized_for_this_process_ = false;
 
+ProxyResolverV8::ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings)
+    : ProxyResolverV8(custom_js_bindings, defaultProxyErrorListener) {
+}
+
 ProxyResolverV8::ProxyResolverV8(
     ProxyResolverJSBindings* custom_js_bindings,
     ProxyErrorListener* error_listener)
@@ -737,13 +734,14 @@
   }
 }
 
-int ProxyResolverV8::GetProxyForURL(const android::String16 spec, const android::String16 host,
-                                    android::String16* results) {
+int ProxyResolverV8::GetProxyForURL(const std::u16string& spec, const std::u16string& host,
+                                    std::u16string* results) {
   // If the V8 instance has not been initialized (either because
   // SetPacScript() wasn't called yet, or because it failed.
-  if (context_ == NULL)
+  if (context_ == NULL) {
+    error_listener_->ErrorMessage(std::string("Context is null."));
     return ERR_FAILED;
-
+  }
   // Otherwise call into V8.
   int rv = context_->ResolveProxy(spec, host, results);
 
@@ -754,14 +752,16 @@
   context_->PurgeMemory();
 }
 
-int ProxyResolverV8::SetPacScript(const android::String16& script_data) {
+int ProxyResolverV8::SetPacScript(const std::u16string& script_data) {
   if (context_ != NULL) {
     delete context_;
     context_ = NULL;
   }
-  if (script_data.size() == 0)
+  if (script_data.length() == 0)
     return ERR_PAC_SCRIPT_FAILED;
 
+  // To help debugging v8 initialization issues, add "--log_all --print_all_exceptions"
+  // to the options
   // Disable JIT
   static const char kNoOpt[] = "--no-opt";
   v8::V8::SetFlagsFromString(kNoOpt, strlen(kNoOpt));
diff --git a/src/proxy_resolver_v8.h b/src/proxy_resolver_v8.h
index 9b74e29..36a3214 100644
--- a/src/proxy_resolver_v8.h
+++ b/src/proxy_resolver_v8.h
@@ -6,25 +6,19 @@
 #define NET_PROXY_PROXY_RESOLVER_V8_H_
 #pragma once
 
-#include <utils/String16.h>
+#include <string>
 
 #include "proxy_resolver_js_bindings.h"
+#include "proxy_resolver_v8_wrapper.h"
 
 namespace net {
 
-typedef void* RequestHandle;
-typedef void* CompletionCallback;
-
-#define OK 0
-#define ERR_PAC_SCRIPT_FAILED -1
-#define ERR_FAILED -2
-
 class ProxyErrorListener {
 protected:
   virtual ~ProxyErrorListener() {}
 public:
-  virtual void AlertMessage(android::String16 message) = 0;
-  virtual void ErrorMessage(android::String16 error) = 0;
+  virtual void AlertMessage(const std::string& message) = 0;
+  virtual void ErrorMessage(const std::string& error) = 0;
 };
 
 // Implementation of ProxyResolver that uses V8 to evaluate PAC scripts.
@@ -50,6 +44,8 @@
   // Constructs a ProxyResolverV8 with custom bindings. ProxyResolverV8 takes
   // ownership of |custom_js_bindings| and deletes it when ProxyResolverV8
   // is destroyed.
+  explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings);
+  // This constructor should only be used for test.
   explicit ProxyResolverV8(ProxyResolverJSBindings* custom_js_bindings,
           ProxyErrorListener* error_listener);
 
@@ -57,10 +53,10 @@
 
   ProxyResolverJSBindings* js_bindings() { return js_bindings_; }
 
-  virtual int GetProxyForURL(const android::String16 spec, const android::String16 host,
-                             android::String16* results);
+  virtual int GetProxyForURL(const std::u16string& spec, const std::u16string& host,
+                             std::u16string* results);
   virtual void PurgeMemory();
-  virtual int SetPacScript(const android::String16& script_data);
+  virtual int SetPacScript(const std::u16string& script_data);
 
  private:
   // Context holds the Javascript state for the most recently loaded PAC
diff --git a/src/proxy_resolver_v8_wrapper.cc b/src/proxy_resolver_v8_wrapper.cc
new file mode 100644
index 0000000..ae56d9a
--- /dev/null
+++ b/src/proxy_resolver_v8_wrapper.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "proxy_resolver_v8_wrapper.h"
+#include "proxy_resolver_v8.h"
+
+using namespace net;
+
+// Implement C interface of ProxyResolverV8
+extern "C" {
+
+ProxyResolverV8Handle* ProxyResolverV8Handle_new() {
+  ProxyResolverV8* proxyResolver = new ProxyResolverV8(ProxyResolverJSBindings::CreateDefault());
+  return reinterpret_cast<ProxyResolverV8Handle*>(proxyResolver);
+}
+
+char16_t* ProxyResolverV8Handle_GetProxyForURL(ProxyResolverV8Handle* handle,
+                                               const char16_t* spec,
+                                               const char16_t* host) {
+  ProxyResolverV8* proxyResolver = reinterpret_cast<ProxyResolverV8*>(handle);
+  std::u16string specStr(spec);
+  std::u16string hostStr(host);
+  std::u16string proxies;
+  int code = proxyResolver->GetProxyForURL(specStr, hostStr, &proxies);
+  if (code != OK) {
+    return NULL;
+  }
+  auto len = proxies.length();
+  char16_t* result = (char16_t*) malloc(sizeof(char16_t) * (len + 1));
+  if (result == 0) { // Failed to allocate
+    return NULL;
+  }
+  proxies.copy(result, len);
+  result[len] = u'\0';
+  return result;
+}
+
+int ProxyResolverV8Handle_SetPacScript(ProxyResolverV8Handle* handle,
+        const char16_t* script_data) {
+  ProxyResolverV8* proxyResolver = reinterpret_cast<ProxyResolverV8*>(handle);
+  std::u16string script(script_data);
+  return proxyResolver->SetPacScript(script);
+}
+
+void ProxyResolverV8Handle_delete(ProxyResolverV8Handle* handle) {
+  ProxyResolverV8* proxyResolver = reinterpret_cast<ProxyResolverV8*>(handle);
+  delete proxyResolver;
+}
+
+} // extern "C"
\ No newline at end of file
diff --git a/test/Android.bp b/test/Android.bp
new file mode 100644
index 0000000..5910ebb
--- /dev/null
+++ b/test/Android.bp
@@ -0,0 +1,23 @@
+cc_test {
+    name: "proxy_resolver_v8_unittest",
+
+    srcs: ["proxy_resolver_v8_unittest.cc"],
+
+    include_dirs: ["external/chromium-libpac/src"],
+
+    cflags: [
+        "-Wno-endif-labels",
+        "-Wno-import",
+        "-Wno-format",
+    ],
+
+    // Use the non-APEX version for C++ API.
+    bootstrap: true,
+
+    shared_libs: [
+        "libpac",
+        "libutils",
+        "liblog",
+        "libandroid_runtime",
+    ],
+}
diff --git a/test/Android.mk b/test/Android.mk
deleted file mode 100644
index edf9107..0000000
--- a/test/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_CPP_EXTENSION := .cc
-
-# Set up the target identity
-LOCAL_MODULE := proxy_resolver_v8_unittest
-
-LOCAL_SRC_FILES := \
-  proxy_resolver_v8_unittest.cc
-
-LOCAL_CFLAGS += \
-  -Wno-endif-labels \
-  -Wno-import \
-  -Wno-format \
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/../src $(LOCAL_PATH)/ external/v8
-
-LOCAL_SHARED_LIBRARIES := libpac libutils liblog libandroid_runtime
-
-include $(BUILD_NATIVE_TEST)
diff --git a/test/js-unittest/string_functions.js b/test/js-unittest/string_functions.js
new file mode 100644
index 0000000..a7c430f
--- /dev/null
+++ b/test/js-unittest/string_functions.js
@@ -0,0 +1,3 @@
+function FindProxyForURL(url, host) {
+  return "direct".toUpperCase();
+}
diff --git a/test/jstocstring.pl b/test/jstocstring.pl
index 7b50772..eade2b8 100755
--- a/test/jstocstring.pl
+++ b/test/jstocstring.pl
@@ -15,6 +15,7 @@
     if (s/\.js/_JS/) {
         $upper = uc();
         print FILE "#define $upper \\\n";
+        print FILE "  u\"\"\\\n";
         while (<FH>) {
             s/\"/\\\"/g;
             chomp();
diff --git a/test/proxy_resolver_v8_unittest.cc b/test/proxy_resolver_v8_unittest.cc
index e66f68b..8469cab 100644
--- a/test/proxy_resolver_v8_unittest.cc
+++ b/test/proxy_resolver_v8_unittest.cc
@@ -11,9 +11,9 @@
 
 #include "android_runtime/AndroidRuntime.h"
 #include "proxy_test_script.h"
+#include "proxy_resolver_v8_wrapper.h"
 #include "proxy_resolver_v8.h"
 
-using namespace android;
 namespace net {
 namespace {
 
@@ -49,20 +49,14 @@
     return !dns_resolve_ex_result.empty();
   }
 
-  virtual void AlertMessage(String16 message) {
-    String8 m8(message);
-    std::string mstd(m8.string());
-
-    ALOGD("PAC-alert: %s\n", mstd.c_str());  // Helpful when debugging.
-    alerts.push_back(mstd);
+  virtual void AlertMessage(const std::string& message) {
+    ALOGD("PAC-alert: %s\n", message.c_str());  // Helpful when debugging.
+    alerts.push_back(message);
   }
 
-  virtual void ErrorMessage(const String16 message) {
-    String8 m8(message);
-    std::string mstd(m8.string());
-
-    ALOGD("PAC-error: %s\n", mstd.c_str());  // Helpful when debugging.
-    errors.push_back(mstd);
+  virtual void ErrorMessage(const std::string& message) {
+    ALOGD("PAC-error: %s\n", message.c_str());  // Helpful when debugging.
+    errors.push_back(message);
   }
 
   virtual void Shutdown() {}
@@ -100,12 +94,12 @@
 };
 
 // Doesn't really matter what these values are for many of the tests.
-const String16 kQueryUrl("http://www.google.com");
-const String16 kQueryHost("www.google.com");
-String16 kResults;
+const std::u16string kQueryUrl(u"http://www.google.com");
+const std::u16string kQueryHost(u"www.google.com");
+std::u16string kResults;
 
-String16 currentPac;
-#define SCRIPT(x) (currentPac = String16(x))
+std::u16string currentPac;
+#define SCRIPT(x) (currentPac = std::u16string(x))
 
 void addString(std::vector<std::string>* list, std::string str) {
   if (str.compare(0, 6, "DIRECT") == 0) {
@@ -117,9 +111,21 @@
   }
 }
 
-std::vector<std::string> string16ToProxyList(String16 response) {
+std::unique_ptr<ProxyResolverV8Handle, void(*)(ProxyResolverV8Handle*)> newProxyResolverV8Handle() {
+  return std::unique_ptr<ProxyResolverV8Handle, void(*)(ProxyResolverV8Handle*)>(
+          ProxyResolverV8Handle_new(), &ProxyResolverV8Handle_delete);
+}
+
+std::unique_ptr<char16_t, decltype(&free)> getProxyForURL(ProxyResolverV8Handle* handle,
+                                                         const char16_t* spec,
+                                                         const char16_t* host) {
+  return std::unique_ptr<char16_t, decltype(&free)>(
+      ProxyResolverV8Handle_GetProxyForURL(handle, spec, host), &free);
+}
+
+std::vector<std::string> string16ToProxyList(const std::u16string& response) {
     std::vector<std::string> ret;
-    String8 response8(response);
+    android::String8 response8(response.data());
     std::string rstr(response8.string());
     if (rstr.find(';') == std::string::npos) {
         addString(&ret, rstr);
@@ -163,6 +169,21 @@
   EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
 }
 
+TEST(ProxyResolverV8Test, Direct_C_API) {
+  auto handle = newProxyResolverV8Handle();
+  int result = ProxyResolverV8Handle_SetPacScript(handle.get(), DIRECT_JS);
+  EXPECT_EQ(OK, result);
+
+  std::unique_ptr<char16_t, decltype(&free)> result_list = getProxyForURL(
+      handle.get(), kQueryUrl.data(), kQueryHost.data());
+
+  ASSERT_NE(nullptr, result_list.get());
+  kResults = result_list.get();
+  std::vector<std::string> proxies = string16ToProxyList(kResults);
+  EXPECT_EQ(proxies.size(), 1U);
+  EXPECT_EQ("DIRECT",proxies[0]);
+}
+
 TEST(ProxyResolverV8Test, ReturnEmptyString) {
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
   int result = resolver.SetPacScript(SCRIPT(RETURN_EMPTY_STRING_JS));
@@ -187,8 +208,8 @@
   // arguments into a pseudo-host. The purpose of this test is to verify that
   // the correct arguments are being passed to FindProxyForURL().
   {
-    String16 queryUrl("http://query.com/path");
-    String16 queryHost("query.com");
+    std::u16string queryUrl(u"http://query.com/path");
+    std::u16string queryHost(u"query.com");
     result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults);
     EXPECT_EQ(OK, result);
     std::vector<std::string> proxies = string16ToProxyList(kResults);
@@ -196,8 +217,8 @@
     EXPECT_EQ("http.query.com.path.query.com", proxies[0]);
   }
   {
-    String16 queryUrl("ftp://query.com:90/path");
-    String16 queryHost("query.com");
+    std::u16string queryUrl(u"ftp://query.com:90/path");
+    std::u16string queryHost(u"query.com");
     int result = resolver.GetProxyForURL(queryUrl, queryHost, &kResults);
 
     EXPECT_EQ(OK, result);
@@ -219,16 +240,33 @@
   resolver.PurgeMemory();
 }
 
+TEST(ProxyResolverV8Test, Basic_C_API) {
+  auto handle = newProxyResolverV8Handle();
+  int result = ProxyResolverV8Handle_SetPacScript(handle.get(), PASSTHROUGH_JS);
+  EXPECT_EQ(OK, result);
+
+  // The "FindProxyForURL" of this PAC script simply concatenates all of the
+  // arguments into a pseudo-host. The purpose of this test is to verify that
+  // the correct arguments are being passed to FindProxyForURL().
+  std::unique_ptr<char16_t, decltype(&free)> result_list = getProxyForURL(
+      handle.get(), u"http://query.com/path", u"query.com");
+  ASSERT_NE(nullptr, result_list.get());
+  kResults = result_list.get();
+  std::vector<std::string> proxies = string16ToProxyList(kResults);
+  EXPECT_EQ(1U, proxies.size());
+  EXPECT_EQ("http.query.com.path.query.com", proxies[0]);
+}
+
 TEST(ProxyResolverV8Test, BadReturnType) {
   // These are the files of PAC scripts which each return a non-string
   // types for FindProxyForURL(). They should all fail with
   // ERR_PAC_SCRIPT_FAILED.
-  static const String16 files[] = {
-      String16(RETURN_UNDEFINED_JS),
-      String16(RETURN_INTEGER_JS),
-      String16(RETURN_FUNCTION_JS),
-      String16(RETURN_OBJECT_JS),
-      String16(RETURN_NULL_JS)
+  static const std::u16string files[] = {
+      std::u16string(RETURN_UNDEFINED_JS),
+      std::u16string(RETURN_INTEGER_JS),
+      std::u16string(RETURN_FUNCTION_JS),
+      std::u16string(RETURN_OBJECT_JS),
+      std::u16string(RETURN_NULL_JS)
   };
 
   for (size_t i = 0; i < 5; ++i) {
@@ -273,10 +311,20 @@
   EXPECT_EQ(0U, bindings->alerts.size());
 
   // We get one error during compilation.
-  ASSERT_EQ(1U, bindings->errors.size());
+  ASSERT_EQ(2U, bindings->errors.size());
 
   EXPECT_EQ("Uncaught SyntaxError: Unexpected end of input",
             bindings->errors[0]);
+  EXPECT_EQ("Context is null.", bindings->errors[1]);
+}
+
+TEST(ProxyResolverV8Test, ParseError_C_API) {
+  auto handle = newProxyResolverV8Handle();
+  int result = ProxyResolverV8Handle_SetPacScript(handle.get(), MISSING_CLOSE_BRACE_JS);
+  EXPECT_EQ(ERR_PAC_SCRIPT_FAILED, result);
+  std::unique_ptr<char16_t, decltype(&free)> result_list = getProxyForURL(
+      handle.get(), u"http://query.com/path", u"query.com");
+  EXPECT_EQ(nullptr, result_list.get());
 }
 
 // Run a PAC script several times, which has side-effects.
@@ -366,6 +414,9 @@
   // Resolve should fail, as we are not yet initialized with a script.
   int result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
   EXPECT_EQ(ERR_FAILED, result);
+  EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+  EXPECT_EQ(1U, resolver.mock_js_bindings()->errors.size());
+  EXPECT_EQ("Context is null.", resolver.mock_js_bindings()->errors[0]);
 
   // Initialize it.
   result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
@@ -382,6 +433,10 @@
   result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
   EXPECT_EQ(ERR_FAILED, result);
 
+  EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+  EXPECT_EQ(2U, resolver.mock_js_bindings()->errors.size());
+  EXPECT_EQ("Context is null.", resolver.mock_js_bindings()->errors[1]);
+
   // Load a good script once more.
   result = resolver.SetPacScript(SCRIPT(DIRECT_JS));
   EXPECT_EQ(OK, result);
@@ -389,7 +444,7 @@
   EXPECT_EQ(OK, result);
 
   EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
-  EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+  EXPECT_EQ(2U, resolver.mock_js_bindings()->errors.size());
 }
 
 // Test marshalling/un-marshalling of values between C++/V8.
@@ -522,7 +577,7 @@
 TEST(ProxyResolverV8Test, DNSResolutionOfInternationDomainName) {
     return;
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
-  int result = resolver.SetPacScript(String16(INTERNATIONAL_DOMAIN_NAMES_JS));
+  int result = resolver.SetPacScript(SCRIPT(INTERNATIONAL_DOMAIN_NAMES_JS));
   EXPECT_EQ(OK, result);
 
   // Execute FindProxyForURL().
@@ -544,9 +599,25 @@
   EXPECT_EQ("xn--bcher-kva.ch", bindings->dns_resolves_ex[0]);
 }
 
+TEST(ProxyResolverV8Test, StringPrototype) {
+  ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
+  int result = resolver.SetPacScript(SCRIPT(STRING_FUNCTIONS_JS));
+  EXPECT_EQ(OK, result);
+
+  result = resolver.GetProxyForURL(kQueryUrl, kQueryHost, &kResults);
+
+  EXPECT_EQ(OK, result);
+  std::vector<std::string> proxies = string16ToProxyList(kResults);
+  EXPECT_EQ(proxies.size(), 1U);
+  EXPECT_EQ("DIRECT",proxies[0]);
+
+  EXPECT_EQ(0U, resolver.mock_js_bindings()->alerts.size());
+  EXPECT_EQ(0U, resolver.mock_js_bindings()->errors.size());
+}
+
 TEST(ProxyResolverV8Test, GetterChangesElementKind) {
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
-  int result = resolver.SetPacScript(String16(CHANGE_ELEMENT_KIND_JS));
+  int result = resolver.SetPacScript(SCRIPT(CHANGE_ELEMENT_KIND_JS));
   EXPECT_EQ(OK, result);
 
   // Execute FindProxyForURL().
@@ -560,7 +631,7 @@
 
 TEST(ProxyResolverV8Test, B_132073833) {
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
-  int result = resolver.SetPacScript(String16(B_132073833_JS));
+  int result = resolver.SetPacScript(SCRIPT(B_132073833_JS));
   EXPECT_EQ(OK, result);
 
   // Execute FindProxyForURL().
@@ -574,7 +645,7 @@
 
 TEST(ProxyResolverV8Test, B_139806216) {
   ProxyResolverV8WithMockBindings resolver(new MockJSBindings());
-  int result = resolver.SetPacScript(String16(B_139806216_JS));
+  int result = resolver.SetPacScript(SCRIPT(B_139806216_JS));
   EXPECT_EQ(OK, result);
 
   // Execute FindProxyForURL().
diff --git a/test/proxy_test_script.h b/test/proxy_test_script.h
index 0deb19f..2a86491 100644
--- a/test/proxy_test_script.h
+++ b/test/proxy_test_script.h
@@ -5,6 +5,7 @@
 #define PROXY_TEST_SCRIPT_H_
 
 #define B_132073833_JS \
+  u""\
   "function FindProxyForURL(url, host){\n" \
   "    function opt() {\n" \
   "        opt['x'] = 1.1;\n" \
@@ -28,6 +29,7 @@
   "var object;\n" \
 
 #define B_139806216_JS \
+  u""\
   "function FindProxyForURL(url, host){\n" \
   "    var x = new ArrayBuffer(1);\n" \
   "    return \"DIRECT\";\n" \
@@ -58,6 +60,7 @@
   "}\n" \
 
 #define BINDING_FROM_GLOBAL_JS \
+  u""\
   "// Calls a bindings outside of FindProxyForURL(). This causes the code to\n" \
   "// get exercised during initialization.\n" \
   "\n" \
@@ -68,6 +71,7 @@
   "}\n" \
 
 #define BINDINGS_JS \
+  u""\
   "// Try calling the browser-side bound functions with varying (invalid)\n" \
   "// inputs. There is no notion of \"success\" for this test, other than\n" \
   "// verifying the correct C++ bindings were reached with expected values.\n" \
@@ -132,6 +136,7 @@
   "\n" \
 
 #define CHANGE_ELEMENT_KIND_JS \
+  u""\
   "// PAC script with getter that changes element kind.\n" \
   "\n" \
   "function FindProxyForURL(url, host) {\n" \
@@ -149,12 +154,14 @@
   "}\n" \
 
 #define DIRECT_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"DIRECT\";\n" \
   "}\n" \
   "\n" \
 
 #define DNS_FAIL_JS \
+  u""\
   "// This script should be run in an environment where all DNS resolution are\n" \
   "// failing. It tests that functions return the expected values.\n" \
   "//\n" \
@@ -184,6 +191,7 @@
   "\n" \
 
 #define ENDS_WITH_COMMENT_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"PROXY success:80\";\n" \
   "}\n" \
@@ -194,11 +202,13 @@
   "// those extra functions was being considered part of the comment.\n" \
 
 #define ENDS_WITH_STATEMENT_NO_SEMICOLON_JS \
+  u""\
   "// Ends with a statement, and no terminal newline.\n" \
   "function FindProxyForURL(url, host) { return \"PROXY success:\" + x; }\n" \
   "x = 3\n" \
 
 #define INTERNATIONAL_DOMAIN_NAMES_JS \
+  u""\
   "// Try resolving hostnames containing non-ASCII characters.\n" \
   "\n" \
   "function FindProxyForURL(url, host) {\n" \
@@ -217,6 +227,7 @@
   "\n" \
 
 #define MISSING_CLOSE_BRACE_JS \
+  u""\
   "// This PAC script is invalid, because there is a missing close brace\n" \
   "// on the function FindProxyForURL().\n" \
   "\n" \
@@ -225,10 +236,12 @@
   "\n" \
 
 #define NO_ENTRYPOINT_JS \
+  u""\
   "var x = \"This is an invalid PAC script because it lacks a \" +\n" \
   "        \"FindProxyForURL() function\";\n" \
 
 #define PAC_LIBRARY_UNITTEST_JS \
+  u""\
   "// This should output \"PROXY success:80\" if all the tests pass.\n" \
   "// Otherwise it will output \"PROXY failure:<num-failures>\".\n" \
   "//\n" \
@@ -597,6 +610,7 @@
   "\n" \
 
 #define PASSTHROUGH_JS \
+  u""\
   "// Return a single-proxy result, which encodes ALL the arguments that were\n" \
   "// passed to FindProxyForURL().\n" \
   "\n" \
@@ -644,48 +658,56 @@
   "}\n" \
 
 #define RETURN_EMPTY_STRING_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return \"\";\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_FUNCTION_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return FindProxyForURL;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_INTEGER_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return 0;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_NULL_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return null;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_OBJECT_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return {result: \"PROXY foo\"};\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_UNDEFINED_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  return undefined;\n" \
   "}\n" \
   "\n" \
 
 #define RETURN_UNICODE_JS \
+  u""\
   "// U+200B is the codepoint for zero-width-space.\n" \
   "function FindProxyForURL(url, host) {\n" \
   "  return \"PROXY foo.com\u200B\";\n" \
   "}\n" \
 
 #define SIDE_EFFECTS_JS \
+  u""\
   "if (!gCounter) {\n" \
   "  // We write it this way so if the script gets loaded twice,\n" \
   "  // gCounter remains dirty.\n" \
@@ -698,6 +720,7 @@
   "\n" \
 
 #define SIMPLE_JS \
+  u""\
   "// PAC script which uses isInNet on both IP addresses and hosts, and calls\n" \
   "// isResolvable().\n" \
   "\n" \
@@ -720,7 +743,14 @@
   "  return \"DIRECT\";\n" \
   "}\n" \
 
+#define STRING_FUNCTIONS_JS \
+  u""\
+  "function FindProxyForURL(url, host) {\n" \
+  "  return \"direct\".toUpperCase();\n" \
+  "}\n" \
+
 #define UNHANDLED_EXCEPTION_JS \
+  u""\
   "function FindProxyForURL(url, host) {\n" \
   "  // This will throw a runtime exception.\n" \
   "  return \"PROXY x\" + undefined_variable;\n" \