You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/12/02 20:58:22 UTC
[couchdb] branch sm60-davisp created (now 466d1d9)
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a change to branch sm60-davisp
in repository https://gitbox.apache.org/repos/asf/couchdb.git.
at 466d1d9 Allow configuring the use of SpiderMonkey 60
This branch includes the following new commits:
new 883c51f Add SpiderMonkey version option to configure
new 699ca1e Enable multi-version SpiderMonkey support
new 1f8cac4 Import SpiderMonkey 60 based CouchJS sources
new 466d1d9 Allow configuring the use of SpiderMonkey 60
The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[couchdb] 02/04: Enable multi-version SpiderMonkey support
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch sm60-davisp
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 699ca1ee2688b6e8b666619e4109e135700bff8d
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Mon Dec 2 13:54:26 2019 -0600
Enable multi-version SpiderMonkey support
---
.gitignore | 7 +---
src/couch/priv/couch_js/{ => 1.8.5}/help.h | 0
src/couch/priv/couch_js/{ => 1.8.5}/http.c | 0
src/couch/priv/couch_js/{ => 1.8.5}/http.h | 0
src/couch/priv/couch_js/{ => 1.8.5}/main.c | 0
src/couch/priv/couch_js/{ => 1.8.5}/utf8.c | 0
src/couch/priv/couch_js/{ => 1.8.5}/utf8.h | 0
src/couch/priv/couch_js/{ => 1.8.5}/util.c | 0
src/couch/priv/couch_js/{ => 1.8.5}/util.h | 0
src/couch/rebar.config.script | 59 +++++++++++++++++++++---------
10 files changed, 44 insertions(+), 22 deletions(-)
diff --git a/.gitignore b/.gitignore
index 6b9198d..54885d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,14 +33,11 @@ share/www
src/b64url/
src/bear/
src/config/
-src/couch/priv/couch_js/config.h
+src/couch/priv/couch_js/**/config.h
src/couch/priv/couchjs
src/couch/priv/couchspawnkillable
src/couch/priv/couch_ejson_compare/couch_ejson_compare.d
-src/couch/priv/couch_js/http.d
-src/couch/priv/couch_js/main.d
-src/couch/priv/couch_js/utf8.d
-src/couch/priv/couch_js/util.d
+src/couch/priv/couch_js/**/*.d
src/couch/priv/icu_driver/couch_icu_driver.d
src/mango/src/mango_cursor_text.nocompile
src/docs/
diff --git a/src/couch/priv/couch_js/help.h b/src/couch/priv/couch_js/1.8.5/help.h
similarity index 100%
rename from src/couch/priv/couch_js/help.h
rename to src/couch/priv/couch_js/1.8.5/help.h
diff --git a/src/couch/priv/couch_js/http.c b/src/couch/priv/couch_js/1.8.5/http.c
similarity index 100%
rename from src/couch/priv/couch_js/http.c
rename to src/couch/priv/couch_js/1.8.5/http.c
diff --git a/src/couch/priv/couch_js/http.h b/src/couch/priv/couch_js/1.8.5/http.h
similarity index 100%
rename from src/couch/priv/couch_js/http.h
rename to src/couch/priv/couch_js/1.8.5/http.h
diff --git a/src/couch/priv/couch_js/main.c b/src/couch/priv/couch_js/1.8.5/main.c
similarity index 100%
rename from src/couch/priv/couch_js/main.c
rename to src/couch/priv/couch_js/1.8.5/main.c
diff --git a/src/couch/priv/couch_js/utf8.c b/src/couch/priv/couch_js/1.8.5/utf8.c
similarity index 100%
rename from src/couch/priv/couch_js/utf8.c
rename to src/couch/priv/couch_js/1.8.5/utf8.c
diff --git a/src/couch/priv/couch_js/utf8.h b/src/couch/priv/couch_js/1.8.5/utf8.h
similarity index 100%
rename from src/couch/priv/couch_js/utf8.h
rename to src/couch/priv/couch_js/1.8.5/utf8.h
diff --git a/src/couch/priv/couch_js/util.c b/src/couch/priv/couch_js/1.8.5/util.c
similarity index 100%
rename from src/couch/priv/couch_js/util.c
rename to src/couch/priv/couch_js/1.8.5/util.c
diff --git a/src/couch/priv/couch_js/util.h b/src/couch/priv/couch_js/1.8.5/util.h
similarity index 100%
rename from src/couch/priv/couch_js/util.h
rename to src/couch/priv/couch_js/1.8.5/util.h
diff --git a/src/couch/rebar.config.script b/src/couch/rebar.config.script
index 846e8b4..f4c0705 100644
--- a/src/couch/rebar.config.script
+++ b/src/couch/rebar.config.script
@@ -54,6 +54,16 @@ CouchConfig = case filelib:is_file(os:getenv("COUCHDB_CONFIG")) of
[]
end.
+SMVsn = case lists:keyfind(spidermonkey_version, 1, CouchConfig) of
+ {_, "1.8.5"} ->
+ "1.8.5";
+ undefined ->
+ "1.8.5";
+ {_, Unsupported} ->
+ io:format(standard_error, "Unsupported SpiderMonkey version: ~s~n", [Unsupported]),
+ erlang:halt(1)
+end.
+
ConfigH = [
{"SM185", ""},
{"HAVE_JS_GET_STRING_CHARS_AND_LENGTH", "1"},
@@ -66,7 +76,7 @@ ConfigH = [
{"PACKAGE_VERSION", "\"" ++ Version ++ "\""}
],
-CouchJSConfig = "priv/couch_js/config.h",
+CouchJSConfig = "priv/couch_js/" ++ SMVsn ++ "/config.h",
ConfigSrc = [["#define ", K, " ", V, $\n] || {K, V} <- ConfigH],
ConfigBin = iolist_to_binary(ConfigSrc),
ok = CopyIfDifferent(CouchJSConfig, ConfigBin),
@@ -78,27 +88,45 @@ MD5Config = case lists:keyfind(erlang_md5, 1, CouchConfig) of
[]
end,
-%% TODO support curl on Windows
-{JS_CFLAGS, JS_LDFLAGS} = case lists:keyfind(with_curl, 1, CouchConfig) of
+{JS_CFLAGS, JS_LDFLAGS} = case os:type() of
+ {win32, _} when SMVsn == "1.8.5" ->
+ {
+ "/DXP_WIN /IC:\\relax\\js-1.8.5\\js\\src",
+ "/LIBPATH:C:\\relax\\js-1.8.5\\js\\src mozjs185-1.0.lib"
+ };
+ _ when SMVsn == "1.8.5" ->
+ {
+ "-DXP_UNIX -I/usr/inlude/js -I/usr/local/include/js",
+ "-L/usr/local/lib -lmozjs185 -lm"
+ }
+end.
+
+{CURL_CFLAGS, CURL_LDFLAGS} = case lists:keyfind(with_curl, 1, CouchConfig) of
{with_curl, true} ->
case os:type() of
{win32, _} ->
- {"/DHAVE_CURL /IC:\\relax\\curl\\include", "/LIBPATH:C:\\relax\\js-1.8.5\\js\\src /LIBPATH:C:\\Relax\\curl\\lib\\release-ssl mozjs185-1.0.lib libcurl_imp.lib"};
+ {
+ "/DHAVE_CURL /IC:\\relax\\curl\\include",
+ "/LIBPATH:C:\\Relax\\curl\\lib\\release-ssl libcurl_imp.lib"
+ };
{unix, freebsd} ->
- {"-DHAVE_CURL -I/usr/local/include", "-DHAVE_CURL -lmozjs185 -lcurl"};
+ {
+ "-DHAVE_CURL -I/usr/local/include",
+ "-DHAVE_CURL -lcurl"
+ };
_ ->
- {"-DHAVE_CURL", "-DHAVE_CURL -lmozjs185 -lcurl"}
+ {
+ "-DHAVE_CURL",
+ "-DHAVE_CURL -lcurl"
+ }
end;
_ ->
- case os:type() of
- {win32, _} ->
- {"", "/LIBPATH:C:\\relax\\js-1.8.5\\js\\src mozjs185-1.0.lib"};
- _ ->
- {"", "-lmozjs185"}
- end
+ {"", ""}
end,
-CouchJSSrc = ["priv/couch_js/*.c"],
+CouchJSSrc = case SMVsn of
+ "1.8.5" -> ["priv/couch_js/1.8.5/*.c"]
+end.
IcuPath = "priv/couch_icu_driver.so",
IcuSrc = ["priv/icu_driver/*.c"],
@@ -116,10 +144,7 @@ CompareSrc = ["priv/couch_ejson_compare/*.c"],
BaseSpecs = [
%% couchjs
- {"darwin", CouchJSPath, CouchJSSrc, [{env, [{"CFLAGS", JS_CFLAGS ++ " -DXP_UNIX -I/usr/local/include/js"}, {"LDFLAGS", JS_LDFLAGS ++ " -L/usr/local/lib"}]}]},
- {"linux", CouchJSPath, CouchJSSrc, [{env, [{"CFLAGS", JS_CFLAGS ++ " -DXP_UNIX -I/usr/include/js"}, {"LDFLAGS", JS_LDFLAGS ++ " -lm"}]}]},
- {"bsd", CouchJSPath, CouchJSSrc, [{env, [{"CFLAGS", JS_CFLAGS ++ " -DXP_UNIX -I/usr/local/include/js"}, {"LDFLAGS", JS_LDFLAGS ++ " -L/usr/local/lib -lm"}]}]},
- {"win32", CouchJSPath, CouchJSSrc, [{env, [{"CFLAGS", JS_CFLAGS ++ " /DXP_WIN /IC:\\relax\\js-1.8.5\\js\\src"}, {"LDFLAGS", JS_LDFLAGS}]}]},
+ {".*", CouchJSPath, CouchJSSrc, [{env, [{"CFLAGS", JS_CFLAGS ++ " " ++ CURL_CFLAGS}, {"LDFLAGS", JS_LDFLAGS ++ " " ++ CURL_LDFLAGS}]}]},
% ICU
{"darwin", IcuPath, IcuSrc, [{env, IcuEnv ++ IcuDarwinEnv}]},
{"linux", IcuPath, IcuSrc, [{env, IcuEnv}]},
[couchdb] 01/04: Add SpiderMonkey version option to configure
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch sm60-davisp
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 883c51f491419bf07c0b4cb804623236407d4489
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Mon Dec 2 13:52:08 2019 -0600
Add SpiderMonkey version option to configure
---
configure | 23 +++++++++++++++++++++++
configure.ps1 | 8 +++++++-
2 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/configure b/configure
index bcd7619..a77f4ea 100755
--- a/configure
+++ b/configure
@@ -30,6 +30,7 @@ ERLANG_MD5="false"
SKIP_DEPS=0
COUCHDB_USER="$(whoami 2>/dev/null || echo couchdb)"
+SM_VSN="1.8.5"
. ${rootdir}/version.mk
COUCHDB_VERSION=${vsn_major}.${vsn_minor}.${vsn_patch}
@@ -50,6 +51,7 @@ Options:
--disable-docs do not build any documentation or manpages
--erlang-md5 use erlang for md5 hash operations
--dev alias for --with-curl --disable-docs --disable-fauxton
+ --spidermonkey-version VSN specify the version of SpiderMonkey to use (defaults to $SM_VSN)
--skip-deps do not update erlang dependencies
--rebar=PATH use rebar by specified path (version >=2.6.0 && <3.0 required)
EOF
@@ -140,6 +142,24 @@ parse_opts() {
printf 'ERROR: "--user" requires a non-empty argument.\n' >&2
exit 1
;;
+
+ --spidermonkey-version)
+ if [ -n "$2" ]; then
+ eval SM_VSN=$2
+ shift 2
+ continue
+ else
+ printf 'ERROR: "--spidermonkey-version" requires a non-empty argument.\n' >&2
+ exit 1
+ fi
+ ;;
+ --spidermonkey-version=?*)
+ eval SM_VSN=${1#*=}
+ ;;
+ --spidermonkey-version=)
+ printf 'ERROR: "--spidermonkey-version" requires a non-empty argument.\n' >&2
+ exit 1
+ ;;
--) # End of options
shift
break
@@ -180,6 +200,7 @@ cat > rel/couchdb.config << EOF
{log_file, "$LOG_FILE"}.
{fauxton_root, "./share/www"}.
{user, "$COUCHDB_USER"}.
+{spidermonkey_version, "$SM_VSN"}.
{node_name, "-name couchdb@127.0.0.1"}.
{cluster_port, 5984}.
{backend_port, 5986}.
@@ -206,12 +227,14 @@ with_fauxton = $WITH_FAUXTON
with_docs = $WITH_DOCS
user = $COUCHDB_USER
+spidermonkey_version = $SM_VSN
EOF
cat > $rootdir/config.erl << EOF
{with_curl, $WITH_CURL}.
{with_proper, $WITH_PROPER}.
{erlang_md5, $ERLANG_MD5}.
+{spidermonkey_version, "$SM_VSN"}.
EOF
install_local_rebar() {
diff --git a/configure.ps1 b/configure.ps1
index 418cac3..9888a9f 100644
--- a/configure.ps1
+++ b/configure.ps1
@@ -10,6 +10,7 @@
-DisableDocs request build process skip building documentation (default false)
-SkipDeps do not update Erlang dependencies (default false)
-CouchDBUser USER set the username to run as (defaults to current user)
+ -SpiderMonkeyVersion VSN select the version of SpiderMonkey to use (defaults to 1.8.5)
Installation directories:
-Prefix PREFIX install architecture-independent files in PREFIX
@@ -49,6 +50,8 @@ Param(
[ValidateNotNullOrEmpty()]
[string]$CouchDBUser = [Environment]::UserName, # set the username to run as (defaults to current user)
[ValidateNotNullOrEmpty()]
+ [string]$SpiderMonkeyVersion = "1.8.5", # select the version of SpiderMonkey to use (default 1.8.5)
+ [ValidateNotNullOrEmpty()]
[string]$Prefix = "C:\Program Files\Apache\CouchDB", # install architecture-independent file location (default C:\Program Files\Apache\CouchDB)
[ValidateNotNullOrEmpty()]
[string]$ExecPrefix = $Prefix, # install architecture-dependent file location (default C:\Program Files\Apache\CouchDB)
@@ -133,6 +136,7 @@ $CouchDBConfig = @"
{log_file, ""}.
{fauxton_root, "./share/www"}.
{user, "$CouchDBUser"}.
+{spidermonkey_version, "$SpiderMonkeyVersion"}.
{node_name, "-name couchdb@localhost"}.
{cluster_port, 5984}.
{backend_port, 5986}.
@@ -144,7 +148,7 @@ $InstallMk = @"
# 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
@@ -175,12 +179,14 @@ with_fauxton = $BuildFauxton
with_docs = $BuildDocs
user = $CouchDBUser
+spidermonkey_version = $SpiderMonkeyVersion
"@
$InstallMk | Out-File "$rootdir\install.mk" -encoding ascii
$lowercurl = "$WithCurl".ToLower()
$ConfigERL = @"
{with_curl, $lowercurl}.
+{spidermonkey_version, "$SpiderMonkeyVersion"}.
"@
$ConfigERL | Out-File "$rootdir\config.erl" -encoding ascii
[couchdb] 04/04: Allow configuring the use of SpiderMonkey 60
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch sm60-davisp
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 466d1d9e6107cdef8e49315837e7e3ea52aa747e
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Mon Dec 2 14:55:06 2019 -0600
Allow configuring the use of SpiderMonkey 60
---
src/couch/rebar.config.script | 36 +++++++++++++++++++++++++++++++-----
1 file changed, 31 insertions(+), 5 deletions(-)
diff --git a/src/couch/rebar.config.script b/src/couch/rebar.config.script
index f4c0705..e384a83 100644
--- a/src/couch/rebar.config.script
+++ b/src/couch/rebar.config.script
@@ -57,6 +57,8 @@ end.
SMVsn = case lists:keyfind(spidermonkey_version, 1, CouchConfig) of
{_, "1.8.5"} ->
"1.8.5";
+ {_, "60"} ->
+ "60";
undefined ->
"1.8.5";
{_, Unsupported} ->
@@ -92,12 +94,22 @@ end,
{win32, _} when SMVsn == "1.8.5" ->
{
"/DXP_WIN /IC:\\relax\\js-1.8.5\\js\\src",
- "/LIBPATH:C:\\relax\\js-1.8.5\\js\\src mozjs185-1.0.lib"
+ "/LIBPATH:C:\\relax\\js-1.8.5\\js\\src mozjs60.lib"
};
- _ when SMVsn == "1.8.5" ->
+ {unix, _} when SMVsn == "1.8.5" ->
{
- "-DXP_UNIX -I/usr/inlude/js -I/usr/local/include/js",
+ "-DXP_UNIX -I/usr/include/js -I/usr/local/include/js",
"-L/usr/local/lib -lmozjs185 -lm"
+ };
+ {win32, _} when SMVsn == "60" ->
+ {
+ "/DXP_WIN /IC:\\relax\\js-60\\js\\src",
+ "/LIBPATH:C:\\relax\\js-60\\js\\src mozjs60.0.lib"
+ };
+ {unix, _} when SMVsn == "60" ->
+ {
+ "-DXP_UNIX -I/usr/include/mozjs-60 -I/usr/local/include/mozjs-60 -std=c++14",
+ "-L/usr/local/lib -std=c++14 -lmozjs-60 -lm"
}
end.
@@ -125,9 +137,23 @@ end.
end,
CouchJSSrc = case SMVsn of
- "1.8.5" -> ["priv/couch_js/1.8.5/*.c"]
+ "1.8.5" -> ["priv/couch_js/1.8.5/*.c"];
+ "60" -> ["priv/couch_js/60/*.cpp"]
end.
+CouchJSEnv = case SMVsn of
+ "1.8.5" ->
+ [
+ {"CFLAGS", JS_CFLAGS ++ " " ++ CURL_CFLAGS},
+ {"LDFLAGS", JS_LDFLAGS ++ " " ++ CURL_LDFLAGS}
+ ];
+ "60" ->
+ [
+ {"CXXFLAGS", JS_CFLAGS ++ " " ++ CURL_CFLAGS},
+ {"LDFLAGS", JS_LDFLAGS ++ " " ++ CURL_LDFLAGS}
+ ]
+end,
+
IcuPath = "priv/couch_icu_driver.so",
IcuSrc = ["priv/icu_driver/*.c"],
IcuEnv = [{"DRV_CFLAGS", "$DRV_CFLAGS -DPIC -O2 -fno-common"},
@@ -144,7 +170,7 @@ CompareSrc = ["priv/couch_ejson_compare/*.c"],
BaseSpecs = [
%% couchjs
- {".*", CouchJSPath, CouchJSSrc, [{env, [{"CFLAGS", JS_CFLAGS ++ " " ++ CURL_CFLAGS}, {"LDFLAGS", JS_LDFLAGS ++ " " ++ CURL_LDFLAGS}]}]},
+ {".*", CouchJSPath, CouchJSSrc, [{env, CouchJSEnv}]},
% ICU
{"darwin", IcuPath, IcuSrc, [{env, IcuEnv ++ IcuDarwinEnv}]},
{"linux", IcuPath, IcuSrc, [{env, IcuEnv}]},
[couchdb] 03/04: Import SpiderMonkey 60 based CouchJS sources
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch sm60-davisp
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 1f8cac429ea76a387b24a246902b2e6fde21d07f
Author: jiangph <ji...@cn.ibm.com>
AuthorDate: Thu Sep 19 17:27:34 2019 +0800
Import SpiderMonkey 60 based CouchJS sources
Co-Authored-By: Jan Lehnardt <ja...@apache.org>
---
src/couch/priv/couch_js/60/help.h | 86 +++++
src/couch/priv/couch_js/60/http.cpp | 707 ++++++++++++++++++++++++++++++++++++
src/couch/priv/couch_js/60/http.h | 27 ++
src/couch/priv/couch_js/60/main.cpp | 493 +++++++++++++++++++++++++
src/couch/priv/couch_js/60/utf8.cpp | 301 +++++++++++++++
src/couch/priv/couch_js/60/utf8.h | 19 +
src/couch/priv/couch_js/60/util.cpp | 323 ++++++++++++++++
src/couch/priv/couch_js/60/util.h | 41 +++
8 files changed, 1997 insertions(+)
diff --git a/src/couch/priv/couch_js/60/help.h b/src/couch/priv/couch_js/60/help.h
new file mode 100644
index 0000000..678651f
--- /dev/null
+++ b/src/couch/priv/couch_js/60/help.h
@@ -0,0 +1,86 @@
+// 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 COUCHJS_HELP_H
+#define COUCHJS_HELP_H
+
+#include "config.h"
+
+static const char VERSION_TEMPLATE[] =
+ "%s - %s\n"
+ "\n"
+ "Licensed under the Apache License, Version 2.0 (the \"License\"); you may "
+ "not use\n"
+ "this file except in compliance with the License. You may obtain a copy of"
+ "the\n"
+ "License at\n"
+ "\n"
+ " http://www.apache.org/licenses/LICENSE-2.0\n"
+ "\n"
+ "Unless required by applicable law or agreed to in writing, software "
+ "distributed\n"
+ "under the License is distributed on an \"AS IS\" BASIS, WITHOUT "
+ "WARRANTIES OR\n"
+ "CONDITIONS OF ANY KIND, either express or implied. See the License "
+ "for the\n"
+ "specific language governing permissions and limitations under the "
+ "License.\n";
+
+static const char USAGE_TEMPLATE[] =
+ "Usage: %s [FILE]\n"
+ "\n"
+ "The %s command runs the %s JavaScript interpreter.\n"
+ "\n"
+ "The exit status is 0 for success or 1 for failure.\n"
+ "\n"
+ "Options:\n"
+ "\n"
+ " -h display a short help message and exit\n"
+ " -V display version information and exit\n"
+ " -H enable %s cURL bindings (only avaiable\n"
+ " if package was built with cURL available)\n"
+ " -T enable test suite specific functions (these\n"
+ " should not be enabled for production systems)\n"
+ " -S SIZE specify that the runtime should allow at\n"
+ " most SIZE bytes of memory to be allocated\n"
+ " default is 64 MiB\n"
+ " -u FILE path to a .uri file containing the address\n"
+ " (or addresses) of one or more servers\n"
+ " --eval Enable runtime code evaluation (dangerous!)\n"
+ "\n"
+ "Report bugs at <%s>.\n";
+
+#define BASENAME COUCHJS_NAME
+
+#define couch_version(basename) \
+ fprintf( \
+ stdout, \
+ VERSION_TEMPLATE, \
+ basename, \
+ PACKAGE_STRING)
+
+#define DISPLAY_VERSION couch_version(BASENAME)
+
+
+#define couch_usage(basename) \
+ fprintf( \
+ stdout, \
+ USAGE_TEMPLATE, \
+ basename, \
+ basename, \
+ PACKAGE_NAME, \
+ basename, \
+ PACKAGE_BUGREPORT)
+
+#define DISPLAY_USAGE couch_usage(BASENAME)
+
+#endif // Included help.h
diff --git a/src/couch/priv/couch_js/60/http.cpp b/src/couch/priv/couch_js/60/http.cpp
new file mode 100644
index 0000000..f500102
--- /dev/null
+++ b/src/couch/priv/couch_js/60/http.cpp
@@ -0,0 +1,707 @@
+// 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <jsapi.h>
+#include <js/Initialization.h>
+#include "config.h"
+#include "utf8.h"
+#include "util.h"
+
+// Soft dependency on cURL bindings because they're
+// only used when running the JS tests from the
+// command line which is rare.
+#ifndef HAVE_CURL
+
+void
+http_check_enabled()
+{
+ fprintf(stderr, "HTTP API was disabled at compile time.\n");
+ exit(3);
+}
+
+
+bool
+http_ctor(JSContext* cx, JSObject* req)
+{
+ return false;
+}
+
+
+bool
+http_dtor(JSFreeOp* fop, JSObject* req)
+{
+ return false;
+}
+
+
+bool
+http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value snc)
+{
+ return false;
+}
+
+
+bool
+http_set_hdr(JSContext* cx, JSObject* req, JS::Value name, JS::Value val)
+{
+ return false;
+}
+
+
+bool
+http_send(JSContext* cx, JSObject* req, JS::Value body)
+{
+ return false;
+}
+
+
+int
+http_status(JSContext* cx, JSObject* req)
+{
+ return -1;
+}
+
+bool
+http_uri(JSContext* cx, JSObject* req, couch_args* args, JS::Value* uri_val)
+{
+ return false;
+}
+
+
+#else
+#include <curl/curl.h>
+#ifndef XP_WIN
+#include <unistd.h>
+#endif
+
+
+void
+http_check_enabled()
+{
+ return;
+}
+
+
+// Map some of the string function names to things which exist on Windows
+#ifdef XP_WIN
+#define strcasecmp _strcmpi
+#define strncasecmp _strnicmp
+#define snprintf _snprintf
+#endif
+
+
+typedef struct curl_slist CurlHeaders;
+
+
+typedef struct {
+ int method;
+ char* url;
+ CurlHeaders* req_headers;
+ int16_t last_status;
+} HTTPData;
+
+
+const char* METHODS[] = {"GET", "HEAD", "POST", "PUT", "DELETE", "COPY", "OPTIONS", NULL};
+
+
+#define GET 0
+#define HEAD 1
+#define POST 2
+#define PUT 3
+#define DELETE 4
+#define COPY 5
+#define OPTIONS 6
+
+
+static bool
+go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t blen);
+
+
+static JSString*
+str_from_binary(JSContext* cx, char* data, size_t length);
+
+
+bool
+http_ctor(JSContext* cx, JSObject* req)
+{
+ HTTPData* http = NULL;
+ bool ret = false;
+
+ http = (HTTPData*) malloc(sizeof(HTTPData));
+ if(!http)
+ {
+ JS_ReportErrorUTF8(cx, "Failed to create CouchHTTP instance.");
+ goto error;
+ }
+
+ http->method = -1;
+ http->url = NULL;
+ http->req_headers = NULL;
+ http->last_status = -1;
+
+ JS_SetPrivate(req, http);
+
+ ret = true;
+ goto success;
+
+error:
+ if(http) free(http);
+
+success:
+ return ret;
+}
+
+
+void
+http_dtor(JSFreeOp* fop, JSObject* obj)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(obj);
+ if(http) {
+ if(http->url) free(http->url);
+ if(http->req_headers) curl_slist_free_all(http->req_headers);
+ free(http);
+ }
+}
+
+
+bool
+http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value snc)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(req);
+ char* method = NULL;
+ int methid;
+ bool ret = false;
+
+ if(!http) {
+ JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
+ goto done;
+ }
+
+ if(mth.isUndefined()) {
+ JS_ReportErrorUTF8(cx, "You must specify a method.");
+ goto done;
+ }
+
+ method = enc_string(cx, mth, NULL);
+ if(!method) {
+ JS_ReportErrorUTF8(cx, "Failed to encode method.");
+ goto done;
+ }
+
+ for(methid = 0; METHODS[methid] != NULL; methid++) {
+ if(strcasecmp(METHODS[methid], method) == 0) break;
+ }
+
+ if(methid > OPTIONS) {
+ JS_ReportErrorUTF8(cx, "Invalid method specified.");
+ goto done;
+ }
+
+ http->method = methid;
+
+ if(url.isUndefined()) {
+ JS_ReportErrorUTF8(cx, "You must specify a URL.");
+ goto done;
+ }
+
+ if(http->url != NULL) {
+ free(http->url);
+ http->url = NULL;
+ }
+
+ http->url = enc_string(cx, url, NULL);
+ if(http->url == NULL) {
+ JS_ReportErrorUTF8(cx, "Failed to encode URL.");
+ goto done;
+ }
+
+ if(snc.isBoolean() && snc.isTrue()) {
+ JS_ReportErrorUTF8(cx, "Synchronous flag must be false.");
+ goto done;
+ }
+
+ if(http->req_headers) {
+ curl_slist_free_all(http->req_headers);
+ http->req_headers = NULL;
+ }
+
+ // Disable Expect: 100-continue
+ http->req_headers = curl_slist_append(http->req_headers, "Expect:");
+
+ ret = true;
+
+done:
+ if(method) free(method);
+ return ret;
+}
+
+
+bool
+http_set_hdr(JSContext* cx, JSObject* req, JS::Value name, JS::Value val)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(req);
+ char* keystr = NULL;
+ char* valstr = NULL;
+ char* hdrbuf = NULL;
+ size_t hdrlen = -1;
+ bool ret = false;
+
+ if(!http) {
+ JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
+ goto done;
+ }
+
+ if(name.isUndefined())
+ {
+ JS_ReportErrorUTF8(cx, "You must speciy a header name.");
+ goto done;
+ }
+
+ keystr = enc_string(cx, name, NULL);
+ if(!keystr)
+ {
+ JS_ReportErrorUTF8(cx, "Failed to encode header name.");
+ goto done;
+ }
+
+ if(val.isUndefined())
+ {
+ JS_ReportErrorUTF8(cx, "You must specify a header value.");
+ goto done;
+ }
+
+ valstr = enc_string(cx, val, NULL);
+ if(!valstr)
+ {
+ JS_ReportErrorUTF8(cx, "Failed to encode header value.");
+ goto done;
+ }
+
+ hdrlen = strlen(keystr) + strlen(valstr) + 3;
+ hdrbuf = (char*) malloc(hdrlen * sizeof(char));
+ if(!hdrbuf) {
+ JS_ReportErrorUTF8(cx, "Failed to allocate header buffer.");
+ goto done;
+ }
+
+ snprintf(hdrbuf, hdrlen, "%s: %s", keystr, valstr);
+ http->req_headers = curl_slist_append(http->req_headers, hdrbuf);
+
+ ret = true;
+
+done:
+ if(keystr) free(keystr);
+ if(valstr) free(valstr);
+ if(hdrbuf) free(hdrbuf);
+ return ret;
+}
+
+bool
+http_send(JSContext* cx, JSObject* req, JS::Value body)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(req);
+ char* bodystr = NULL;
+ size_t bodylen = 0;
+ bool ret = false;
+
+ if(!http) {
+ JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
+ goto done;
+ }
+
+ if(!body.isUndefined()) {
+ bodystr = enc_string(cx, body, &bodylen);
+ if(!bodystr) {
+ JS_ReportErrorUTF8(cx, "Failed to encode body.");
+ goto done;
+ }
+ }
+
+ ret = go(cx, req, http, bodystr, bodylen);
+
+done:
+ if(bodystr) free(bodystr);
+ return ret;
+}
+
+int
+http_status(JSContext* cx, JSObject* req)
+{
+ HTTPData* http = (HTTPData*) JS_GetPrivate(req);
+
+ if(!http) {
+ JS_ReportErrorUTF8(cx, "Invalid CouchHTTP instance.");
+ return false;
+ }
+
+ return http->last_status;
+}
+
+bool
+http_uri(JSContext* cx, JSObject* req, couch_args* args, JS::Value* uri_val)
+{
+ FILE* uri_fp = NULL;
+ JSString* uri_str;
+
+ // Default is http://localhost:15986/ when no uri file is specified
+ if (!args->uri_file) {
+ uri_str = JS_NewStringCopyZ(cx, "http://localhost:15986/");
+ *uri_val = JS::StringValue(uri_str);
+ JS_SetReservedSlot(req, 0, *uri_val);
+ return true;
+ }
+
+ // Else check to see if the base url is cached in a reserved slot
+ *uri_val = JS_GetReservedSlot(req, 0);
+ if (!(*uri_val).isUndefined()) {
+ return true;
+ }
+
+ // Read the first line of the couch.uri file.
+ if(!((uri_fp = fopen(args->uri_file, "r")) &&
+ (uri_str = couch_readline(cx, uri_fp)))) {
+ JS_ReportErrorUTF8(cx, "Failed to read couch.uri file.");
+ goto error;
+ }
+
+ fclose(uri_fp);
+ *uri_val = JS::StringValue(uri_str);
+ JS_SetReservedSlot(req, 0, *uri_val);
+ return true;
+
+error:
+ if(uri_fp) fclose(uri_fp);
+ return false;
+}
+
+
+// Curl Helpers
+
+typedef struct {
+ HTTPData* http;
+ JSContext* cx;
+ JSObject* resp_headers;
+ char* sendbuf;
+ size_t sendlen;
+ size_t sent;
+ int sent_once;
+ char* recvbuf;
+ size_t recvlen;
+ size_t read;
+} CurlState;
+
+/*
+ * I really hate doing this but this doesn't have to be
+ * uber awesome, it just has to work.
+ */
+CURL* HTTP_HANDLE = NULL;
+char ERRBUF[CURL_ERROR_SIZE];
+
+static size_t send_body(void *ptr, size_t size, size_t nmem, void *data);
+static int seek_body(void *ptr, curl_off_t offset, int origin);
+static size_t recv_body(void *ptr, size_t size, size_t nmem, void *data);
+static size_t recv_header(void *ptr, size_t size, size_t nmem, void *data);
+
+static bool
+go(JSContext* cx, JSObject* obj, HTTPData* http, char* body, size_t bodylen)
+{
+ CurlState state;
+ char* referer;
+ JSString* jsbody;
+ bool ret = false;
+ JS::Value tmp;
+ JS::RootedObject robj(cx, obj);
+ JS::RootedValue vobj(cx);
+
+
+ state.cx = cx;
+ state.http = http;
+
+ state.sendbuf = body;
+ state.sendlen = bodylen;
+ state.sent = 0;
+ state.sent_once = 0;
+
+ state.recvbuf = NULL;
+ state.recvlen = 0;
+ state.read = 0;
+
+ if(HTTP_HANDLE == NULL) {
+ HTTP_HANDLE = curl_easy_init();
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_READFUNCTION, send_body);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKFUNCTION,
+ (curl_seek_callback) seek_body);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_HEADERFUNCTION, recv_header);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEFUNCTION, recv_body);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_ERRORBUFFER, ERRBUF);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_COOKIEFILE, "");
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_USERAGENT,
+ "CouchHTTP Client - Relax");
+ }
+
+ if(!HTTP_HANDLE) {
+ JS_ReportErrorUTF8(cx, "Failed to initialize cURL handle.");
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+
+ tmp = JS_GetReservedSlot(obj, 0);
+
+ if(!(referer = enc_string(cx, tmp, NULL))) {
+ JS_ReportErrorUTF8(cx, "Failed to encode referer.");
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_REFERER, referer);
+ free(referer);
+
+ if(http->method < 0 || http->method > OPTIONS) {
+ JS_ReportErrorUTF8(cx, "INTERNAL: Unknown method.");
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_CUSTOMREQUEST, METHODS[http->method]);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 0);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 0);
+
+ if(http->method == HEAD) {
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_NOBODY, 1);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
+ } else if(http->method == POST || http->method == PUT) {
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_FOLLOWLOCATION, 0);
+ }
+
+ if(body && bodylen) {
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, bodylen);
+ } else {
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_INFILESIZE, 0);
+ }
+
+ // curl_easy_setopt(HTTP_HANDLE, CURLOPT_VERBOSE, 1);
+
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_URL, http->url);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_HTTPHEADER, http->req_headers);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_READDATA, &state);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_SEEKDATA, &state);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEHEADER, &state);
+ curl_easy_setopt(HTTP_HANDLE, CURLOPT_WRITEDATA, &state);
+
+ if(curl_easy_perform(HTTP_HANDLE) != 0) {
+ JS_ReportErrorUTF8(cx, "Failed to execute HTTP request: %s", ERRBUF);
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+
+ if(!state.resp_headers) {
+ JS_ReportErrorUTF8(cx, "Failed to recieve HTTP headers.");
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+ tmp = JS::ObjectValue(*state.resp_headers);
+ JS::RootedValue rtmp(cx, tmp);
+
+ if(!JS_DefineProperty(
+ cx, robj,
+ "_headers",
+ rtmp,
+ JSPROP_READONLY
+ )) {
+ JS_ReportErrorUTF8(cx, "INTERNAL: Failed to set response headers.");
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;;
+ }
+
+ if(state.recvbuf) {
+ state.recvbuf[state.read] = '\0';
+ jsbody = dec_string(cx, state.recvbuf, state.read+1);
+ if(!jsbody) {
+ // If we can't decode the body as UTF-8 we forcefully
+ // convert it to a string by just forcing each byte
+ // to a char16_t.
+ jsbody = str_from_binary(cx, state.recvbuf, state.read);
+ if(!jsbody) {
+ if(!JS_IsExceptionPending(cx)) {
+ JS_ReportErrorUTF8(cx, "INTERNAL: Failed to decode body.");
+ }
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+ }
+ tmp = JS::StringValue(jsbody);
+ } else {
+ tmp = JS_GetEmptyStringValue(cx);
+ }
+
+ JS::RootedValue rtmp2(cx, tmp);
+
+ if(!JS_DefineProperty(
+ cx, robj,
+ "responseText",
+ rtmp2,
+ JSPROP_READONLY
+ )) {
+ JS_ReportErrorUTF8(cx, "INTERNAL: Failed to set responseText.");
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+ }
+
+ ret = true;
+ if(state.recvbuf) JS_free(cx, state.recvbuf);
+ return ret;
+}
+
+static size_t
+send_body(void *ptr, size_t size, size_t nmem, void *data)
+{
+ CurlState* state = (CurlState*) data;
+ size_t length = size * nmem;
+ size_t towrite = state->sendlen - state->sent;
+
+ // Assume this is cURL trying to resend a request that
+ // failed.
+ if(towrite == 0 && state->sent_once == 0) {
+ state->sent_once = 1;
+ return 0;
+ } else if(towrite == 0) {
+ state->sent = 0;
+ state->sent_once = 0;
+ towrite = state->sendlen;
+ }
+
+ if(length < towrite) towrite = length;
+
+ memcpy(ptr, state->sendbuf + state->sent, towrite);
+ state->sent += towrite;
+
+ return towrite;
+}
+
+static int
+seek_body(void* ptr, curl_off_t offset, int origin)
+{
+ CurlState* state = (CurlState*) ptr;
+ if(origin != SEEK_SET) return -1;
+
+ state->sent = (size_t) offset;
+ return (int) state->sent;
+}
+
+static size_t
+recv_header(void *ptr, size_t size, size_t nmem, void *data)
+{
+ CurlState* state = (CurlState*) data;
+ char code[4];
+ char* header = (char*) ptr;
+ size_t length = size * nmem;
+ JSString* hdr = NULL;
+ uint32_t hdrlen;
+
+ if(length > 7 && strncasecmp(header, "HTTP/1.", 7) == 0) {
+ if(length < 12) {
+ return CURLE_WRITE_ERROR;
+ }
+
+ memcpy(code, header+9, 3*sizeof(char));
+ code[3] = '\0';
+ state->http->last_status = atoi(code);
+
+ state->resp_headers = JS_NewArrayObject(state->cx, 0);
+ if(!state->resp_headers) {
+ return CURLE_WRITE_ERROR;
+ }
+
+ return length;
+ }
+
+ // We get a notice at the \r\n\r\n after headers.
+ if(length <= 2) {
+ return length;
+ }
+
+ // Append the new header to our array.
+ hdr = dec_string(state->cx, header, length);
+ if(!hdr) {
+ return CURLE_WRITE_ERROR;
+ }
+
+ JS::RootedObject obj(state->cx, state->resp_headers);
+ if(!JS_GetArrayLength(state->cx, obj, &hdrlen)) {
+ return CURLE_WRITE_ERROR;
+ }
+
+ JS::RootedString hdrval(state->cx, hdr);
+ if(!JS_SetElement(state->cx, obj, hdrlen, hdrval)) {
+ return CURLE_WRITE_ERROR;
+ }
+
+ return length;
+}
+
+static size_t
+recv_body(void *ptr, size_t size, size_t nmem, void *data)
+{
+ CurlState* state = (CurlState*) data;
+ size_t length = size * nmem;
+ char* tmp = NULL;
+
+ if(!state->recvbuf) {
+ state->recvlen = 4096;
+ state->read = 0;
+ state->recvbuf = (char *)JS_malloc(state->cx, state->recvlen);
+ }
+
+ if(!state->recvbuf) {
+ return CURLE_WRITE_ERROR;
+ }
+
+ // +1 so we can add '\0' back up in the go function.
+ size_t oldlen = state->recvlen;
+ while(length+1 > state->recvlen - state->read) state->recvlen *= 2;
+ tmp = (char *) JS_realloc(state->cx, state->recvbuf, oldlen, state->recvlen);
+ if(!tmp) return CURLE_WRITE_ERROR;
+ state->recvbuf = tmp;
+
+ memcpy(state->recvbuf + state->read, ptr, length);
+ state->read += length;
+ return length;
+}
+
+JSString*
+str_from_binary(JSContext* cx, char* data, size_t length)
+{
+ char16_t* conv = (char16_t*) JS_malloc(cx, length * sizeof(char16_t));
+ JSString* ret = NULL;
+ size_t i;
+
+ if(!conv) return NULL;
+
+ for(i = 0; i < length; i++) {
+ conv[i] = (char16_t) data[i];
+ }
+
+ ret = JS_NewUCString(cx, conv, length);
+ if(!ret) JS_free(cx, conv);
+
+ return ret;
+}
+
+#endif /* HAVE_CURL */
diff --git a/src/couch/priv/couch_js/60/http.h b/src/couch/priv/couch_js/60/http.h
new file mode 100644
index 0000000..797b3c0
--- /dev/null
+++ b/src/couch/priv/couch_js/60/http.h
@@ -0,0 +1,27 @@
+// 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 COUCH_JS_HTTP_H
+#define COUCH_JS_HTTP_H
+
+#include "util.h"
+
+void http_check_enabled();
+bool http_ctor(JSContext* cx, JSObject* req);
+void http_dtor(JSFreeOp* fop, JSObject* req);
+bool http_open(JSContext* cx, JSObject* req, JS::Value mth, JS::Value url, JS::Value snc);
+bool http_set_hdr(JSContext* cx, JSObject* req, JS::Value name, JS::Value val);
+bool http_send(JSContext* cx, JSObject* req, JS::Value body);
+int http_status(JSContext* cx, JSObject* req);
+bool http_uri(JSContext* cx, JSObject *req, couch_args* args, JS::Value* uri);
+
+#endif
diff --git a/src/couch/priv/couch_js/60/main.cpp b/src/couch/priv/couch_js/60/main.cpp
new file mode 100644
index 0000000..201cb03
--- /dev/null
+++ b/src/couch/priv/couch_js/60/main.cpp
@@ -0,0 +1,493 @@
+// 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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef XP_WIN
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+#include <jsapi.h>
+#include <js/Initialization.h>
+#include <js/Conversions.h>
+#include <js/Wrapper.h>
+
+#include "config.h"
+#include "http.h"
+#include "utf8.h"
+#include "util.h"
+
+static bool enableSharedMemory = true;
+
+static JSClassOps global_ops = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ JS_GlobalObjectTraceHook
+};
+
+/* The class of the global object. */
+static JSClass global_class = {
+ "global",
+ JSCLASS_GLOBAL_FLAGS,
+ &global_ops
+};
+
+
+static void
+req_dtor(JSFreeOp* fop, JSObject* obj)
+{
+ http_dtor(fop, obj);
+}
+
+// With JSClass.construct.
+static const JSClassOps clsOps = {
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ req_dtor,
+ nullptr,
+ nullptr,
+ nullptr
+};
+
+static const JSClass CouchHTTPClass = {
+ "CouchHTTP", /* name */
+ JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2), /* flags */
+ &clsOps
+};
+
+static bool
+req_ctor(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ bool ret;
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JSObject* obj = JS_NewObjectForConstructor(cx, &CouchHTTPClass, args);
+ if(!obj) {
+ JS_ReportErrorUTF8(cx, "Failed to create CouchHTTP instance.\n", NULL);
+ return false;
+ }
+ ret = http_ctor(cx, obj);
+ args.rval().setObject(*obj);
+ return ret;
+}
+
+static bool
+req_open(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ bool ret = false;
+
+ if(argc == 2) {
+ ret = http_open(cx, obj, args[0], args[1], JS::BooleanValue(false));
+ } else if(argc == 3) {
+ ret = http_open(cx, obj, args[0], args[1], args[2]);
+ } else {
+ JS_ReportErrorUTF8(cx, "Invalid call to CouchHTTP.open");
+ }
+
+ args.rval().setUndefined();
+ return ret;
+}
+
+
+static bool
+req_set_hdr(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ bool ret = false;
+
+ if(argc == 2) {
+ ret = http_set_hdr(cx, obj, args[0], args[1]);
+ } else {
+ JS_ReportErrorUTF8(cx, "Invalid call to CouchHTTP.set_header");
+ }
+
+ args.rval().setUndefined();
+ return ret;
+}
+
+
+static bool
+req_send(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ bool ret = false;
+
+ if(argc == 1) {
+ ret = http_send(cx, obj, args[0]);
+ } else {
+ JS_ReportErrorUTF8(cx, "Invalid call to CouchHTTP.send");
+ }
+
+ args.rval().setUndefined();
+ return ret;
+}
+
+static bool
+req_status(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ int status = http_status(cx, obj);
+
+ if(status < 0)
+ return false;
+
+ args.rval().set(JS::Int32Value(status));
+ return true;
+}
+
+static bool
+base_url(JSContext *cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JSObject* obj = JS_THIS_OBJECT(cx, vp);
+ couch_args *cargs = (couch_args*)JS_GetContextPrivate(cx);
+ JS::Value uri_val;
+ bool rc = http_uri(cx, obj, cargs, &uri_val);
+ args.rval().set(uri_val);
+ return rc;
+}
+
+static void
+SetStandardCompartmentOptions(JS::CompartmentOptions& options)
+{
+ options.creationOptions().setSharedMemoryAndAtomicsEnabled(enableSharedMemory);
+}
+
+static JSObject*
+NewSandbox(JSContext* cx, bool lazy)
+{
+ JS::CompartmentOptions options;
+ SetStandardCompartmentOptions(options);
+ JS::RootedObject obj(cx, JS_NewGlobalObject(cx, &global_class, nullptr,
+ JS::DontFireOnNewGlobalHook, options));
+ if (!obj)
+ return nullptr;
+
+ {
+ JSAutoCompartment ac(cx, obj);
+ if (!lazy && !JS_InitStandardClasses(cx, obj))
+ return nullptr;
+
+ JS::RootedValue value(cx, JS::BooleanValue(lazy));
+ if (!JS_DefineProperty(cx, obj, "lazy", value, JSPROP_PERMANENT | JSPROP_READONLY))
+ return nullptr;
+
+ JS_FireOnNewGlobalObject(cx, obj);
+ }
+
+ if (!JS_WrapObject(cx, &obj))
+ return nullptr;
+ return obj;
+}
+
+static bool
+evalcx(JSContext *cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ bool ret = false;
+
+ JS::RootedString str(cx, JS::ToString(cx, args[0]));
+ if (!str)
+ return false;
+
+ JS::RootedObject sandbox(cx);
+ if (args.hasDefined(1)) {
+ sandbox = JS::ToObject(cx, args[1]);
+ if (!sandbox)
+ return false;
+ }
+ JS_BeginRequest(cx);
+ JSAutoRequest ar(cx);
+
+ js::AutoStableStringChars strChars(cx);
+ if (!strChars.initTwoByte(cx, str))
+ return false;
+
+ mozilla::Range<const char16_t> chars = strChars.twoByteRange();
+ size_t srclen = chars.length();
+ const char16_t* src = chars.begin().get();
+
+ if (!sandbox) {
+ sandbox = NewSandbox(cx, false);
+ if (!sandbox)
+ return false;
+ }
+
+ if(srclen == 0) {
+ args.rval().setObject(*sandbox);
+ } else {
+ mozilla::Maybe<JSAutoCompartment> ac;
+ unsigned flags;
+ JSObject* unwrapped = UncheckedUnwrap(sandbox, true, &flags);
+ if (flags & js::Wrapper::CROSS_COMPARTMENT) {
+ sandbox = unwrapped;
+ ac.emplace(cx, sandbox);
+ }
+
+ JS::CompileOptions opts(cx);
+ JS::RootedValue rval(cx);
+ opts.setFileAndLine(__FILE__, __LINE__);
+ if (!JS::Evaluate(cx, opts, src, srclen, args.rval())) {
+ return false;
+ }
+ }
+ ret = true;
+ if (!JS_WrapValue(cx, args.rval()))
+ return false;
+
+ return ret;
+}
+
+
+static bool
+gc(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS_GC(cx);
+ args.rval().setUndefined();
+ return true;
+}
+
+
+static bool
+print(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ couch_print(cx, argc, args);
+ args.rval().setUndefined();
+ return true;
+}
+
+
+static bool
+quit(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ int exit_code = args[0].toInt32();;
+ exit(exit_code);
+}
+
+
+static bool
+readline(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JSString* line;
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ /* GC Occasionally */
+ JS_MaybeGC(cx);
+
+ line = couch_readline(cx, stdin);
+ if(line == NULL) return false;
+
+ // return with JSString* instead of JSValue in the past
+ args.rval().setString(line);
+ return true;
+}
+
+
+static bool
+seal(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ JS::RootedObject target(cx);
+ target = JS::ToObject(cx, args[0]);
+ if (!target)
+ args.rval().setUndefined();
+ return true;
+ bool deep = false;
+ deep = args[1].toBoolean();
+ bool ret = deep ? JS_DeepFreezeObject(cx, target) : JS_FreezeObject(cx, target);
+ args.rval().setUndefined();
+ return ret;
+}
+
+
+static bool
+js_sleep(JSContext* cx, unsigned int argc, JS::Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+
+ int duration = args[0].toInt32();
+
+#ifdef XP_WIN
+ Sleep(duration);
+#else
+ usleep(duration * 1000);
+#endif
+
+ return true;
+}
+
+JSPropertySpec CouchHTTPProperties[] = {
+ JS_PSG("status", req_status, 0),
+ JS_PSG("base_url", base_url, 0),
+ JS_PS_END
+};
+
+
+JSFunctionSpec CouchHTTPFunctions[] = {
+ JS_FN("_open", req_open, 3, 0),
+ JS_FN("_setRequestHeader", req_set_hdr, 2, 0),
+ JS_FN("_send", req_send, 1, 0),
+ JS_FS_END
+};
+
+
+JSFunctionSpec TestSuiteFunctions[] = {
+ JS_FN("sleep", js_sleep, 1, 0),
+ JS_FS_END
+};
+
+
+static JSFunctionSpec global_functions[] = {
+ JS_FN("evalcx", evalcx, 0, 0),
+ JS_FN("gc", gc, 0, 0),
+ JS_FN("print", print, 0, 0),
+ JS_FN("quit", quit, 0, 0),
+ JS_FN("readline", readline, 0, 0),
+ JS_FN("seal", seal, 0, 0),
+ JS_FS_END
+};
+
+
+static bool
+csp_allows(JSContext* cx)
+{
+ couch_args *args = (couch_args*)JS_GetContextPrivate(cx);
+ if(args->eval) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+static JSSecurityCallbacks security_callbacks = {
+ csp_allows,
+ nullptr
+};
+
+
+int
+main(int argc, const char* argv[])
+{
+ JSContext* cx = NULL;
+ JSObject* klass = NULL;
+ char* scriptsrc;
+ size_t slen;
+ int i;
+
+ couch_args* args = couch_parse_args(argc, argv);
+
+ JS_Init();
+ cx = JS_NewContext(args->stack_size, 8L * 1024L);
+ if(cx == NULL)
+ return 1;
+
+ if (!JS::InitSelfHostedCode(cx))
+ return 1;
+
+ JS::SetWarningReporter(cx, couch_error);
+ JS_SetContextPrivate(cx, args);
+ JS_SetSecurityCallbacks(cx, &security_callbacks);
+
+ JSAutoRequest ar(cx);
+ JS::CompartmentOptions options;
+ JS::RootedObject global(cx, JS_NewGlobalObject(cx, &global_class, nullptr,
+ JS::FireOnNewGlobalHook, options));
+ if (!global)
+ return 1;
+
+ JSAutoCompartment ac(cx, global);
+
+ if(!JS_InitStandardClasses(cx, global))
+ return 1;
+
+ if(couch_load_funcs(cx, global, global_functions) != true)
+ return 1;
+
+ if(args->use_http) {
+ http_check_enabled();
+
+ klass = JS_InitClass(
+ cx, global,
+ NULL,
+ &CouchHTTPClass, req_ctor,
+ 0,
+ CouchHTTPProperties, CouchHTTPFunctions,
+ NULL, NULL
+ );
+
+ if(!klass)
+ {
+ fprintf(stderr, "Failed to initialize CouchHTTP class.\n");
+ exit(2);
+ }
+ }
+
+ if(args->use_test_funs) {
+ if(couch_load_funcs(cx, global, TestSuiteFunctions) != true)
+ return 1;
+ }
+
+ for(i = 0 ; args->scripts[i] ; i++) {
+ slen = couch_readfile(args->scripts[i], &scriptsrc);
+
+ // Compile and run
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(__FILE__, __LINE__);
+ JS::RootedScript script(cx);
+
+ if(!JS_CompileScript(cx, scriptsrc, slen, options, &script)) {
+ fprintf(stderr, "Failed to compile script.\n");
+ return 1;
+ }
+
+ free(scriptsrc);
+
+ JS::RootedValue result(cx);
+ if(JS_ExecuteScript(cx, script, &result) != true) {
+ fprintf(stderr, "Failed to execute script.\n");
+ return 1;
+ }
+
+ // Give the GC a chance to run.
+ JS_MaybeGC(cx);
+ }
+
+ JS_DestroyContext(cx);
+ JS_ShutDown();
+
+ return 0;
+}
diff --git a/src/couch/priv/couch_js/60/utf8.cpp b/src/couch/priv/couch_js/60/utf8.cpp
new file mode 100644
index 0000000..38dfa62
--- /dev/null
+++ b/src/couch/priv/couch_js/60/utf8.cpp
@@ -0,0 +1,301 @@
+// 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 <jsapi.h>
+#include <js/Initialization.h>
+#include <js/Conversions.h>
+#include <js/Wrapper.h>
+#include "config.h"
+#include "util.h"
+
+static int
+enc_char(uint8_t *utf8Buffer, uint32_t ucs4Char)
+{
+ int utf8Length = 1;
+
+ if (ucs4Char < 0x80)
+ {
+ *utf8Buffer = (uint8_t)ucs4Char;
+ }
+ else
+ {
+ int i;
+ uint32_t a = ucs4Char >> 11;
+ utf8Length = 2;
+ while(a)
+ {
+ a >>= 5;
+ utf8Length++;
+ }
+ i = utf8Length;
+ while(--i)
+ {
+ utf8Buffer[i] = (uint8_t)((ucs4Char & 0x3F) | 0x80);
+ ucs4Char >>= 6;
+ }
+ *utf8Buffer = (uint8_t)(0x100 - (1 << (8-utf8Length)) + ucs4Char);
+ }
+
+ return utf8Length;
+}
+
+static bool
+enc_charbuf(const char16_t* src, size_t srclen, char* dst, size_t* dstlenp)
+{
+ size_t i;
+ size_t utf8Len;
+ size_t dstlen = *dstlenp;
+ size_t origDstlen = dstlen;
+ char16_t c;
+ char16_t c2;
+ uint32_t v;
+ uint8_t utf8buf[6];
+
+ if(!dst)
+ {
+ dstlen = origDstlen = (size_t) -1;
+ }
+
+ while(srclen)
+ {
+ c = *src++;
+ srclen--;
+
+ if(c <= 0xD7FF || c >= 0xE000)
+ {
+ v = (uint32_t) c;
+ }
+ else if(c >= 0xD800 && c <= 0xDBFF)
+ {
+ if(srclen < 1) goto buffer_too_small;
+ c2 = *src++;
+ srclen--;
+ if(c2 >= 0xDC00 && c2 <= 0xDFFF)
+ {
+ v = (uint32_t) (((c - 0xD800) << 10) + (c2 - 0xDC00) + 0x10000);
+ }
+ else
+ {
+ // Invalid second half of surrogate pair
+ v = (uint32_t) 0xFFFD;
+ // Undo our character advancement
+ src--;
+ srclen++;
+ }
+ }
+ else
+ {
+ // Invalid first half surrogate pair
+ v = (uint32_t) 0xFFFD;
+ }
+
+ if(v < 0x0080)
+ {
+ /* no encoding necessary - performance hack */
+ if(!dstlen) goto buffer_too_small;
+ if(dst) *dst++ = (char) v;
+ utf8Len = 1;
+ }
+ else
+ {
+ utf8Len = enc_char(utf8buf, v);
+ if(utf8Len > dstlen) goto buffer_too_small;
+ if(dst)
+ {
+ for (i = 0; i < utf8Len; i++)
+ {
+ *dst++ = (char) utf8buf[i];
+ }
+ }
+ }
+ dstlen -= utf8Len;
+ }
+
+ *dstlenp = (origDstlen - dstlen);
+ return true;
+
+buffer_too_small:
+ *dstlenp = (origDstlen - dstlen);
+ return false;
+}
+
+char*
+enc_string(JSContext* cx, JS::Value arg, size_t* buflen)
+{
+ JSString* str = NULL;
+ const char16_t* src = NULL;
+ char* bytes = NULL;
+ size_t srclen = 0;
+ size_t byteslen = 0;
+ js::AutoStableStringChars rawChars(cx);
+
+ str = arg.toString();
+ if(!str) goto error;
+
+ if (!rawChars.initTwoByte(cx, str))
+ return NULL;
+
+ src = rawChars.twoByteRange().begin().get();
+ srclen = JS_GetStringLength(str);
+
+ if(!enc_charbuf(src, srclen, NULL, &byteslen)) goto error;
+
+ bytes = (char *)JS_malloc(cx, (byteslen) + 1);
+ bytes[byteslen] = 0;
+
+ if(!enc_charbuf(src, srclen, bytes, &byteslen)) goto error;
+
+ if(buflen) *buflen = byteslen;
+ goto success;
+
+error:
+ if(bytes != NULL) JS_free(cx, bytes);
+ bytes = NULL;
+
+success:
+ return bytes;
+}
+
+static uint32_t
+dec_char(const uint8_t *utf8Buffer, int utf8Length)
+{
+ uint32_t ucs4Char;
+ uint32_t minucs4Char;
+
+ /* from Unicode 3.1, non-shortest form is illegal */
+ static const uint32_t minucs4Table[] = {
+ 0x00000080, 0x00000800, 0x0001000, 0x0020000, 0x0400000
+ };
+
+ if (utf8Length == 1)
+ {
+ ucs4Char = *utf8Buffer;
+ }
+ else
+ {
+ ucs4Char = *utf8Buffer++ & ((1<<(7-utf8Length))-1);
+ minucs4Char = minucs4Table[utf8Length-2];
+ while(--utf8Length)
+ {
+ ucs4Char = ucs4Char<<6 | (*utf8Buffer++ & 0x3F);
+ }
+ if(ucs4Char < minucs4Char || ucs4Char == 0xFFFE || ucs4Char == 0xFFFF)
+ {
+ ucs4Char = 0xFFFD;
+ }
+ }
+
+ return ucs4Char;
+}
+
+static bool
+dec_charbuf(const char *src, size_t srclen, char16_t *dst, size_t *dstlenp)
+{
+ uint32_t v;
+ size_t offset = 0;
+ size_t j;
+ size_t n;
+ size_t dstlen = *dstlenp;
+ size_t origDstlen = dstlen;
+
+ if(!dst) dstlen = origDstlen = (size_t) -1;
+
+ while(srclen)
+ {
+ v = (uint8_t) *src;
+ n = 1;
+
+ if(v & 0x80)
+ {
+ while(v & (0x80 >> n))
+ {
+ n++;
+ }
+
+ if(n > srclen) goto buffer_too_small;
+ if(n == 1 || n > 6) goto bad_character;
+
+ for(j = 1; j < n; j++)
+ {
+ if((src[j] & 0xC0) != 0x80) goto bad_character;
+ }
+
+ v = dec_char((const uint8_t *) src, n);
+ if(v >= 0x10000)
+ {
+ v -= 0x10000;
+
+ if(v > 0xFFFFF || dstlen < 2)
+ {
+ *dstlenp = (origDstlen - dstlen);
+ return false;
+ }
+
+ if(dstlen < 2) goto buffer_too_small;
+
+ if(dst)
+ {
+ *dst++ = (char16_t)((v >> 10) + 0xD800);
+ v = (char16_t)((v & 0x3FF) + 0xDC00);
+ }
+ dstlen--;
+ }
+ }
+
+ if(!dstlen) goto buffer_too_small;
+ if(dst) *dst++ = (char16_t) v;
+
+ dstlen--;
+ offset += n;
+ src += n;
+ srclen -= n;
+ }
+
+ *dstlenp = (origDstlen - dstlen);
+ return true;
+
+bad_character:
+ *dstlenp = (origDstlen - dstlen);
+ return false;
+
+buffer_too_small:
+ *dstlenp = (origDstlen - dstlen);
+ return false;
+}
+
+JSString*
+dec_string(JSContext* cx, const char* bytes, size_t byteslen)
+{
+ JSString* str = NULL;
+ char16_t* chars = NULL;
+ size_t charslen;
+
+ if(!dec_charbuf(bytes, byteslen, NULL, &charslen)) goto error;
+
+ chars = (char16_t *)JS_malloc(cx, (charslen + 1) * sizeof(char16_t));
+ if(!chars) return NULL;
+ chars[charslen] = 0;
+
+ if(!dec_charbuf(bytes, byteslen, chars, &charslen)) goto error;
+
+ str = JS_NewUCString(cx, chars, charslen - 1);
+ if(!str) goto error;
+
+ goto success;
+
+error:
+ if(chars != NULL) JS_free(cx, chars);
+ str = NULL;
+
+success:
+ return str;
+}
diff --git a/src/couch/priv/couch_js/60/utf8.h b/src/couch/priv/couch_js/60/utf8.h
new file mode 100644
index 0000000..c8b1f4d
--- /dev/null
+++ b/src/couch/priv/couch_js/60/utf8.h
@@ -0,0 +1,19 @@
+// 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 COUCH_JS_UTF_8_H
+#define COUCH_JS_UTF_8_H
+
+char* enc_string(JSContext* cx, JS::Value arg, size_t* buflen);
+JSString* dec_string(JSContext* cx, const char* buf, size_t buflen);
+
+#endif
diff --git a/src/couch/priv/couch_js/60/util.cpp b/src/couch/priv/couch_js/60/util.cpp
new file mode 100644
index 0000000..894b425
--- /dev/null
+++ b/src/couch/priv/couch_js/60/util.cpp
@@ -0,0 +1,323 @@
+// 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 <stdlib.h>
+#include <string.h>
+
+#include <jsapi.h>
+#include <js/Initialization.h>
+#include <js/Conversions.h>
+
+#include "help.h"
+#include "util.h"
+#include "utf8.h"
+
+std::string
+js_to_string(JSContext* cx, JS::HandleValue val)
+{
+ JS::RootedString sval(cx);
+ sval = val.toString();
+
+ JS::UniqueChars chars(JS_EncodeStringToUTF8(cx, sval));
+ if(!chars) {
+ JS_ClearPendingException(cx);
+ fprintf(stderr, "Error converting value to string.\n");
+ exit(3);
+ }
+
+ return chars.get();
+}
+
+std::string
+js_to_string(JSContext* cx, JSString *str)
+{
+ JS::UniqueChars chars(JS_EncodeString(cx, str));
+ if(!chars) {
+ JS_ClearPendingException(cx);
+ fprintf(stderr, "Error converting to string.\n");
+ exit(3);
+ }
+
+ return chars.get();
+}
+
+JSString*
+string_to_js(JSContext* cx, const std::string& s)
+{
+ JSString* ret = JS_NewStringCopyN(cx, s.c_str(), s.size());
+ if(ret != nullptr) {
+ return ret;
+ }
+
+ fprintf(stderr, "Unable to allocate string object.\n");
+ exit(3);
+}
+
+size_t
+couch_readfile(const char* file, char** outbuf_p)
+{
+ FILE* fp;
+ char fbuf[16384];
+ char *buf = NULL;
+ char* tmp;
+ size_t nread = 0;
+ size_t buflen = 0;
+
+ if(strcmp(file, "-") == 0) {
+ fp = stdin;
+ } else {
+ fp = fopen(file, "r");
+ if(fp == NULL) {
+ fprintf(stderr, "Failed to read file: %s\n", file);
+ exit(3);
+ }
+ }
+
+ while((nread = fread(fbuf, 1, 16384, fp)) > 0) {
+ if(buf == NULL) {
+ buf = (char*) malloc(nread + 1);
+ if(buf == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(3);
+ }
+ memcpy(buf, fbuf, nread);
+ } else {
+ tmp = (char*) malloc(buflen + nread + 1);
+ if(tmp == NULL) {
+ fprintf(stderr, "Out of memory.\n");
+ exit(3);
+ }
+ memcpy(tmp, buf, buflen);
+ memcpy(tmp+buflen, fbuf, nread);
+ free(buf);
+ buf = tmp;
+ }
+ buflen += nread;
+ buf[buflen] = '\0';
+ }
+ *outbuf_p = buf;
+ return buflen ;
+}
+
+couch_args*
+couch_parse_args(int argc, const char* argv[])
+{
+ couch_args* args;
+ int i = 1;
+
+ args = (couch_args*) malloc(sizeof(couch_args));
+ if(args == NULL)
+ return NULL;
+
+ memset(args, '\0', sizeof(couch_args));
+ args->stack_size = 64L * 1024L * 1024L;
+
+ while(i < argc) {
+ if(strcmp("-h", argv[i]) == 0) {
+ DISPLAY_USAGE;
+ exit(0);
+ } else if(strcmp("-V", argv[i]) == 0) {
+ DISPLAY_VERSION;
+ exit(0);
+ } else if(strcmp("-H", argv[i]) == 0) {
+ args->use_http = 1;
+ } else if(strcmp("-T", argv[i]) == 0) {
+ args->use_test_funs = 1;
+ } else if(strcmp("-S", argv[i]) == 0) {
+ args->stack_size = atoi(argv[++i]);
+ if(args->stack_size <= 0) {
+ fprintf(stderr, "Invalid stack size.\n");
+ exit(2);
+ }
+ } else if(strcmp("-u", argv[i]) == 0) {
+ args->uri_file = argv[++i];
+ } else if(strcmp("--eval", argv[i]) == 0) {
+ args->eval = 1;
+ } else if(strcmp("--", argv[i]) == 0) {
+ i++;
+ break;
+ } else {
+ break;
+ }
+ i++;
+ }
+
+ if(i >= argc) {
+ DISPLAY_USAGE;
+ exit(3);
+ }
+ args->scripts = argv + i;
+
+ return args;
+}
+
+
+int
+couch_fgets(char* buf, int size, FILE* fp)
+{
+ int n, i, c;
+
+ if(size <= 0) return -1;
+ n = size - 1;
+
+ for(i = 0; i < n && (c = getc(fp)) != EOF; i++) {
+ buf[i] = c;
+ if(c == '\n') {
+ i++;
+ break;
+ }
+ }
+
+ buf[i] = '\0';
+ return i;
+}
+
+
+JSString*
+couch_readline(JSContext* cx, FILE* fp)
+{
+ JSString* str;
+ char* bytes = NULL;
+ char* tmp = NULL;
+ size_t used = 0;
+ size_t byteslen = 256;
+ size_t oldbyteslen = 256;
+ size_t readlen = 0;
+
+ bytes = (char *)JS_malloc(cx, byteslen);
+ if(bytes == NULL) return NULL;
+
+ while((readlen = couch_fgets(bytes+used, byteslen-used, fp)) > 0) {
+ used += readlen;
+
+ if(bytes[used-1] == '\n') {
+ bytes[used-1] = '\0';
+ break;
+ }
+
+ // Double our buffer and read more.
+ oldbyteslen = byteslen;
+ byteslen *= 2;
+ tmp = (char *)JS_realloc(cx, bytes, oldbyteslen, byteslen);
+ if(!tmp) {
+ JS_free(cx, bytes);
+ return NULL;
+ }
+
+ bytes = tmp;
+ }
+
+ // Treat empty strings specially
+ if(used == 0) {
+ JS_free(cx, bytes);
+ return JS_NewStringCopyZ(cx, nullptr);
+ }
+
+ // Shring the buffer to the actual data size
+ tmp = (char *)JS_realloc(cx, bytes, byteslen, used);
+ if(!tmp) {
+ JS_free(cx, bytes);
+ return NULL;
+ }
+ bytes = tmp;
+ byteslen = used;
+
+ str = string_to_js(cx, std::string(tmp));
+ JS_free(cx, bytes);
+ return str;
+}
+
+
+void
+couch_print(JSContext* cx, unsigned int argc, JS::CallArgs argv)
+{
+ uint8_t* bytes = nullptr;
+ FILE *stream = stdout;
+
+ if (argc) {
+ if (argc > 1 && argv[1].isTrue()) {
+ stream = stderr;
+ }
+ JSString* str = JS::ToString(cx, argv.get(0));
+ bytes = reinterpret_cast<uint8_t*>(JS_EncodeString(cx, str));
+ fprintf(stream, "%s", bytes);
+ JS_free(cx, bytes);
+ }
+
+ fputc('\n', stream);
+ fflush(stream);
+}
+
+
+void
+couch_error(JSContext* cx, JSErrorReport* report)
+{
+ JS::RootedValue v(cx), stack(cx), replace(cx);
+ char* bytes;
+ JSObject* regexp;
+
+ if(!report || !JSREPORT_IS_WARNING(report->flags))
+ {
+ fprintf(stderr, "%s\n", report->message().c_str());
+
+ // Print a stack trace, if available.
+ if (JSREPORT_IS_EXCEPTION(report->flags) &&
+ JS_GetPendingException(cx, &v))
+ {
+ // Clear the exception before an JS method calls or the result is
+ // infinite, recursive error report generation.
+ JS_ClearPendingException(cx);
+
+ // Use JS regexp to indent the stack trace.
+ // If the regexp can't be created, don't JS_ReportErrorUTF8 since it is
+ // probably not productive to wind up here again.
+ JS::RootedObject vobj(cx, v.toObjectOrNull());
+
+ if(JS_GetProperty(cx, vobj, "stack", &stack) &&
+ (regexp = JS_NewRegExpObject(
+ cx, "^(?=.)", 6, JSREG_GLOB | JSREG_MULTILINE)))
+ {
+ // Set up the arguments to ``String.replace()``
+ JS::AutoValueVector re_args(cx);
+ JS::RootedValue arg0(cx, JS::ObjectValue(*regexp));
+ auto arg1 = JS::StringValue(string_to_js(cx, "\t"));
+
+ if (re_args.append(arg0) && re_args.append(arg1)) {
+ // Perform the replacement
+ JS::RootedObject sobj(cx, stack.toObjectOrNull());
+ if(JS_GetProperty(cx, sobj, "replace", &replace) &&
+ JS_CallFunctionValue(cx, sobj, replace, re_args, &v))
+ {
+ // Print the result
+ bytes = enc_string(cx, v, NULL);
+ fprintf(stderr, "Stacktrace:\n%s", bytes);
+ JS_free(cx, bytes);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+bool
+couch_load_funcs(JSContext* cx, JS::HandleObject obj, JSFunctionSpec* funcs)
+{
+ JSFunctionSpec* f;
+ for(f = funcs; f->name != NULL; f++) {
+ if(!JS_DefineFunction(cx, obj, f->name, f->call.op, f->nargs, f->flags)) {
+ fprintf(stderr, "Failed to create function: %s\n", f->name);
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/src/couch/priv/couch_js/60/util.h b/src/couch/priv/couch_js/60/util.h
new file mode 100644
index 0000000..45caa34
--- /dev/null
+++ b/src/couch/priv/couch_js/60/util.h
@@ -0,0 +1,41 @@
+// 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 COUCHJS_UTIL_H
+#define COUCHJS_UTIL_H
+
+#include <jsapi.h>
+
+typedef struct {
+ int eval;
+ int use_http;
+ int use_test_funs;
+ int stack_size;
+ const char** scripts;
+ const char* uri_file;
+ JSString* uri;
+} couch_args;
+
+std::string js_to_string(JSContext* cx, JS::HandleValue val);
+std::string js_to_string(JSContext* cx, JSString *str);
+JSString* string_to_js(JSContext* cx, const std::string& s);
+
+couch_args* couch_parse_args(int argc, const char* argv[]);
+int couch_fgets(char* buf, int size, FILE* fp);
+JSString* couch_readline(JSContext* cx, FILE* fp);
+size_t couch_readfile(const char* file, char** outbuf_p);
+void couch_print(JSContext* cx, unsigned int argc, JS::CallArgs argv);
+void couch_error(JSContext* cx, JSErrorReport* report);
+bool couch_load_funcs(JSContext* cx, JS::HandleObject obj, JSFunctionSpec* funcs);
+
+
+#endif // Included util.h